tomcat.getHost().setDeployOnStartup(true);
new TomcatAccessLog().configure(tomcat, props);
TomcatConnectors.configure(tomcat, props);
- webappContext = Webapp.configure(tomcat, props);
+ webappContext = new TomcatContexts().configure(tomcat, props);
try {
tomcat.start();
new TomcatStartupLogs(props, Loggers.get(getClass())).log(tomcat);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.app;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import javax.servlet.ServletException;
+import org.apache.catalina.Context;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.isEmpty;
+
+/**
+ * Configures Tomcat contexts:
+ * <ul>
+ * <li>/deploy delivers the plugins required by analyzers. It maps directory ${sonar.path.data}/web/deploy.</li>
+ * <li>/ is the regular webapp</li>
+ * </ul>
+ */
+public class TomcatContexts {
+
+ private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes";
+ private static final String RAILS_ENV = "rails.env";
+ private static final String ROOT_CONTEXT_PATH = "";
+ public static final String WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR = "web/deploy";
+
+ private final Fs fs;
+
+ public TomcatContexts() {
+ this.fs = new Fs();
+ }
+
+ @VisibleForTesting
+ TomcatContexts(Fs fs) {
+ this.fs = fs;
+ }
+
+ public StandardContext configure(Tomcat tomcat, Props props) {
+ addStaticDir(tomcat, "/deploy", new File(props.nonNullValueAsFile(ProcessProperties.PATH_DATA), WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR));
+
+ StandardContext webapp = addContext(tomcat, ROOT_CONTEXT_PATH, webappDir(props));
+ configureRails(props, webapp);
+ for (Map.Entry<Object, Object> entry : props.rawProperties().entrySet()) {
+ String key = entry.getKey().toString();
+ webapp.addParameter(key, entry.getValue().toString());
+ }
+ return webapp;
+ }
+
+ @VisibleForTesting
+ StandardContext addStaticDir(Tomcat tomcat, String contextPath, File dir) {
+ try {
+ fs.createOrCleanupDir(dir);
+ } catch (IOException e) {
+ throw new IllegalStateException(format("Fail to create or clean-up directory %s", dir.getAbsolutePath()), e);
+ }
+
+ return addContext(tomcat, contextPath, dir);
+ }
+
+ private StandardContext addContext(Tomcat tomcat, String contextPath, File dir) {
+ try {
+ StandardContext context = (StandardContext) tomcat.addWebapp(contextPath, dir.getAbsolutePath());
+ context.setClearReferencesHttpClientKeepAliveThread(false);
+ context.setClearReferencesStatic(false);
+ context.setClearReferencesStopThreads(false);
+ context.setClearReferencesStopTimerThreads(false);
+ context.setClearReferencesStopTimerThreads(false);
+ context.setAntiResourceLocking(false);
+ context.setReloadable(false);
+ context.setUseHttpOnly(true);
+ context.setTldValidation(false);
+ context.setXmlValidation(false);
+ context.setXmlNamespaceAware(false);
+ context.setUseNaming(false);
+ context.setDelegate(true);
+ context.setJarScanner(new NullJarScanner());
+ return context;
+ } catch (ServletException e) {
+ throw new IllegalStateException("Fail to configure webapp from " + dir, e);
+ }
+ }
+
+ static void configureRails(Props props, Context context) {
+ // sonar.dev is kept for backward-compatibility
+ if (props.valueAsBoolean("sonar.dev", false)) {
+ props.set("sonar.web.dev", "true");
+ }
+ if (props.valueAsBoolean("sonar.web.dev", false)) {
+ context.addParameter(RAILS_ENV, "development");
+ context.addParameter(JRUBY_MAX_RUNTIMES, "3");
+ Loggers.get(TomcatContexts.class).warn("WEB DEVELOPMENT MODE IS ENABLED - DO NOT USE FOR PRODUCTION USAGE");
+ } else {
+ context.addParameter(RAILS_ENV, "production");
+ context.addParameter(JRUBY_MAX_RUNTIMES, "1");
+ }
+ }
+
+ static File webappDir(Props props) {
+ String devDir = props.value("sonar.web.dev.sources");
+ File dir;
+ if (isEmpty(devDir)) {
+ dir = new File(props.value(ProcessProperties.PATH_HOME), "web");
+ } else {
+ dir = new File(devDir);
+ }
+ Loggers.get(TomcatContexts.class).info("Webapp directory: {}", dir);
+ return dir;
+ }
+
+ static class Fs {
+ void createOrCleanupDir(File dir) throws IOException {
+ FileUtils.forceMkdir(dir);
+ FileUtils.cleanDirectory(dir);
+ }
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.app;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.io.File;
-import java.io.IOException;
-import javax.servlet.ServletException;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.commons.io.FileUtils;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static java.lang.String.format;
-
-public class WebDeployContext {
-
- public static final String RELATIVE_DIR_IN_DATA = "web/deploy";
- private final Fs fs;
-
- public WebDeployContext() {
- this(new Fs());
- }
-
- @VisibleForTesting
- public WebDeployContext(Fs fs) {
- this.fs = fs;
- }
-
- public void configureTomcat(Tomcat tomcat, Props props) throws ServletException {
- File deployDir = new File(props.nonNullValueAsFile(ProcessProperties.PATH_DATA), RELATIVE_DIR_IN_DATA);
- try {
- fs.createOrCleanupDir(deployDir);
- } catch (IOException e) {
- throw new IllegalStateException(format("Fail to create or clean-up directory %s", deployDir.getAbsolutePath()), e);
- }
- tomcat.addWebapp("/deploy", deployDir.getAbsolutePath());
- }
-
- static class Fs {
- void createOrCleanupDir(File dir) throws IOException {
- FileUtils.forceMkdir(dir);
- FileUtils.cleanDirectory(dir);
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.app;
-
-import java.io.File;
-import java.util.Map;
-import org.apache.catalina.Context;
-import org.apache.catalina.core.StandardContext;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-/**
- * Configures webapp into Tomcat
- */
-class Webapp {
-
- private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes";
- private static final String RAILS_ENV = "rails.env";
- private static final String ROOT_CONTEXT_PATH = "";
-
- private Webapp() {
- }
-
- static StandardContext configure(Tomcat tomcat, Props props) {
- try {
- // URL /deploy must serve files deployed during startup into DATA_DIR/web/deploy
- new WebDeployContext().configureTomcat(tomcat, props);
-
- StandardContext context = (StandardContext) tomcat.addWebapp(ROOT_CONTEXT_PATH, webappPath(props));
- context.setClearReferencesHttpClientKeepAliveThread(false);
- context.setClearReferencesStatic(false);
- context.setClearReferencesStopThreads(false);
- context.setClearReferencesStopTimerThreads(false);
- context.setClearReferencesStopTimerThreads(false);
- context.setAntiResourceLocking(false);
- context.setReloadable(false);
- context.setUseHttpOnly(true);
- context.setTldValidation(false);
- context.setXmlValidation(false);
- context.setXmlNamespaceAware(false);
- context.setUseNaming(false);
- context.setDelegate(true);
- context.setJarScanner(new NullJarScanner());
- configureRails(props, context);
-
- for (Map.Entry<Object, Object> entry : props.rawProperties().entrySet()) {
- String key = entry.getKey().toString();
- context.addParameter(key, entry.getValue().toString());
- }
- return context;
-
- } catch (Exception e) {
- throw new IllegalStateException("Fail to configure webapp", e);
- }
- }
-
- static void configureRails(Props props, Context context) {
- // sonar.dev is kept for backward-compatibility
- if (props.valueAsBoolean("sonar.dev", false)) {
- props.set("sonar.web.dev", "true");
- }
- if (props.valueAsBoolean("sonar.web.dev", false)) {
- context.addParameter(RAILS_ENV, "development");
- context.addParameter(JRUBY_MAX_RUNTIMES, "3");
- Loggers.get(Webapp.class).warn("WEB DEVELOPMENT MODE IS ENABLED - DO NOT USE FOR PRODUCTION USAGE");
- } else {
- context.addParameter(RAILS_ENV, "production");
- context.addParameter(JRUBY_MAX_RUNTIMES, "1");
- }
- }
-
- static String webappPath(Props props) {
- String webDir = props.value("sonar.web.dev.sources");
- if (StringUtils.isEmpty(webDir)) {
- webDir = new File(props.value(ProcessProperties.PATH_HOME), "web").getAbsolutePath();
- }
- Loggers.get(Webapp.class).info(String.format("Webapp directory: %s", webDir));
- return webDir;
- }
-}
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.process.ProcessProperties;
-import org.sonar.server.app.WebDeployContext;
+import org.sonar.server.app.TomcatContexts;
import static org.sonar.api.CoreProperties.SERVER_BASE_URL;
import static org.sonar.api.CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE;
throw new IllegalStateException("SonarQube home directory is not valid");
}
- deployDir = new File(settings.getString(ProcessProperties.PATH_DATA), WebDeployContext.RELATIVE_DIR_IN_DATA);
+ deployDir = new File(settings.getString(ProcessProperties.PATH_DATA), TomcatContexts.WEB_DEPLOY_PATH_RELATIVE_TO_DATA_DIR);
LOG.info("SonarQube {}", Joiner.on(" / ").skipNulls().join("Server", version, implementationBuild));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.app;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.catalina.Context;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TomcatContextsTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ Tomcat tomcat = mock(Tomcat.class);
+ Properties props = new Properties();
+
+ @Before
+ public void setUp() throws Exception {
+ props.setProperty(ProcessProperties.PATH_DATA, temp.newFolder("data").getAbsolutePath());
+ when(tomcat.addWebapp(anyString(), anyString())).thenReturn(mock(StandardContext.class));
+ }
+
+ @Test
+ public void configure_root_webapp() throws Exception {
+ props.setProperty("foo", "bar");
+ StandardContext context = mock(StandardContext.class);
+ when(tomcat.addWebapp(anyString(), anyString())).thenReturn(context);
+
+ new TomcatContexts().configure(tomcat, new Props(props));
+
+ // configure webapp with properties
+ verify(context).addParameter("foo", "bar");
+ }
+
+ @Test
+ public void configure_rails_dev_mode() {
+ props.setProperty("sonar.web.dev", "true");
+ Context context = mock(Context.class);
+
+ new TomcatContexts().configureRails(new Props(props), context);
+
+ verify(context).addParameter("jruby.max.runtimes", "3");
+ verify(context).addParameter("rails.env", "development");
+ }
+
+ @Test
+ public void configure_rails_production_mode() {
+ props.setProperty("sonar.web.dev", "false");
+ Context context = mock(Context.class);
+
+ new TomcatContexts().configureRails(new Props(props), context);
+
+ verify(context).addParameter("jruby.max.runtimes", "1");
+ verify(context).addParameter("rails.env", "production");
+ }
+
+ @Test
+ public void create_dir_and_configure_static_directory() throws Exception {
+ File dir = temp.newFolder();
+ dir.delete();
+
+ new TomcatContexts().addStaticDir(tomcat, "/deploy", dir);
+
+ assertThat(dir).isDirectory().exists();
+ verify(tomcat).addWebapp("/deploy", dir.getAbsolutePath());
+ }
+
+ @Test
+ public void cleanup_static_directory_if_already_exists() throws Exception {
+ File dir = temp.newFolder();
+ FileUtils.touch(new File(dir, "foo.txt"));
+
+ new TomcatContexts().addStaticDir(tomcat, "/deploy", dir);
+
+ assertThat(dir).isDirectory().exists();
+ assertThat(dir.listFiles()).isEmpty();
+ }
+
+ @Test
+ public void fail_if_static_directory_can_not_be_initialized() throws Exception {
+ File dir = temp.newFolder();
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fail to create or clean-up directory " + dir.getAbsolutePath());
+
+ TomcatContexts.Fs fs = mock(TomcatContexts.Fs.class);
+ doThrow(new IOException()).when(fs).createOrCleanupDir(any(File.class));
+
+ new TomcatContexts(fs).addStaticDir(tomcat, "/deploy", dir);
+
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.app;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.commons.io.FileUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class WebDeployContextTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- Tomcat tomcat = mock(Tomcat.class);
- Properties props = new Properties();
-
- @Test
- public void create_dir_and_configure_tomcat_context() throws Exception {
- File dataDir = temp.newFolder();
- props.setProperty(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
- new WebDeployContext().configureTomcat(tomcat, new Props(props));
-
- File deployDir = new File(dataDir, "web/deploy");
- assertThat(deployDir).isDirectory().exists();
- verify(tomcat).addWebapp("/deploy", deployDir.getAbsolutePath());
- }
-
- @Test
- public void cleanup_directory_if_already_exists() throws Exception {
- File dataDir = temp.newFolder();
- File deployDir = new File(dataDir, "web/deploy");
- FileUtils.touch(new File(deployDir, "foo.txt"));
- props.setProperty(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
- new WebDeployContext().configureTomcat(tomcat, new Props(props));
-
- assertThat(deployDir).isDirectory().exists();
- assertThat(deployDir.listFiles()).isEmpty();
- }
-
- @Test
- public void fail_if_directory_can_not_be_initialized() throws Exception {
- File dataDir = temp.newFolder();
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Fail to create or clean-up directory " + dataDir.getAbsolutePath());
-
- props.setProperty(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
- WebDeployContext.Fs fs = mock(WebDeployContext.Fs.class);
- doThrow(new IOException()).when(fs).createOrCleanupDir(any(File.class));
-
- new WebDeployContext(fs).configureTomcat(tomcat, new Props(props));
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.app;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-import org.apache.catalina.Context;
-import org.apache.catalina.core.StandardContext;
-import org.apache.catalina.startup.Tomcat;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class WebappTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- Props props = new Props(new Properties());
-
- @Before
- public void initDataDir() throws Exception {
- props.set(ProcessProperties.PATH_DATA, temp.newFolder("data").getAbsolutePath());
- }
-
- @Test
- public void fail_on_error() throws Exception {
- File webDir = temp.newFolder("web");
-
- Tomcat tomcat = mock(Tomcat.class, RETURNS_DEEP_STUBS);
- when(tomcat.addContext("", webDir.getAbsolutePath())).thenThrow(new NullPointerException());
-
- try {
- Webapp.configure(tomcat, props);
- fail();
- } catch (IllegalStateException e) {
- assertThat(e).hasMessage("Fail to configure webapp");
- }
- }
-
- @Test
- public void configure_context() throws Exception {
- props.set("foo", "bar");
- StandardContext context = mock(StandardContext.class);
- Tomcat tomcat = mock(Tomcat.class);
- when(tomcat.addWebapp(anyString(), anyString())).thenReturn(context);
-
- Webapp.configure(tomcat, props);
-
- // configure webapp with properties
- verify(context).addParameter("foo", "bar");
- }
-
- @Test
- public void configure_rails_dev_mode() {
- props.set("sonar.web.dev", "true");
- Context context = mock(Context.class);
-
- Webapp.configureRails(props, context);
-
- verify(context).addParameter("jruby.max.runtimes", "3");
- verify(context).addParameter("rails.env", "development");
- }
-
- @Test
- public void configure_production_mode() {
- props.set("sonar.web.dev", "false");
- Context context = mock(Context.class);
-
- Webapp.configureRails(props, context);
-
- verify(context).addParameter("jruby.max.runtimes", "1");
- verify(context).addParameter("rails.env", "production");
- }
-}