summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh2
-rw-r--r--pom.xml18
-rwxr-xr-xquick-build.sh2
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Process.java3
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java7
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Props.java10
-rw-r--r--server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java4
-rw-r--r--sonar-application/assembly.xml2
-rw-r--r--sonar-application/pom.xml27
-rw-r--r--sonar-application/src/main/assembly/conf/sonar.properties11
-rw-r--r--sonar-application/src/main/java/org/sonar/application/DefaultSettings.java82
-rw-r--r--sonar-application/src/main/java/org/sonar/application/Installation.java30
-rw-r--r--sonar-application/src/main/java/org/sonar/application/StartServer.java15
13 files changed, 155 insertions, 58 deletions
diff --git a/build.sh b/build.sh
index 5527fa3055e..b5fadb4c34a 100755
--- a/build.sh
+++ b/build.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-mvn clean install $*
+mvn clean install -Pdev $*
diff --git a/pom.xml b/pom.xml
index 20de056d4d3..0bca68ac20f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.sonar</groupId>
@@ -1256,6 +1257,13 @@
<profiles>
<profile>
+ <id>dev</id>
+ <properties>
+ <skipSanityChecks>true</skipSanityChecks>
+ <enforcer.skip>true</enforcer.skip>
+ </properties>
+ </profile>
+ <profile>
<id>release</id>
<build>
<plugins>
@@ -1358,7 +1366,7 @@
</goals>
</pluginExecutionFilter>
<action>
- <ignore />
+ <ignore/>
</action>
</pluginExecution>
<pluginExecution>
@@ -1371,7 +1379,7 @@
</goals>
</pluginExecutionFilter>
<action>
- <ignore />
+ <ignore/>
</action>
</pluginExecution>
<pluginExecution>
@@ -1385,7 +1393,7 @@
</goals>
</pluginExecutionFilter>
<action>
- <ignore />
+ <ignore/>
</action>
</pluginExecution>
<pluginExecution>
@@ -1398,7 +1406,7 @@
</goals>
</pluginExecutionFilter>
<action>
- <ignore />
+ <ignore/>
</action>
</pluginExecution>
</pluginExecutions>
diff --git a/quick-build.sh b/quick-build.sh
index fcb2deea9e0..c27c8c8be13 100755
--- a/quick-build.sh
+++ b/quick-build.sh
@@ -38,4 +38,4 @@ echo ''
echo ''
echo ''
-mvn clean install -DskipTests $*
+mvn clean install -DskipTests -Pdev $*
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Process.java b/server/sonar-process/src/main/java/org/sonar/process/Process.java
index dfcaca18022..848d2da3cd9 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Process.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Process.java
@@ -54,7 +54,6 @@ public abstract class Process implements ProcessMXBean {
private Integer port;
protected final Props props;
- private Thread shutdownHook;
private static final long MAX_ALLOWED_TIME = 15000L;
private ScheduledFuture<?> pingTask = null;
@@ -120,7 +119,7 @@ public abstract class Process implements ProcessMXBean {
throw new IllegalStateException("Process is not a compliant MBean", e);
}
- shutdownHook = new Thread(new Runnable() {
+ Thread shutdownHook = new Thread(new Runnable() {
@Override
public void run() {
terminate();
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
index d752c91faa4..43bfbddf924 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
@@ -19,6 +19,7 @@
*/
package org.sonar.process;
+import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
@@ -131,22 +132,19 @@ public class ProcessWrapper extends Thread {
try {
LOGGER.debug("ProcessWrapper::executeProcess() -- Starting process with command '{}'", StringUtils.join(command, " "));
process = processBuilder.start();
- LOGGER.debug("ProcessWrapper::executeProcess() -- Process started: {}", process.toString());
errorGobbler = new StreamGobbler(process.getErrorStream(), this.getName() + "-ERROR");
outputGobbler = new StreamGobbler(process.getInputStream(), this.getName());
outputGobbler.start();
errorGobbler.start();
processMXBean = waitForJMX();
return this;
-
} catch (IOException e) {
- throw new IllegalStateException("Fail to start process: " + StringUtils.join(command, " "), e);
+ throw new IllegalStateException("Fail to start command: " + StringUtils.join(command, " "), e);
}
}
@Override
public void run() {
- LOGGER.trace("ProcessWrapper::run() START");
try {
process.waitFor();
} catch (InterruptedException e) {
@@ -156,6 +154,7 @@ public class ProcessWrapper extends Thread {
waitUntilFinish(outputGobbler);
waitUntilFinish(errorGobbler);
closeStreams(process);
+ FileUtils.deleteQuietly(propertiesFile);
}
//process.destroy(); //Uncertain if this is required or not...
LOGGER.trace("ProcessWrapper::run() END");
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Props.java b/server/sonar-process/src/main/java/org/sonar/process/Props.java
index 21bd8842e3a..f3cca38e05e 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Props.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Props.java
@@ -33,6 +33,10 @@ public class Props {
this.encryption = new Encryption(props.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH));
}
+ public boolean contains(String key) {
+ return props.containsKey(key);
+ }
+
@CheckForNull
public String of(String key) {
String value = props.getProperty(key);
@@ -82,4 +86,10 @@ public class Props {
props.setProperty(key, value);
return this;
}
+
+ public void setDefault(String propKey, String defaultValue) {
+ if (!props.contains(propKey)) {
+ props.setProperty(propKey, defaultValue);
+ }
+ }
}
diff --git a/server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java b/server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java
index 83b11cbc08c..2b71844183c 100644
--- a/server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java
+++ b/server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java
@@ -35,7 +35,7 @@ public class ElasticSearch extends Process {
public static final String ES_DEBUG_PROPERTY = "esDebug";
public static final String ES_PORT_PROPERTY = "sonar.es.port";
- public static final String ES_CLUSTER_PROPERTY = "sonar.es.cluster.name";
+ public static final String ES_CLUSTER_PROPERTY = "sonar.es.clusterName";
private Node node;
@@ -164,7 +164,7 @@ public class ElasticSearch extends Process {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
-
+ // TODO
}
}
}
diff --git a/sonar-application/assembly.xml b/sonar-application/assembly.xml
index 2304815d061..728a152b68c 100644
--- a/sonar-application/assembly.xml
+++ b/sonar-application/assembly.xml
@@ -100,7 +100,7 @@
</dependencySet>
<!-- Server -->
<dependencySet>
- <outputDirectory>web</outputDirectory>
+ <outputDirectory>lib/web</outputDirectory>
<includes>
<include>org.codehaus.sonar:sonar-web</include>
</includes>
diff --git a/sonar-application/pom.xml b/sonar-application/pom.xml
index 7bdaba42b25..125a5c05f5d 100644
--- a/sonar-application/pom.xml
+++ b/sonar-application/pom.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -10,19 +11,21 @@
<artifactId>sonar-application</artifactId>
<packaging>jar</packaging>
-
<name>SonarQube :: Application</name>
<description>Package the standalone distribution</description>
- <dependencies>
+ <properties>
+ <assembly.recompressZippedFiles>true</assembly.recompressZippedFiles>
+ </properties>
+ <dependencies>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-process</artifactId>
<version>${project.version}</version>
</dependency>
-
+
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
@@ -145,8 +148,8 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.sonatype.jsw-binaries</groupId>
- <artifactId>jsw-binaries</artifactId>
+ <groupId>org.sonatype.jsw-binaries</groupId>
+ <artifactId>jsw-binaries</artifactId>
<version>3.2.3.6</version>
<type>tar.gz</type>
<scope>provided</scope>
@@ -236,7 +239,7 @@
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
- <recompressZippedFiles>true</recompressZippedFiles>
+ <recompressZippedFiles>${assembly.recompressZippedFiles}</recompressZippedFiles>
</configuration>
</execution>
</executions>
@@ -248,8 +251,8 @@
<phase>package</phase>
<configuration>
<target>
- <checksum file="${project.build.directory}/sonarqube-${project.version}.zip" algorithm="md5" />
- <checksum file="${project.build.directory}/sonarqube-${project.version}.zip" algorithm="sha" />
+ <checksum file="${project.build.directory}/sonarqube-${project.version}.zip" algorithm="md5"/>
+ <checksum file="${project.build.directory}/sonarqube-${project.version}.zip" algorithm="sha"/>
</target>
</configuration>
<goals>
@@ -298,6 +301,12 @@
</plugins>
</build>
</profile>
+ <profile>
+ <id>dev</id>
+ <properties>
+ <assembly.recompressZippedFiles>false</assembly.recompressZippedFiles>
+ </properties>
+ </profile>
</profiles>
</project>
diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties
index 603baed9fd0..be3d1c09d70 100644
--- a/sonar-application/src/main/assembly/conf/sonar.properties
+++ b/sonar-application/src/main/assembly/conf/sonar.properties
@@ -9,14 +9,14 @@
#--------------------------------------------------------------------------------------------------
# TO BE DOCUMENTED - WORK IN PROGRESS
#sonar.es.javaOpts=-Djava.net.preferIPv4Stack=true -server -Xmx256m -Xms256m -Xss256k -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
-#sonar.es.port=0
-#sonar.es.jmxPort=0
+#sonar.es.port=9001
+#sonar.es.jmxPort=9002
# For debug only
#sonar.es.httpPort=
#sonar.web.javaOpts=-server -Xmx768m -XX:MaxPermSize=160m -XX:+HeapDumpOnOutOfMemoryError
-#sonar.web.jmxPort=0
+#sonar.web.jmxPort=9003
# Paths are absolute or relative to installation root directory
#sonar.path.data=data
@@ -31,10 +31,11 @@
# Please use a production-ready database. Supported databases are MySQL, Oracle, PostgreSQL
# and Microsoft SQLServer.
+# User credentials.
# Permissions to create tables, indices and triggers must be granted to JDBC user.
# The schema must be created first.
-sonar.jdbc.username=sonar
-sonar.jdbc.password=sonar
+#sonar.jdbc.username=sonar
+#sonar.jdbc.password=sonar
#----- Embedded database H2
# Note: it does not accept connections from remote hosts, so the
diff --git a/sonar-application/src/main/java/org/sonar/application/DefaultSettings.java b/sonar-application/src/main/java/org/sonar/application/DefaultSettings.java
new file mode 100644
index 00000000000..22d31ddef17
--- /dev/null
+++ b/sonar-application/src/main/java/org/sonar/application/DefaultSettings.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.application;
+
+import org.sonar.process.NetworkUtils;
+import org.sonar.process.Props;
+
+class DefaultSettings {
+
+ private DefaultSettings() {
+ // only static stuff
+ }
+
+ static final String ES_PORT_KEY = "sonar.es.port";
+ private static final int ES_PORT_DEFVAL = 9001;
+
+ static final String ES_CLUSTER_NAME_KEY = "sonar.es.clusterName";
+ private static final String ES_CLUSTER_NAME_DEFVAL = "sonarqube";
+
+ static final String ES_JMX_PORT_KEY = "sonar.es.jmxPort";
+ private static final int ES_JMX_PORT_DEFVAL = 9002;
+
+ static final String ES_JAVA_OPTS_KEY = "sonar.es.javaOpts";
+ private static final String ES_JAVA_OPTS_DEFVAL = "-server -Xmx256m -Xms256m -Xss256k -Djava.net.preferIPv4Stack=true " +
+ "-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly " +
+ "-XX:+HeapDumpOnOutOfMemoryError";
+
+
+ static final String WEB_JMX_PORT_KEY = "sonar.web.jmxPort";
+ private static final int WEB_JMX_PORT_DEFVAL = 9003;
+
+ static final String WEB_JAVA_OPTS_KEY = "sonar.web.javaOpts";
+ private static final String WEB_JAVA_OPTS_DEFVAL = "-server -Xmx768m -XX:MaxPermSize=160m -XX:+HeapDumpOnOutOfMemoryError";
+ static final String WEB_JAVA_OPTS_APPENDED_VAL = "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djruby.management.enabled=false";
+
+ static final String JDBC_LOGIN_KEY = "sonar.jdbc.username";
+ private static final String JDBC_LOGIN_DEFVAL = "sonar";
+ static final String JDBC_PASSWORD_KEY = "sonar.jdbc.password";
+ private static final String JDBC_PASSWORD_DEFVAL = "sonar";
+
+ static void initDefaults(Props props) {
+ // elasticsearch
+ props.set("sonar.es.type", "TRANSPORT");
+ props.setDefault(DefaultSettings.ES_CLUSTER_NAME_KEY, DefaultSettings.ES_CLUSTER_NAME_DEFVAL);
+ setDefaultPort(props, DefaultSettings.ES_PORT_KEY, DefaultSettings.ES_PORT_DEFVAL, "Elasticsearch");
+ setDefaultPort(props, DefaultSettings.ES_JMX_PORT_KEY, DefaultSettings.ES_JMX_PORT_DEFVAL, "Elasticsearch JMX");
+ props.setDefault(DefaultSettings.ES_JAVA_OPTS_KEY, DefaultSettings.ES_JAVA_OPTS_DEFVAL);
+
+ // web
+ setDefaultPort(props, DefaultSettings.WEB_JMX_PORT_KEY, DefaultSettings.WEB_JMX_PORT_DEFVAL, "HTTP Server JMX");
+ props.setDefault(DefaultSettings.WEB_JAVA_OPTS_KEY, DefaultSettings.WEB_JAVA_OPTS_DEFVAL);
+ props.setDefault(DefaultSettings.JDBC_LOGIN_KEY, DefaultSettings.JDBC_LOGIN_DEFVAL);
+ props.setDefault(DefaultSettings.JDBC_PASSWORD_KEY, DefaultSettings.JDBC_PASSWORD_DEFVAL);
+ }
+
+ private static void setDefaultPort(Props props, String propertyKey, int defaultPort, String label) {
+ int port = props.intOf(propertyKey, -1);
+ if (port == -1) {
+ port = defaultPort;
+ } else if (port == 0) {
+ port = NetworkUtils.freePort();
+ }
+ props.set(propertyKey, String.valueOf(port));
+ }
+}
diff --git a/sonar-application/src/main/java/org/sonar/application/Installation.java b/sonar-application/src/main/java/org/sonar/application/Installation.java
index ce9c451e4b0..fbc86ec973a 100644
--- a/sonar-application/src/main/java/org/sonar/application/Installation.java
+++ b/sonar-application/src/main/java/org/sonar/application/Installation.java
@@ -24,7 +24,6 @@ import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.process.NetworkUtils;
import org.sonar.process.Props;
import javax.annotation.CheckForNull;
@@ -39,7 +38,7 @@ public class Installation {
private static final Logger LOG = LoggerFactory.getLogger(Installation.class);
private final File homeDir;
- private final File tempDir, dataDir, logsDir, webDir;
+ private final File tempDir, logsDir;
private final Props props;
Installation() throws URISyntaxException, IOException {
@@ -48,14 +47,13 @@ public class Installation {
homeDir = appJar.getParentFile().getParentFile();
props = initProps(homeDir);
+ DefaultSettings.initDefaults(props);
// init file system
+ initExistingDir("sonar.path.data", "data");
+ initExistingDir("sonar.path.web", "lib/web");
this.tempDir = initTempDir("sonar.path.temp", "temp");
- this.dataDir = existingDir("sonar.path.data", "data");
- this.logsDir = existingDir("sonar.path.logs", "logs");
- this.webDir = existingDir("sonar.path.web", "web");
-
- initElasticsearch();
+ this.logsDir = initExistingDir("sonar.path.logs", "logs");
}
/**
@@ -80,17 +78,6 @@ public class Installation {
return new Props(p);
}
- private void initElasticsearch() {
- int port = props.intOf("sonar.es.port", 0);
- if (port <= 0) {
- props.set("sonar.es.port", String.valueOf(NetworkUtils.freePort()));
- }
- if (props.of("sonar.es.cluster.name") == null) {
- props.set("sonar.es.cluster.name", "sonarqube");
- }
- props.set("sonar.es.type", "TRANSPORT");
- }
-
File homeDir() {
return homeDir;
}
@@ -120,7 +107,7 @@ public class Installation {
return dir;
}
- private File existingDir(String propKey, String defaultRelativePath) throws IOException {
+ private File initExistingDir(String propKey, String defaultRelativePath) throws IOException {
File dir = configuredDir(propKey, defaultRelativePath);
if (!dir.exists()) {
throw new IllegalStateException(String.format("Directory does not exist: %s. Please check property %s", dir.getAbsolutePath(), propKey));
@@ -146,6 +133,11 @@ public class Installation {
return props.of(key, defaultValue);
}
+ @CheckForNull
+ String prop(String key) {
+ return props.of(key);
+ }
+
public Props props() {
return props;
}
diff --git a/sonar-application/src/main/java/org/sonar/application/StartServer.java b/sonar-application/src/main/java/org/sonar/application/StartServer.java
index d7a8a89aaa5..84d83783aaf 100644
--- a/sonar-application/src/main/java/org/sonar/application/StartServer.java
+++ b/sonar-application/src/main/java/org/sonar/application/StartServer.java
@@ -22,7 +22,6 @@ package org.sonar.application;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.process.Monitor;
-import org.sonar.process.NetworkUtils;
import org.sonar.process.ProcessWrapper;
import javax.annotation.Nullable;
@@ -49,11 +48,10 @@ public class StartServer {
monitor = new Monitor();
- String opts = installation.prop("sonar.es.javaOpts", "-server -Xmx256m -Xms128m -Xss256k -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly");
elasticsearch = new ProcessWrapper("ES")
.setWorkDir(installation.homeDir())
- .setJmxPort(NetworkUtils.freePort())
- .addJavaOpts(opts)
+ .setJmxPort(Integer.parseInt(installation.prop(DefaultSettings.ES_JMX_PORT_KEY)))
+ .addJavaOpts(installation.prop(DefaultSettings.ES_JAVA_OPTS_KEY))
.addJavaOpts("-Djava.io.tmpdir=" + installation.tempDir().getAbsolutePath())
.addJavaOpts("-Dsonar.path.logs=" + installation.logsDir().getAbsolutePath())
.setClassName("org.sonar.search.ElasticSearch")
@@ -64,12 +62,11 @@ public class StartServer {
monitor.registerProcess(elasticsearch);
- opts = installation.prop("sonar.web.javaOpts", "-Xmx768m -server -XX:MaxPermSize=160m -XX:+HeapDumpOnOutOfMemoryError");
server = new ProcessWrapper("SQ")
.setWorkDir(installation.homeDir())
- .setJmxPort(NetworkUtils.freePort())
- .addJavaOpts(opts)
- .addJavaOpts("-Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djruby.management.enabled=false")
+ .setJmxPort(Integer.parseInt(installation.prop(DefaultSettings.WEB_JMX_PORT_KEY)))
+ .addJavaOpts(installation.prop(DefaultSettings.WEB_JAVA_OPTS_KEY))
+ .addJavaOpts(DefaultSettings.WEB_JAVA_OPTS_APPENDED_VAL)
.addJavaOpts("-Djava.io.tmpdir=" + installation.tempDir().getAbsolutePath())
.addJavaOpts("-Dsonar.path.logs=" + installation.logsDir().getAbsolutePath())
.setClassName("org.sonar.server.app.ServerProcess")
@@ -114,7 +111,7 @@ public class StartServer {
try {
process.join();
} catch (InterruptedException e) {
- process = null;
+ // TODO
}
}
}