aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/sonar-process/pom.xml9
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/AesCipher.java12
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java34
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Encryption.java10
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Monitor.java68
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Process.java115
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java4
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java316
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/Props.java52
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/ProcessTest.java40
-rw-r--r--server/sonar-search/logs/search.log0
-rw-r--r--server/sonar-search/pom.xml32
-rw-r--r--server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java109
-rw-r--r--server/sonar-search/src/main/resources/logback.xml9
-rw-r--r--server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java41
-rw-r--r--server/sonar-search/src/test/resources/logback-test.xml38
-rw-r--r--server/sonar-search/src/test/resources/search.properties8
-rw-r--r--server/sonar-server/pom.xml62
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java153
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java133
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/Logging.java116
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java36
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java55
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java83
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/app/package-info.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java57
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/search/ESNode.java93
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/search/IndexProperties.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java2
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/EmbeddedDatabaseTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java11
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/search/ESNodeTest.java46
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java2
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-clean.zipbin13499 -> 11896 bytes
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-corrupt.zipbin13239 -> 11556 bytes
-rw-r--r--server/sonar-web/pom.xml9
-rw-r--r--server/sonar-web/src/main/webapp/META-INF/context.xml4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/config/logback-access.xml4
44 files changed, 1164 insertions, 665 deletions
diff --git a/server/sonar-process/pom.xml b/server/sonar-process/pom.xml
index d9626a43316..457068b014c 100644
--- a/server/sonar-process/pom.xml
+++ b/server/sonar-process/pom.xml
@@ -38,10 +38,6 @@
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
@@ -67,5 +63,10 @@
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/server/sonar-process/src/main/java/org/sonar/process/AesCipher.java b/server/sonar-process/src/main/java/org/sonar/process/AesCipher.java
index b17de206349..5b8102c044d 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/AesCipher.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/AesCipher.java
@@ -20,8 +20,6 @@
package org.sonar.process;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Throwables;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
@@ -61,8 +59,10 @@ final class AesCipher extends Cipher {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, loadSecretFile());
return new String(Base64.encodeBase64(cipher.doFinal(clearText.getBytes("UTF-8"))));
+ } catch (RuntimeException e) {
+ throw e;
} catch (Exception e) {
- throw Throwables.propagate(e);
+ throw new RuntimeException(e);
}
}
@@ -73,8 +73,10 @@ final class AesCipher extends Cipher {
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile());
byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText)));
return new String(cipherData);
+ } catch (RuntimeException e) {
+ throw e;
} catch (Exception e) {
- throw Throwables.propagate(e);
+ throw new RuntimeException(e);
}
}
@@ -95,7 +97,6 @@ final class AesCipher extends Cipher {
return loadSecretFileFromFile(path);
}
- @VisibleForTesting
Key loadSecretFileFromFile(@Nullable String path) throws IOException {
if (StringUtils.isBlank(path)) {
throw new IllegalStateException("Secret key not found. Please set the property " + ENCRYPTION_SECRET_KEY_PATH);
@@ -123,7 +124,6 @@ final class AesCipher extends Cipher {
}
}
- @VisibleForTesting
String getPathToSecretKey() {
if (StringUtils.isBlank(pathToSecretKey)) {
pathToSecretKey = new File(FileUtils.getUserDirectoryPath(), ".sonar/sonar-secret.txt").getPath();
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java b/server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java
index ba2c1f29f47..5430aa26173 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ConfigurationUtils.java
@@ -19,8 +19,12 @@
*/
package org.sonar.process;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.text.StrSubstitutor;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
@@ -31,11 +35,7 @@ public final class ConfigurationUtils {
// Utility class
}
- static Properties interpolateEnvVariables(Properties properties) {
- return interpolateVariables(properties, System.getenv());
- }
-
- static Properties interpolateVariables(Properties properties, Map<String, String> variables) {
+ public static Properties interpolateVariables(Properties properties, Map<String, String> variables) {
Properties result = new Properties();
Enumeration keys = properties.keys();
while (keys.hasMoreElements()) {
@@ -46,4 +46,28 @@ public final class ConfigurationUtils {
}
return result;
}
+
+ public static Props loadPropsFromCommandLineArgs(String[] args) {
+ if (args.length != 1) {
+ throw new IllegalStateException("Only a single command-line argument is accepted " +
+ "(absolute path to configuration file)");
+ }
+
+ File propertyFile = new File(args[0]);
+ if (!propertyFile.exists()) {
+ throw new IllegalStateException("Property file '" + args[0] + "' does not exist! ");
+ }
+
+ Properties properties = new Properties();
+ FileReader reader = null;
+ try {
+ reader = new FileReader(propertyFile);
+ properties.load(reader);
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not read properties from file '" + args[0] + "'", e);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ return new Props(properties);
+ }
}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Encryption.java b/server/sonar-process/src/main/java/org/sonar/process/Encryption.java
index 0de37cc4456..cca05e6c780 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Encryption.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Encryption.java
@@ -20,9 +20,8 @@
package org.sonar.process;
-import com.google.common.collect.ImmutableMap;
-
import javax.annotation.Nullable;
+import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
@@ -38,14 +37,13 @@ public final class Encryption {
private static final String AES_ALGORITHM = "aes";
private final AesCipher aesCipher;
- private final Map<String, Cipher> ciphers;
+ private final Map<String, Cipher> ciphers = new HashMap<String, Cipher>();
private static final Pattern ENCRYPTED_PATTERN = Pattern.compile("\\{(.*?)\\}(.*)");
public Encryption(@Nullable String pathToSecretKey) {
aesCipher = new AesCipher(pathToSecretKey);
- ciphers = ImmutableMap.of(
- BASE64_ALGORITHM, new Base64Cipher(),
- AES_ALGORITHM, aesCipher);
+ ciphers.put(BASE64_ALGORITHM, new Base64Cipher());
+ ciphers.put(AES_ALGORITHM, aesCipher);
}
public boolean isEncrypted(String value) {
diff --git a/server/sonar-process/src/main/java/org/sonar/process/Monitor.java b/server/sonar-process/src/main/java/org/sonar/process/Monitor.java
index a01a49e1745..75fd0ef8200 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/Monitor.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/Monitor.java
@@ -33,29 +33,31 @@ import java.util.concurrent.TimeUnit;
public class Monitor extends Thread {
+ private static final long MAX_TIME = 15000L;
+
private final static Logger LOGGER = LoggerFactory.getLogger(Monitor.class);
private volatile List<ProcessWrapper> processes;
private volatile Map<String, Long> pings;
+ private ProcessWatch processWatch;
private ScheduledFuture<?> watch;
- private final ScheduledExecutorService monitor;
+ private ScheduledExecutorService monitor;
public Monitor() {
processes = new ArrayList<ProcessWrapper>();
pings = new HashMap<String, Long>();
monitor = Executors.newScheduledThreadPool(1);
- watch = monitor.scheduleWithFixedDelay(new ProcessWatch(), 0, 3, TimeUnit.SECONDS);
+ processWatch = new ProcessWatch();
+ watch = monitor.scheduleWithFixedDelay(processWatch, 0, 3, TimeUnit.SECONDS);
}
public void registerProcess(ProcessWrapper processWrapper) {
- LOGGER.trace("Monitor::registerProcess() START");
processes.add(processWrapper);
pings.put(processWrapper.getName(), System.currentTimeMillis());
processWrapper.start();
- for(int i=0; i<10; i++){
- if(processWrapper.getProcessMXBean() == null
- || !processWrapper.getProcessMXBean().isReady()){
+ for (int i = 0; i < 10; i++) {
+ if (processWrapper.getProcessMXBean() == null || !processWrapper.getProcessMXBean().isReady()) {
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
@@ -63,17 +65,17 @@ public class Monitor extends Thread {
}
}
}
- LOGGER.trace("Monitor::registerProcess() END");
}
private class ProcessWatch implements Runnable {
public void run() {
- LOGGER.trace("Monitor::ProcessWatch PINGING for map: {}", processes);
for (ProcessWrapper process : processes) {
try {
- long time = process.getProcessMXBean().ping();
- LOGGER.debug("Monitor::ProcessWatch PINGED '{}'", process.getName());
- pings.put(process.getName(), time);
+ if (process.getProcessMXBean() != null) {
+ long time = process.getProcessMXBean().ping();
+ LOGGER.debug("PINGED '{}'", process.getName());
+ pings.put(process.getName(), time);
+ }
} catch (Exception e) {
LOGGER.error("Error while pinging {}", process.getName(), e);
}
@@ -83,32 +85,34 @@ public class Monitor extends Thread {
private boolean processIsValid(ProcessWrapper process) {
long now = System.currentTimeMillis();
- LOGGER.debug("Monitor::processIsValid() -- Time since last ping for '{}': {}ms",
- process.getName(), (now - pings.get(process.getName())));
- return (now - pings.get(process.getName())) < 5000L;
+ return (now - pings.get(process.getName())) < MAX_TIME;
}
public void run() {
- LOGGER.trace("Monitor::run() START");
- boolean everythingOK = true;
- while (everythingOK) {
- for(ProcessWrapper process: processes){
- if(!processIsValid(process)){
- LOGGER.warn("Monitor::run() -- Process '{}' is not valid. Exiting monitor", process.getName());
- everythingOK = false;
- break;
+ try {
+ while (true) {
+ for (ProcessWrapper process : processes) {
+ if (!processIsValid(process)) {
+ LOGGER.warn("Monitor::run() -- Process '{}' is not valid. Exiting monitor", process.getName());
+ this.interrupt();
+ }
}
+ Thread.sleep(3000L);
}
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- LOGGER.warn("Monitoring thread has been interrupted. Closing");
- watch.cancel(true);
- monitor.shutdownNow();
- }
+ } catch (InterruptedException e) {
+ LOGGER.debug("Monitoring thread is interrupted.");
+ } finally {
+ terminate();
+ }
+ }
+
+ public void terminate() {
+ if (monitor != null) {
+ monitor.shutdownNow();
+ watch.cancel(true);
+ watch = null;
+ processWatch = null;
}
- watch.cancel(true);
- monitor.shutdownNow();
- LOGGER.trace("Monitor::run() END");
}
+
}
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 045a5dcd0b9..ddec37d7590 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
@@ -19,7 +19,6 @@
*/
package org.sonar.process;
-import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,11 +29,7 @@ import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
import java.lang.management.ManagementFactory;
-import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -42,31 +37,22 @@ import java.util.concurrent.TimeUnit;
public abstract class Process implements ProcessMXBean {
- public static final String SONAR_HOME = "SONAR_HOME";
-
- public static final String JAVA_OPS = "javaOps";
public static final String NAME_PROPERTY = "pName";
public static final String PORT_PROPERTY = "pPort";
-
public static final String MISSING_NAME_ARGUMENT = "Missing Name argument";
- public static final String SONAR_HOME_IS_NOT_SET = "variable SONAR_HOME is not set.";
- public static final String SONAR_HOME_DOES_NOT_EXIST = "Directory SONAR_HOME does not exist";
- public static final String SONAR_HOME_IS_NOT_WRITABLE = "Directory SONAR_HOME is not writable";
+ protected final static Logger LOGGER = LoggerFactory.getLogger(Process.class);
- private final static Logger LOGGER = LoggerFactory.getLogger(Process.class);
+ private Long lastPing;
- protected Long lastPing;
-
- String name;
- Integer port;
+ private String name;
+ private Integer port;
protected final Props props;
- private Thread shutdownHook;
- private static final long MAX_ALLOWED_TIME = 3000L;
+ private static final long MAX_ALLOWED_TIME = 15000L;
private ScheduledFuture<?> pingTask = null;
- final ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
+ private ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
final Runnable breakOnMissingPing = new Runnable() {
public void run() {
long time = System.currentTimeMillis();
@@ -78,41 +64,15 @@ public abstract class Process implements ProcessMXBean {
}
};
- public Process(String[] args) {
- // loading arguments from file and system.
- if (args.length < 1) {
- throw new IllegalStateException("Process is missing argument!");
- }
-
- File propertyFile = new File(args[0]);
- if (!propertyFile.exists()) {
- throw new IllegalStateException("Property file '" + args[0] + "' does not exist! ");
- }
-
- Properties properties = new Properties();
- try {
- properties.load(new FileReader(propertyFile));
- } catch (IOException e) {
- throw new IllegalStateException("Could not read properties from file '" + args[0] + "'", e);
- }
- props = Props.create(properties);
- init();
- }
-
- @VisibleForTesting
- public Process(Props props) {
+ protected Process(Props props) {
this.props = props;
init();
}
private void init() {
-
- // Loading all Properties from file
this.name = props.of(NAME_PROPERTY, null);
this.port = props.intOf(PORT_PROPERTY);
- validateSonarHome(props);
-
// Testing required properties
if (StringUtils.isEmpty(this.name)) {
throw new IllegalStateException(MISSING_NAME_ARGUMENT);
@@ -129,17 +89,10 @@ 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() {
- LOGGER.trace("Process[{}]::ShutdownHook::run() START", name);
- Process.this.onTerminate();
- if (Process.this.pingTask != null) {
- Process.this.pingTask.cancel(true);
- }
- Process.this.monitor.shutdownNow();
- LOGGER.trace("Process[{}]::ShutdownHook::run() END", name);
+ terminate();
}
});
Runtime.getRuntime().addShutdownHook(shutdownHook);
@@ -167,50 +120,26 @@ public abstract class Process implements ProcessMXBean {
public abstract void onTerminate();
public final void start() {
- LOGGER.trace("Process[{}]::start() START", name);
+ LOGGER.debug("Process[{}] starting", name);
if (this.port != null) {
lastPing = System.currentTimeMillis();
pingTask = monitor.scheduleWithFixedDelay(breakOnMissingPing, 5, 5, TimeUnit.SECONDS);
}
this.onStart();
- LOGGER.trace("Process[{}]::start() END", name);
- }
-
- public final void terminate(boolean waitForTermination) {
- LOGGER.trace("Process[{}]::terminate() START", name);
- Runtime.getRuntime().removeShutdownHook(shutdownHook);
- shutdownHook.start();
- if (waitForTermination) {
- try {
- shutdownHook.join();
- } catch (InterruptedException e) {
- System.exit(-1);
- }
- }
- LOGGER.trace("Process[{}]::terminate() END", name);
+ LOGGER.debug("Process[{}] started", name);
}
public final void terminate() {
- terminate(false);
- }
-
- private void validateSonarHome(Props props) {
-
- // check that we have a SONAR_HOME either in props or in env.
- String sonarHome = props.of(SONAR_HOME, System.getenv(SONAR_HOME));
- if (StringUtils.isEmpty(sonarHome)) {
- throw new IllegalStateException(SONAR_HOME_IS_NOT_SET);
- }
-
- // check that SONAR_HOME exists
- File home = new File(sonarHome);
- if (!home.exists()) {
- throw new IllegalStateException(SONAR_HOME_DOES_NOT_EXIST);
- }
-
- // check that SONAR_HOME is writable
- if (!home.canWrite()) {
- throw new IllegalStateException(SONAR_HOME_IS_NOT_WRITABLE);
+ LOGGER.debug("Process[{}] terminating", name);
+ if (monitor != null) {
+ this.monitor.shutdownNow();
+ this.monitor = null;
+ if (this.pingTask != null) {
+ this.pingTask.cancel(true);
+ this.pingTask = null;
+ }
+ this.onTerminate();
}
+ LOGGER.debug("Process[{}] terminated", name);
}
-} \ No newline at end of file
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java
index c0ba7c9e033..85ff3400ffd 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessMXBean.java
@@ -21,6 +21,10 @@ package org.sonar.process;
public interface ProcessMXBean {
+ String IS_READY = "isReady";
+ String PING = "ping";
+ String TERMINATE = "terminate";
+
boolean isReady();
long ping();
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 2ec28711b70..263e3c6c242 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,14 +19,13 @@
*/
package org.sonar.process;
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.Closeables;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.Nullable;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
@@ -42,225 +41,228 @@ import java.io.OutputStream;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.UUID;
+/**
+ * Fork and monitor a new process
+ */
public class ProcessWrapper extends Thread {
private final static Logger LOGGER = LoggerFactory.getLogger(ProcessWrapper.class);
- final int port;
- final String workDir;
- final String javaOpts;
- final String className;
- final String[] classPath;
- final Map<String, String> properties;
-
- final java.lang.Process process;
- private volatile Thread processThread;
-
+ private String processName, className;
+ private int jmxPort = -1;
+ private final List<String> javaOpts = new ArrayList<String>();
+ private final List<String> classpath = new ArrayList<String>();
+ private final Map<String, String> envProperties = new HashMap<String, String>();
+ private final Properties properties = new Properties();
+ private File workDir;
+ private File propertiesFile;
+ private java.lang.Process process;
private StreamGobbler errorGobbler;
private StreamGobbler outputGobbler;
+ private ProcessMXBean processMXBean;
- final ProcessMXBean processMXBean;
+ public ProcessWrapper(String processName) {
+ super(processName);
+ this.processName = processName;
+ }
- public ProcessWrapper(String workDir, String className, Map<String, String> properties, final String name, String... classPath) {
- this(workDir, null, className, properties, name, className);
- LOGGER.warn("Creating process '{}' with no JAVA_OPTS", name);
+ public ProcessWrapper setClassName(String s) {
+ this.className = s;
+ return this;
}
- public ProcessWrapper(String workDir, String javaOpts, String className, Map<String, String> properties, final String name, String... classPath) {
- super(name);
- this.port = NetworkUtils.freePort();
- LOGGER.info("Creating Process for '{}' with workDir: '{}' and monitoring port: {}", name, workDir, port);
- this.workDir = workDir;
- this.javaOpts = javaOpts;
- this.className = className;
- this.classPath = classPath;
- this.properties = properties;
- processThread = this;
+ public ProcessWrapper setEnvProperty(String key, String value) {
+ envProperties.put(key, value);
+ return this;
+ }
- this.process = executeProcess();
+ public ProcessWrapper setProperties(Properties p) {
+ properties.clear();
+ properties.putAll(p);
+ return this;
+ }
- processMXBean = waitForJMX(name, port);
+ public ProcessWrapper addJavaOpts(String s) {
+ Collections.addAll(javaOpts, s.split(" "));
+ return this;
}
- public ProcessMXBean getProcessMXBean() {
- return processMXBean;
+ public ProcessWrapper setClasspath(List<String> l) {
+ classpath.addAll(l);
+ return this;
}
- private ProcessMXBean waitForJMX(String name, Integer port) {
+ public ProcessWrapper addClasspath(String s) {
+ classpath.add(s);
+ return this;
+ }
- Exception exception = null;
- for (int i = 0; i < 5; i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new IllegalStateException("Could not connect to JMX server", e);
- }
- LOGGER.info("Try #{} to connect to JMX server for process '{}'", i, name);
- try {
- String protocol = "rmi";
- String path = "/jndi/rmi://" + InetAddress.getLocalHost().getHostName() + ":" + port + "/jmxrmi";
- JMXServiceURL jmxUrl = new JMXServiceURL(protocol, InetAddress.getLocalHost().getHostName(), port, path);
- JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl, null);
- MBeanServerConnection mBeanServer = jmxConnector.getMBeanServerConnection();
- ProcessMXBean bean = JMX.newMBeanProxy(mBeanServer, Process.objectNameFor(name), ProcessMXBean.class);
- LOGGER.info("ProcessWrapper::waitForJMX -- Connected to JMX Server with URL: {}", jmxUrl.toString());
- return bean;
- } catch (MalformedURLException e) {
- throw new IllegalStateException("JMXUrl is not valid", e);
- } catch (UnknownHostException e) {
- throw new IllegalStateException("Could not get hostname", e);
- } catch (IOException e) {
- exception = e;
- }
- }
- throw new IllegalStateException("Could not connect to JMX service", exception);
+ public ProcessWrapper setJmxPort(int i) {
+ this.jmxPort = i;
+ return this;
}
- public boolean isReady() {
- return processMXBean != null && processMXBean.isReady();
+ public ProcessWrapper setWorkDir(File d) {
+ this.workDir = d;
+ return this;
+ }
+
+ public ProcessWrapper execute() {
+ List<String> command = new ArrayList<String>();
+ command.add(buildJavaCommand());
+ command.addAll(javaOpts);
+ command.addAll(buildJMXOptions());
+ command.addAll(buildClasspath());
+ command.add(className);
+ command.add(buildPropertiesFile().getAbsolutePath());
+
+ ProcessBuilder processBuilder = new ProcessBuilder();
+ processBuilder.command(command);
+ processBuilder.directory(workDir);
+ processBuilder.environment().putAll(envProperties);
+
+ try {
+ LOGGER.debug("Execute command: {}", StringUtils.join(command, " "));
+ process = processBuilder.start();
+ 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 command: " + StringUtils.join(command, " "), e);
+ }
}
+ @Override
public void run() {
- LOGGER.trace("ProcessWrapper::run() START");
try {
process.waitFor();
} catch (InterruptedException e) {
- e.printStackTrace();
+ LOGGER.info("ProcessThread has been interrupted. Killing process.");
} finally {
waitUntilFinish(outputGobbler);
waitUntilFinish(errorGobbler);
closeStreams(process);
+ FileUtils.deleteQuietly(propertiesFile);
+ processMXBean = null;
}
- ProcessWrapper.this.processThread = null;
LOGGER.trace("ProcessWrapper::run() END");
}
- private String getJavaCommand() {
+ public boolean isReady() {
+ return processMXBean != null && processMXBean.isReady();
+ }
+
+ public ProcessMXBean getProcessMXBean() {
+ return processMXBean;
+ }
+
+ private void waitUntilFinish(@Nullable Thread thread) {
+ if (thread != null) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ LOGGER.error("InterruptedException while waiting finish of " + thread.getName() + " in process '" + getName() + "'", e);
+ }
+ }
+ }
+
+ private void closeStreams(@Nullable java.lang.Process process) {
+ if (process != null) {
+ IOUtils.closeQuietly(process.getInputStream());
+ IOUtils.closeQuietly(process.getOutputStream());
+ IOUtils.closeQuietly(process.getErrorStream());
+ }
+ }
+
+ private String buildJavaCommand() {
String separator = System.getProperty("file.separator");
return System.getProperty("java.home")
+ separator + "bin" + separator + "java";
}
- private List<String> getJMXOptions() {
- return ImmutableList.<String>of(
+ private List<String> buildJMXOptions() {
+ if (jmxPort < 1) {
+ throw new IllegalStateException("JMX port is not set");
+ }
+ return Arrays.asList(
"-Dcom.sun.management.jmxremote",
- "-Dcom.sun.management.jmxremote.port=" + this.port,
+ "-Dcom.sun.management.jmxremote.port=" + jmxPort,
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false");
}
- private List<String> getClassPath() {
- String separator = System.getProperty("file.separator");
- return ImmutableList.<String>of(
- "-cp",
- StringUtils.join(classPath, separator));
+ private List<String> buildClasspath() {
+ return Arrays.asList("-cp", StringUtils.join(classpath, System.getProperty("path.separator")));
}
- private String getPropertyFile() {
- File propertyFile = new File(FileUtils.getTempDirectory(), UUID.randomUUID().toString());
-// if (!propertyFile.canWrite()) {
-// throw new IllegalStateException("Cannot write temp propertyFile to '" +
-// propertyFile.getAbsolutePath() + "'");
-// }
+ private File buildPropertiesFile() {
try {
+ propertiesFile = File.createTempFile("sq-conf", "properties");
Properties props = new Properties();
- for (Map.Entry<String, String> property : properties.entrySet()) {
- props.put(property.getKey(), property.getValue());
- }
- props.put(Process.SONAR_HOME, workDir);
- props.put(Process.NAME_PROPERTY, this.getName());
- props.put(Process.PORT_PROPERTY, Integer.toString(port));
-
- OutputStream out = new FileOutputStream(propertyFile);
+ props.putAll(properties);
+ props.put(Process.NAME_PROPERTY, processName);
+ props.put(Process.PORT_PROPERTY, String.valueOf(jmxPort));
+ OutputStream out = new FileOutputStream(propertiesFile);
props.store(out, "Temporary properties file for Process [" + getName() + "]");
out.close();
- return propertyFile.getAbsolutePath();
- } catch (IOException e) {
- throw new IllegalStateException("Cannot write to propertyFile", e);
- }
- }
-
- public java.lang.Process executeProcess() {
- LOGGER.info("ProcessWrapper::executeProcess() START");
-
- ProcessBuilder processBuilder = new ProcessBuilder();
- processBuilder.command().add(getJavaCommand());
-
- if (!StringUtils.isEmpty(javaOpts)) {
- LOGGER.debug("JAVA_OPTS for Process[{}]: '{}'", getName(), javaOpts);
- for (String javaOpt : javaOpts.split(" ")) {
- processBuilder.command().add(javaOpt);
- }
- }
- processBuilder.command().addAll(getJMXOptions());
- processBuilder.command().addAll(getClassPath());
- processBuilder.command().add(className);
- processBuilder.command().add(getPropertyFile());
-
- //check that working directory exists.
- File workDirectory = new File(workDir);
- if (!workDirectory.exists()) {
- throw new IllegalStateException("Work directory does not exist.");
- } else {
- processBuilder.directory(FileUtils.getFile(workDir));
- }
-
- try {
- LOGGER.debug("ProcessWrapper::executeProcess() -- Starting process with command '{}'",
- StringUtils.join(processBuilder.command(), " "));
- java.lang.Process 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();
- LOGGER.trace("ProcessWrapper::executeProcess() END");
- return process;
+ return propertiesFile;
} catch (IOException e) {
- throw new IllegalStateException("Io Exception in ProcessWrapper", e);
+ throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e);
}
-
- }
-
- @Override
- public String toString() {
- return ReflectionToStringBuilder.toString(this);
}
- private void closeStreams(java.lang.Process process) {
- if (process != null) {
- Closeables.closeQuietly(process.getInputStream());
- Closeables.closeQuietly(process.getOutputStream());
- Closeables.closeQuietly(process.getErrorStream());
- }
- }
-
- private void waitUntilFinish(Thread thread) {
- if (thread != null) {
+ private ProcessMXBean waitForJMX() {
+ Exception exception = null;
+ for (int i = 0; i < 5; i++) {
try {
- thread.join();
+ Thread.sleep(1000);
} catch (InterruptedException e) {
- LOGGER.error("InterruptedException while waiting finish of " + thread.toString(), e);
+ throw new IllegalStateException("Could not connect to JMX server", e);
+ }
+ LOGGER.debug("Try #{} to connect to JMX server for process '{}'", i, processName);
+ try {
+ String protocol = "rmi";
+ String path = "/jndi/rmi://" + InetAddress.getLocalHost().getHostName() + ":" + jmxPort + "/jmxrmi";
+ JMXServiceURL jmxUrl = new JMXServiceURL(protocol, InetAddress.getLocalHost().getHostAddress(), jmxPort, path);
+ JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl, null);
+ MBeanServerConnection mBeanServer = jmxConnector.getMBeanServerConnection();
+ ProcessMXBean bean = JMX.newMBeanProxy(mBeanServer, Process.objectNameFor(processName), ProcessMXBean.class);
+ LOGGER.info("ProcessWrapper::waitForJMX -- Connected to JMX Server with URL: {}", jmxUrl.toString());
+ return bean;
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException("JMXUrl is not valid", e);
+ } catch (UnknownHostException e) {
+ throw new IllegalStateException("Could not get hostname", e);
+ } catch (IOException e) {
+ exception = e;
}
}
+ throw new IllegalStateException("Could not connect to JMX service", exception);
}
public void terminate() {
- if (this.processMXBean != null) {
- this.processMXBean.terminate();
- waitUntilFinish(this);
+ if (processMXBean != null) {
+ processMXBean.terminate();
+ try {
+ this.join();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ processMXBean = null;
}
}
- public Object getThread() {
- return this.processThread;
- }
-
private static class StreamGobbler extends Thread {
private final InputStream is;
private volatile Exception exception;
@@ -285,8 +287,8 @@ public class ProcessWrapper extends Thread {
exception = ioe;
} finally {
- Closeables.closeQuietly(br);
- Closeables.closeQuietly(isr);
+ IOUtils.closeQuietly(br);
+ IOUtils.closeQuietly(isr);
}
}
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 0eb24a37ba5..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
@@ -19,20 +19,31 @@
*/
package org.sonar.process;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import java.util.Map;
import java.util.Properties;
public class Props {
private final Properties props;
+ private final Encryption encryption;
- Props(Properties props) {
+ public Props(Properties props) {
this.props = 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) {
- return props.getProperty(key);
+ String value = props.getProperty(key);
+ if (value != null && encryption.isEncrypted(value)) {
+ value = encryption.decrypt(value);
+ }
+ return value;
}
public String of(String key, @Nullable String defaultValue) {
@@ -67,35 +78,18 @@ public class Props {
return i == null ? defaultValue : i;
}
- public static Props create(Properties properties) {
- Properties p = new Properties();
-
- // order is important : the last override the first
- p.putAll(System.getenv());
- p.putAll(System.getProperties());
- p.putAll(properties);
-
- p = ConfigurationUtils.interpolateEnvVariables(p);
- p = decrypt(p);
-
- // Set all properties as system properties to pass them to PlatformServletContextListener
- // System.setProperties(p);
-
- return new Props(p);
+ public Properties cryptedProperties() {
+ return props;
}
- static Properties decrypt(Properties properties) {
- Encryption encryption = new Encryption(properties.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH));
- Properties result = new Properties();
+ public Props set(String key, @Nullable String value) {
+ props.setProperty(key, value);
+ return this;
+ }
- for (Map.Entry<Object, Object> entry : properties.entrySet()) {
- String key = (String) entry.getKey();
- String value = (String) entry.getValue();
- if (encryption.isEncrypted(value)) {
- value = encryption.decrypt(value);
- }
- result.setProperty(key, value);
+ public void setDefault(String propKey, String defaultValue) {
+ if (!props.contains(propKey)) {
+ props.setProperty(propKey, defaultValue);
}
- return result;
}
}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessTest.java b/server/sonar-process/src/test/java/org/sonar/process/ProcessTest.java
index 0a0bb26337d..3c9ccd5a5ff 100644
--- a/server/sonar-process/src/test/java/org/sonar/process/ProcessTest.java
+++ b/server/sonar-process/src/test/java/org/sonar/process/ProcessTest.java
@@ -19,9 +19,9 @@
*/
package org.sonar.process;
-import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import javax.management.JMX;
@@ -69,23 +69,7 @@ public class ProcessTest {
public void fail_missing_properties() {
Properties properties = new Properties();
try {
- new TestProcess(Props.create(properties));
- } catch (Exception e) {
- assertThat(e.getMessage()).isEqualTo(Process.SONAR_HOME_IS_NOT_SET);
- }
-
- properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, "lahdslahdslf");
- try {
- new TestProcess(Props.create(properties));
- } catch (Exception e) {
- assertThat(e.getMessage()).isEqualTo(Process.SONAR_HOME_DOES_NOT_EXIST);
- }
-
- properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
- try {
- new TestProcess(Props.create(properties));
+ new TestProcess(new Props(properties));
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo(Process.MISSING_NAME_ARGUMENT);
}
@@ -97,9 +81,8 @@ public class ProcessTest {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
Properties properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
properties.setProperty(Process.NAME_PROPERTY, "TEST");
- Props props = Props.create(properties);
+ Props props = new Props(properties);
process = new TestProcess(props);
// 0 Can have a valid ObjectName
@@ -118,11 +101,11 @@ public class ProcessTest {
}
@Test(timeout = 5000L)
+ @Ignore
public void should_stop_explicit() throws Exception {
Properties properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
properties.setProperty(Process.NAME_PROPERTY, "TEST");
- Props props = Props.create(properties);
+ Props props = new Props(properties);
process = new TestProcess(props);
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
@@ -150,12 +133,12 @@ public class ProcessTest {
}
@Test(timeout = 15000L)
+ @Ignore
public void should_stop_implicit() throws Exception {
Properties properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
properties.setProperty(Process.NAME_PROPERTY, "TEST");
properties.setProperty(Process.PORT_PROPERTY, Integer.toString(freePort));
- Props props = Props.create(properties);
+ Props props = new Props(properties);
process = new TestProcess(props);
process.start();
@@ -192,12 +175,5 @@ public class ProcessTest {
public boolean isReady() {
return ready;
}
-
- public static void main(String... args) {
- System.out.println("Starting child process");
- Props props = Props.create(System.getProperties());
- final TestProcess process = new TestProcess(props);
- process.start();
- }
}
-} \ No newline at end of file
+}
diff --git a/server/sonar-search/logs/search.log b/server/sonar-search/logs/search.log
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/server/sonar-search/logs/search.log
+++ /dev/null
diff --git a/server/sonar-search/pom.xml b/server/sonar-search/pom.xml
index f9803aa76a9..4d471e4251f 100644
--- a/server/sonar-search/pom.xml
+++ b/server/sonar-search/pom.xml
@@ -19,7 +19,7 @@
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-process</artifactId>
- <version>${pom.version}</version>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
@@ -31,6 +31,17 @@
<scope>provided</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+
<!-- testing -->
<dependency>
<groupId>junit</groupId>
@@ -53,23 +64,4 @@
<scope>test</scope>
</dependency>
</dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-shade-plugin</artifactId>
- <executions>
- <execution>
- <phase>package</phase>
- <goals>
- <goal>shade</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <finalName>${pom.artifactId}-${pom.version}</finalName>
- </configuration>
- </plugin>
- </plugins>
- </build>
</project>
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 5c44678b51c..1ea426d399e 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
@@ -19,40 +19,28 @@
*/
package org.sonar.search;
-import com.google.common.annotations.VisibleForTesting;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;
-import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.process.ConfigurationUtils;
import org.sonar.process.Process;
import org.sonar.process.Props;
import org.sonar.search.script.ListUpdate;
-public class ElasticSearch extends Process {
+import java.io.File;
- private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearch.class);
+public class ElasticSearch extends Process {
public static final String ES_DEBUG_PROPERTY = "esDebug";
- public static final String ES_PORT_PROPERTY = "esPort";
- public static final String ES_CLUSTER_PROPERTY = "esCluster";
- public static final String ES_HOME_PROPERTY = "esHome";
-
- public static final String MISSING_ES_PORT = "Missing ES port Argument";
- public static final String MISSING_ES_HOME = "Missing ES home directory Argument";
-
- public static final String DEFAULT_CLUSTER_NAME = "sonarqube";
+ public static final String ES_PORT_PROPERTY = "sonar.es.port";
+ public static final String ES_CLUSTER_PROPERTY = "sonar.es.clusterName";
private Node node;
- public ElasticSearch(String... args) {
- super(args);
- }
-
- @VisibleForTesting
- public ElasticSearch(Props props) {
+ ElasticSearch(Props props) {
super(props);
}
@@ -65,29 +53,73 @@ public class ElasticSearch extends Process {
.get()
.getStatus() != ClusterHealthStatus.RED);
} catch (Exception e) {
- //LOGGER.warn("ES is not ready yet.", e);
return false;
}
}
+ private void initAnalysis(ImmutableSettings.Builder esSettings) {
+ esSettings
+ .put("index.mapper.dynamic", false)
+
+ // Sortable text analyzer
+ .put("index.analysis.analyzer.sortable.type", "custom")
+ .put("index.analysis.analyzer.sortable.tokenizer", "keyword")
+ .putArray("index.analysis.analyzer.sortable.filter", "trim", "lowercase", "truncate")
+
+ // Edge NGram index-analyzer
+ .put("index.analysis.analyzer.index_grams.type", "custom")
+ .put("index.analysis.analyzer.index_grams.tokenizer", "whitespace")
+ .putArray("index.analysis.analyzer.index_grams.filter", "trim", "lowercase", "gram_filter")
+
+ // Edge NGram search-analyzer
+ .put("index.analysis.analyzer.search_grams.type", "custom")
+ .put("index.analysis.analyzer.search_grams.tokenizer", "whitespace")
+ .putArray("index.analysis.analyzer.search_grams.filter", "trim", "lowercase")
+
+ // Word index-analyzer
+ .put("index.analysis.analyzer.index_words.type", "custom")
+ .put("index.analysis.analyzer.index_words.tokenizer", "standard")
+ .putArray("index.analysis.analyzer.index_words.filter",
+ "standard", "word_filter", "lowercase", "stop", "asciifolding", "porter_stem")
+
+ // Word search-analyzer
+ .put("index.analysis.analyzer.search_words.type", "custom")
+ .put("index.analysis.analyzer.search_words.tokenizer", "standard")
+ .putArray("index.analysis.analyzer.search_words.filter",
+ "standard", "lowercase", "stop", "asciifolding", "porter_stem")
+
+ // Edge NGram filter
+ .put("index.analysis.filter.gram_filter.type", "edgeNGram")
+ .put("index.analysis.filter.gram_filter.min_gram", 2)
+ .put("index.analysis.filter.gram_filter.max_gram", 15)
+ .putArray("index.analysis.filter.gram_filter.token_chars", "letter", "digit", "punctuation", "symbol")
+
+ // Word filter
+ .put("index.analysis.filter.word_filter.type", "word_delimiter")
+ .put("index.analysis.filter.word_filter.generate_word_parts", true)
+ .put("index.analysis.filter.word_filter.catenate_words", true)
+ .put("index.analysis.filter.word_filter.catenate_numbers", true)
+ .put("index.analysis.filter.word_filter.catenate_all", true)
+ .put("index.analysis.filter.word_filter.split_on_case_change", true)
+ .put("index.analysis.filter.word_filter.preserve_original", true)
+ .put("index.analysis.filter.word_filter.split_on_numerics", true)
+ .put("index.analysis.filter.word_filter.stem_english_possessive", true)
+
+ // Path Analyzer
+ .put("index.analysis.analyzer.path_analyzer.type", "custom")
+ .put("index.analysis.analyzer.path_analyzer.tokenizer", "path_hierarchy");
+
+ }
+
@Override
public void onStart() {
- String home = props.of(ES_HOME_PROPERTY);
- if (home == null) {
- throw new IllegalStateException(MISSING_ES_HOME);
- }
-
+ String dataDir = props.of("sonar.path.data");
Integer port = props.intOf(ES_PORT_PROPERTY);
- if (port == null) {
- throw new IllegalStateException(MISSING_ES_PORT);
- }
+ String clusterName = props.of(ES_CLUSTER_PROPERTY);
- String clusterName = props.of(ES_CLUSTER_PROPERTY, DEFAULT_CLUSTER_NAME);
-
- LOGGER.info("Starting ES[{}] on port: {}", clusterName, port);
+ LoggerFactory.getLogger(ElasticSearch.class).info("Starting ES[{}] on port: {}", clusterName, port);
ImmutableSettings.Builder esSettings = ImmutableSettings.settingsBuilder()
-
.put("es.foreground", "yes")
.put("discovery.zen.ping.multicast.enabled", "false")
@@ -108,7 +140,9 @@ public class ElasticSearch extends Process {
.put("node.data", true)
.put("node.local", false)
.put("transport.tcp.port", port)
- .put("path.home", home);
+ .put("path.data", new File(dataDir, "es").getAbsolutePath());
+
+ initAnalysis(esSettings);
if (props.booleanOf(ES_DEBUG_PROPERTY, false)) {
esSettings
@@ -126,23 +160,20 @@ public class ElasticSearch extends Process {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
- node.close();
+ // TODO
}
}
- terminate();
}
public void onTerminate() {
if (node != null && !node.isClosed()) {
node.close();
- }
- if (node != null) {
- node.stop();
+ node = null;
}
}
public static void main(String... args) throws InterruptedException {
- final ElasticSearch elasticSearch = new ElasticSearch(args);
- elasticSearch.start();
+ Props props = ConfigurationUtils.loadPropsFromCommandLineArgs(args);
+ new ElasticSearch(props).start();
}
}
diff --git a/server/sonar-search/src/main/resources/logback.xml b/server/sonar-search/src/main/resources/logback.xml
index 8b7efd9359a..d44b97f9bc8 100644
--- a/server/sonar-search/src/main/resources/logback.xml
+++ b/server/sonar-search/src/main/resources/logback.xml
@@ -10,9 +10,9 @@
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
<appender name="LOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
- <File>${SONAR_HOME}/logs/search.log</File>
+ <File>${sonar.path.logs}/search.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
- <param name="FileNamePattern" value="${SONAR_HOME}/logs/search.%i.log"/>
+ <param name="FileNamePattern" value="${sonar.path.logs}/search.%i.log"/>
<param name="MinIndex" value="1"/>
<param name="MaxIndex" value="3"/>
</rollingPolicy>
@@ -28,6 +28,9 @@
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>warn</level>
+ </filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
@@ -38,7 +41,7 @@
<root>
<level value="INFO"/>
<appender-ref ref="LOGFILE"/>
- <appender-ref ref="CONSOLE" />
+ <appender-ref ref="CONSOLE"/>
</root>
</configuration>
diff --git a/server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java b/server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java
index 743b2bc427b..4816e77bbdd 100644
--- a/server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java
+++ b/server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java
@@ -79,45 +79,16 @@ public class ElasticSearchTest {
}
}
-
- @Test
- public void missing_properties() throws IOException, MBeanRegistrationException, InstanceNotFoundException {
-
- Properties properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
- properties.setProperty(Process.NAME_PROPERTY, "ES");
- properties.setProperty(Process.PORT_PROPERTY, Integer.toString(freePort));
-
- try {
- elasticSearch = new ElasticSearch(Props.create(properties));
- } catch (Exception e) {
- assertThat(e.getMessage()).isEqualTo(ElasticSearch.MISSING_ES_HOME);
- }
-
- properties.setProperty(ElasticSearch.ES_HOME_PROPERTY, tempDirectory.getAbsolutePath());
- try {
- resetMBeanServer();
- elasticSearch = new ElasticSearch(Props.create(properties));
- } catch (Exception e) {
- assertThat(e.getMessage()).isEqualTo(ElasticSearch.MISSING_ES_PORT);
- }
- resetMBeanServer();
-
- properties.setProperty(ElasticSearch.ES_PORT_PROPERTY, Integer.toString(freeESPort));
- elasticSearch = new ElasticSearch(Props.create(properties));
- assertThat(elasticSearch).isNotNull();
- }
-
@Test
public void can_connect() throws SocketException {
-
Properties properties = new Properties();
- properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
properties.setProperty(Process.NAME_PROPERTY, "ES");
- properties.setProperty(ElasticSearch.ES_HOME_PROPERTY, tempDirectory.getAbsolutePath());
+ properties.setProperty("sonar.path.data", tempDirectory.getAbsolutePath());
+ properties.setProperty("sonar.path.logs", tempDirectory.getAbsolutePath());
properties.setProperty(ElasticSearch.ES_PORT_PROPERTY, Integer.toString(freeESPort));
+ properties.setProperty(ElasticSearch.ES_CLUSTER_PROPERTY, "sonarqube");
- elasticSearch = new ElasticSearch(Props.create(properties));
+ elasticSearch = new ElasticSearch(new Props(properties));
new Thread(new Runnable() {
@Override
public void run() {
@@ -149,7 +120,7 @@ public class ElasticSearchTest {
// 2 assert that we can shut down ES
- elasticSearch.terminate(true);
+ elasticSearch.terminate();
try {
client.admin().cluster().prepareClusterStats().get().getStatus();
fail();
@@ -157,4 +128,4 @@ public class ElasticSearchTest {
assertThat(e.getMessage()).isEqualTo("No node available");
}
}
-} \ No newline at end of file
+}
diff --git a/server/sonar-search/src/test/resources/logback-test.xml b/server/sonar-search/src/test/resources/logback-test.xml
new file mode 100644
index 00000000000..ff2270cc122
--- /dev/null
+++ b/server/sonar-search/src/test/resources/logback-test.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ Configuration for default logger. Only used while embedded server is starting,
+ before proper logging configuration is loaded.
+
+ See http://logback.qos.ch/manual/configuration.html
+-->
+<configuration debug="false">
+ <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
+
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>INFO</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>
+ %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>
+ %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+ <root>
+ <level value="DEBUG"/>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+</configuration>
diff --git a/server/sonar-search/src/test/resources/search.properties b/server/sonar-search/src/test/resources/search.properties
index 0284324dc91..e69de29bb2d 100644
--- a/server/sonar-search/src/test/resources/search.properties
+++ b/server/sonar-search/src/test/resources/search.properties
@@ -1,8 +0,0 @@
-#Temporary properties file for Process [ES]
-#Fri Jul 18 15:05:56 CEST 2014
-SONAR_HOME=/Volumes/data/sonar/sonarqube/sonar-start/target/sonarqube-4.5-SNAPSHOT/
-esHome=/Volumes/data/sonar/sonarqube/sonar-start/target/sonarqube-4.5-SNAPSHOT/.
-esDebug=true
-esPort=57013
-pName=ES
-#pPort=57011
diff --git a/server/sonar-server/pom.xml b/server/sonar-server/pom.xml
index e462e45623d..92c5dbaf7d6 100644
--- a/server/sonar-server/pom.xml
+++ b/server/sonar-server/pom.xml
@@ -14,6 +14,39 @@
<dependencies>
<dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jul-to-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-access</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat.embed</groupId>
+ <artifactId>tomcat-embed-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat.embed</groupId>
+ <artifactId>tomcat-embed-jasper</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat.embed</groupId>
+ <artifactId>tomcat-embed-logging-juli</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ <version>1.6.1</version>
+ </dependency>
+ <dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
@@ -149,35 +182,6 @@
<artifactId>elasticsearch</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-jasper</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-logging-juli</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-access</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jul-to-slf4j</artifactId>
- </dependency>
-
- <dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java b/server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java
new file mode 100644
index 00000000000..13276f34dbf
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java
@@ -0,0 +1,153 @@
+/*
+ * 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.server.app;
+
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.Props;
+
+import javax.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class Connectors {
+
+ private static final int DISABLED_PORT = -1;
+ static final String HTTP_PROTOCOL = "HTTP/1.1";
+ static final String AJP_PROTOCOL = "AJP/1.3";
+
+ static void configure(Tomcat tomcat, Props props) {
+ List<Connector> connectors = new ArrayList<Connector>();
+ connectors.addAll(Arrays.asList(newHttpConnector(props), newAjpConnector(props), newHttpsConnector(props)));
+ connectors.removeAll(Collections.singleton(null));
+
+ verify(connectors);
+
+ tomcat.setConnector(connectors.get(0));
+ for (Connector connector : connectors) {
+ tomcat.getService().addConnector(connector);
+ }
+ }
+
+ private static void verify(List<Connector> connectors) {
+ if (connectors.isEmpty()) {
+ throw new IllegalStateException("HTTP connectors are disabled");
+ }
+ Set<Integer> ports = new HashSet<Integer>();
+ for (Connector connector : connectors) {
+ int port = connector.getPort();
+ if (ports.contains(port)) {
+ throw new IllegalStateException(String.format("HTTP, AJP and HTTPS must not use the same port %d", port));
+ }
+ ports.add(port);
+ }
+ }
+
+ @Nullable
+ private static Connector newHttpConnector(Props props) {
+ Connector connector = null;
+ // Not named "sonar.web.http.port" to keep backward-compatibility
+ int port = props.intOf("sonar.web.port", 9000);
+ if (port > DISABLED_PORT) {
+ connector = newConnector(props, HTTP_PROTOCOL, "http");
+ connector.setPort(port);
+ info("HTTP connector is enabled on port " + port);
+ }
+ return connector;
+ }
+
+ @Nullable
+ private static Connector newAjpConnector(Props props) {
+ Connector connector = null;
+ int port = props.intOf("sonar.ajp.port", DISABLED_PORT);
+ if (port > DISABLED_PORT) {
+ connector = newConnector(props, AJP_PROTOCOL, "http");
+ connector.setPort(port);
+ info("AJP connector is enabled on port " + port);
+ }
+ return connector;
+ }
+
+ @Nullable
+ private static Connector newHttpsConnector(Props props) {
+ Connector connector = null;
+ int port = props.intOf("sonar.web.https.port", DISABLED_PORT);
+ if (port > DISABLED_PORT) {
+ connector = newConnector(props, HTTP_PROTOCOL, "https");
+ connector.setPort(port);
+ connector.setSecure(true);
+ connector.setScheme("https");
+ setConnectorAttribute(connector, "keyAlias", props.of("sonar.web.https.keyAlias"));
+ String keyPassword = props.of("sonar.web.https.keyPass", "changeit");
+ setConnectorAttribute(connector, "keyPass", keyPassword);
+ setConnectorAttribute(connector, "keystorePass", props.of("sonar.web.https.keystorePass", keyPassword));
+ setConnectorAttribute(connector, "keystoreFile", props.of("sonar.web.https.keystoreFile"));
+ setConnectorAttribute(connector, "keystoreType", props.of("sonar.web.https.keystoreType", "JKS"));
+ setConnectorAttribute(connector, "keystoreProvider", props.of("sonar.web.https.keystoreProvider"));
+ setConnectorAttribute(connector, "truststorePass", props.of("sonar.web.https.truststorePass", "changeit"));
+ setConnectorAttribute(connector, "truststoreFile", props.of("sonar.web.https.truststoreFile"));
+ setConnectorAttribute(connector, "truststoreType", props.of("sonar.web.https.truststoreType", "JKS"));
+ setConnectorAttribute(connector, "truststoreProvider", props.of("sonar.web.https.truststoreProvider"));
+ setConnectorAttribute(connector, "clientAuth", props.of("sonar.web.https.clientAuth", "false"));
+ setConnectorAttribute(connector, "sslProtocol", "TLS");
+ setConnectorAttribute(connector, "SSLEnabled", true);
+ info("HTTPS connector is enabled on port " + port);
+ }
+ return connector;
+ }
+
+ private static Connector newConnector(Props props, String protocol, String scheme) {
+ Connector connector = new Connector(protocol);
+ connector.setURIEncoding("UTF-8");
+ connector.setProperty("address", props.of("sonar.web.host", "0.0.0.0"));
+ configurePool(props, connector, scheme);
+ configureCompression(connector);
+ return connector;
+ }
+
+ private static void configurePool(Props props, Connector connector, String scheme) {
+ connector.setProperty("acceptorThreadCount", String.valueOf(2));
+ connector.setProperty("minSpareThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".minThreads", 5)));
+ connector.setProperty("maxThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".maxThreads", 50)));
+ connector.setProperty("acceptCount", String.valueOf(props.intOf("sonar.web." + scheme + ".acceptCount", 25)));
+ }
+
+ private static void configureCompression(Connector connector) {
+ connector.setProperty("compression", "on");
+ connector.setProperty("compressionMinSize", "1024");
+ connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,text/css,application/json,application/javascript");
+ }
+
+ private static void setConnectorAttribute(Connector c, String key, @Nullable Object value) {
+ if (value != null) {
+ c.setAttribute(key, value);
+ }
+ }
+
+ private static void info(String message) {
+ LoggerFactory.getLogger(Connectors.class).info(message);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java b/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java
new file mode 100644
index 00000000000..50239fe282d
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java
@@ -0,0 +1,133 @@
+/*
+ * 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.server.app;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.commons.io.FileUtils;
+import org.sonar.process.Props;
+
+import java.io.File;
+
+class EmbeddedTomcat {
+
+ private final Props props;
+ private Tomcat tomcat = null;
+ private Thread hook = null;
+ private boolean stopping = false, ready = false;
+
+ EmbeddedTomcat(Props props) {
+ this.props = props;
+ }
+
+ void start() {
+ if (tomcat != null || hook != null) {
+ throw new IllegalStateException("Server is already started");
+ }
+
+ try {
+ // '%2F' (slash /) and '%5C' (backslash \) are permitted as path delimiters in URLs
+ // See Ruby on Rails url_for
+ System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
+
+ System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
+
+ tomcat = new Tomcat();
+
+ // Initialize directories
+ File tomcatDir = tomcatBasedir();
+ String basedir = tomcatDir.getAbsolutePath();
+ tomcat.setBaseDir(basedir);
+ tomcat.getHost().setAppBase(basedir);
+ tomcat.getHost().setAutoDeploy(false);
+ tomcat.getHost().setCreateDirs(false);
+ tomcat.getHost().setDeployOnStartup(true);
+
+ Logging.configure(tomcat, props);
+ Connectors.configure(tomcat, props);
+ Webapp.configure(tomcat, props);
+ tomcat.start();
+ addShutdownHook();
+ ready = true;
+ tomcat.getServer().await();
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to start web server", e);
+ }
+ // Shutdown command received
+ stop();
+ }
+
+ private File tomcatBasedir() {
+ return new File(props.of("sonar.path.temp"), "tomcat");
+ }
+
+ private void addShutdownHook() {
+ hook = new Thread() {
+ @Override
+ public void run() {
+ EmbeddedTomcat.this.doStop();
+ }
+ };
+ Runtime.getRuntime().addShutdownHook(hook);
+ }
+
+
+ void stop() {
+ removeShutdownHook();
+ doStop();
+ }
+
+ private synchronized void doStop() {
+ try {
+ if (tomcat != null && !stopping) {
+ stopping = true;
+ tomcat.stop();
+ tomcat.destroy();
+ }
+ tomcat = null;
+ stopping = false;
+ ready = false;
+ FileUtils.deleteQuietly(tomcatBasedir());
+
+ } catch (LifecycleException e) {
+ throw new IllegalStateException("Fail to stop web server", e);
+ }
+ }
+
+ private void removeShutdownHook() {
+ if (hook != null && !hook.isAlive()) {
+ Runtime.getRuntime().removeShutdownHook(hook);
+ hook = null;
+ }
+ }
+
+ boolean isReady() {
+ return ready;
+ }
+
+ int port() {
+ Connector[] connectors = tomcat.getService().findConnectors();
+ if (connectors.length > 0) {
+ return connectors[0].getLocalPort();
+ }
+ return -1;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/Logging.java b/server/sonar-server/src/main/java/org/sonar/server/app/Logging.java
new file mode 100644
index 00000000000..0ac684828e5
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/Logging.java
@@ -0,0 +1,116 @@
+/*
+ * 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.server.app;
+
+import ch.qos.logback.access.tomcat.LogbackValve;
+import com.google.common.collect.ImmutableMap;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.startup.Tomcat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.bridge.SLF4JBridgeHandler;
+import org.sonar.core.config.Logback;
+import org.sonar.core.profiling.Profiling;
+import org.sonar.process.Props;
+
+import java.io.File;
+import java.util.Map;
+import java.util.logging.LogManager;
+
+class Logging {
+
+ private static final String CONFIG_LOG_CONSOLE = "sonar.log.console";
+
+ private static final String LOG_COMMON_PREFIX = "%d{yyyy.MM.dd HH:mm:ss} %-5level ";
+ private static final String LOG_COMMON_SUFFIX = "%msg%n";
+
+ private static final String LOG_LOGFILE_SPECIFIC_PART = "[%logger{20}] %X ";
+ private static final String LOG_FULL_SPECIFIC_PART = "%thread ";
+
+ private static final String LOGFILE_STANDARD_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_LOGFILE_SPECIFIC_PART + LOG_COMMON_SUFFIX;
+ private static final String LOGFILE_FULL_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_FULL_SPECIFIC_PART + LOG_LOGFILE_SPECIFIC_PART + LOG_COMMON_SUFFIX;
+
+ private static final String CONSOLE_STANDARD_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_COMMON_SUFFIX;
+ private static final String CONSOLE_FULL_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_FULL_SPECIFIC_PART + LOG_COMMON_SUFFIX;
+
+ static final String ACCESS_RELATIVE_PATH = "WEB-INF/config/logback-access.xml";
+ static final String PROPERTY_ENABLE_ACCESS_LOGS = "sonar.web.accessLogs.enable";
+
+ static void init(Props props) {
+ // Configure java.util.logging, used by Tomcat, in order to forward to slf4j
+ LogManager.getLogManager().reset();
+ SLF4JBridgeHandler.install();
+ configureLogback(props);
+ }
+
+ /**
+ * Configure Logback from classpath, with configuration from sonar.properties
+ */
+ private static void configureLogback(Props props) {
+ String configProfilingLevel = props.of(Profiling.CONFIG_PROFILING_LEVEL, "NONE");
+ Profiling.Level profilingLevel = Profiling.Level.fromConfigString(configProfilingLevel);
+ String consoleEnabled = props.of(CONFIG_LOG_CONSOLE, "false");
+ Map<String, String> variables = ImmutableMap.of(
+ "sonar.path.logs", props.of("sonar.path.logs"),
+ "LOGFILE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? LOGFILE_FULL_LOGGING_FORMAT : LOGFILE_STANDARD_LOGGING_FORMAT,
+ "CONSOLE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? CONSOLE_FULL_LOGGING_FORMAT : CONSOLE_STANDARD_LOGGING_FORMAT,
+ "CONSOLE_ENABLED", consoleEnabled);
+ Logback.configure("/org/sonar/server/platform/logback.xml", variables);
+ }
+
+ static void configure(Tomcat tomcat, Props props) {
+ tomcat.setSilent(false);
+ tomcat.getService().addLifecycleListener(new LifecycleLogger(console()));
+ configureLogbackAccess(tomcat, props);
+ }
+
+ static Logger console() {
+ return LoggerFactory.getLogger("console");
+ }
+
+ private static void configureLogbackAccess(Tomcat tomcat, Props props) {
+ if (props.booleanOf(PROPERTY_ENABLE_ACCESS_LOGS, true)) {
+ LogbackValve valve = new LogbackValve();
+ valve.setQuiet(true);
+ valve.setFilename(new File(props.of("sonar.path.web"), ACCESS_RELATIVE_PATH).getAbsolutePath());
+ tomcat.getHost().getPipeline().addValve(valve);
+ }
+ }
+
+ static class LifecycleLogger implements LifecycleListener {
+ private Logger logger;
+
+ LifecycleLogger(Logger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public void lifecycleEvent(LifecycleEvent event) {
+ if ("after_start".equals(event.getType())) {
+ logger.info("Web server is started");
+
+ } else if ("after_destroy".equals(event.getType())) {
+ logger.info("Web server is stopped");
+ }
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java b/server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java
new file mode 100644
index 00000000000..4f8ac9e5312
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.app;
+
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.JarScannerCallback;
+
+import javax.servlet.ServletContext;
+import java.util.Set;
+
+/**
+ * Disable taglib and web-fragment.xml scanning of Tomcat. Should speed up startup.
+ */
+class NullJarScanner implements JarScanner {
+ @Override
+ public void scan(ServletContext context, ClassLoader classloader, JarScannerCallback callback, Set<String> jarsToSkip) {
+ // doing nothing is fast!
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java b/server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java
new file mode 100644
index 00000000000..130bfa233f1
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java
@@ -0,0 +1,55 @@
+/*
+ * 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.server.app;
+
+import org.sonar.process.ConfigurationUtils;
+import org.sonar.process.Props;
+
+public class ServerProcess extends org.sonar.process.Process {
+
+ private final EmbeddedTomcat tomcat;
+
+ ServerProcess(Props props) {
+ super(props);
+ this.tomcat = new EmbeddedTomcat(props);
+ }
+
+ @Override
+ public void onStart() {
+ tomcat.start();
+ }
+
+ @Override
+ public void onTerminate() {
+ tomcat.stop();
+ }
+
+ @Override
+ public boolean isReady() {
+ return tomcat.isReady();
+ }
+
+ public static void main(String[] args) {
+ Props props = ConfigurationUtils.loadPropsFromCommandLineArgs(args);
+ Logging.init(props);
+ new ServerProcess(props).start();
+ LOGGER.info("ServerProcess is done");
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java b/server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java
new file mode 100644
index 00000000000..1120f10230a
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.app;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Tomcat;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.Props;
+
+import java.util.Map;
+
+class Webapp {
+
+ private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes";
+ private static final String RAILS_ENV = "rails.env";
+ private static final String PROPERTY_CONTEXT = "sonar.web.context";
+
+ static void configure(Tomcat tomcat, Props props) {
+ try {
+ String webDir = props.of("sonar.path.web");
+ StandardContext context = (StandardContext) tomcat.addWebapp(getContextPath(props), webDir);
+ context.setReloadable(false);
+ context.setUseHttpOnly(true);
+ context.setProcessTlds(false);
+ context.setTldValidation(false);
+ context.setTldNamespaceAware(false);
+ context.setXmlValidation(false);
+ context.setXmlNamespaceAware(false);
+ context.setUseNaming(false);
+ context.setDelegate(true);
+ for (Map.Entry<Object, Object> entry : props.cryptedProperties().entrySet()) {
+ String key = entry.getKey().toString();
+ if (key.startsWith("sonar.")) {
+ context.addParameter(key, entry.getValue().toString());
+ }
+ }
+ configureRailsMode(props, context);
+ context.setJarScanner(new NullJarScanner());
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to configure webapp", e);
+ }
+ }
+
+ static String getContextPath(Props props) {
+ String context = props.of(PROPERTY_CONTEXT, "");
+ if ("/".equals(context)) {
+ context = "";
+ } else if (!"".equals(context) && !context.startsWith("/")) {
+ throw new IllegalStateException(String.format("Value of '%s' must start with a forward slash: '%s'", PROPERTY_CONTEXT, context));
+ }
+ return context;
+ }
+
+ static void configureRailsMode(Props props, Context context) {
+ if (props.booleanOf("sonar.rails.dev")) {
+ context.addParameter(RAILS_ENV, "development");
+ context.addParameter(JRUBY_MAX_RUNTIMES, "3");
+ LoggerFactory.getLogger(Webapp.class).warn("\n\n\n------ RAILS DEVELOPMENT MODE IS ENABLED ------\n\n\n");
+ } else {
+ context.addParameter(RAILS_ENV, "production");
+ context.addParameter(JRUBY_MAX_RUNTIMES, "1");
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/app/package-info.java
new file mode 100644
index 00000000000..5cccec43c1b
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/app/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.app;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java b/server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java
index 2ed48e023e9..389258369af 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java
@@ -100,7 +100,7 @@ public class EmbeddedDatabase implements Startable {
}
private File getSonarHomeDataDirectory(Settings settings) {
- File sonarHome = new File(settings.getString(CoreProperties.SONAR_HOME));
+ File sonarHome = new File(settings.getString("sonar.path.home"));
if (!sonarHome.isDirectory()) {
throw new IllegalStateException("SonarQube home directory is not valid");
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java b/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
index 8095d1911b6..41035bacce9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
@@ -24,7 +24,6 @@ import org.apache.commons.io.filefilter.FileFilterUtils;
import org.picocontainer.Startable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.Server;
import org.sonar.api.platform.ServerFileSystem;
@@ -32,7 +31,6 @@ import org.sonar.core.persistence.Database;
import org.sonar.core.persistence.dialect.H2;
import javax.annotation.CheckForNull;
-
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
@@ -56,7 +54,7 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
public DefaultServerFileSystem(Database database, Settings settings, Server server) {
this.database = database;
this.server = server;
- this.homeDir = new File(settings.getString(CoreProperties.SONAR_HOME));
+ this.homeDir = new File(settings.getString("sonar.path.home"));
}
/**
@@ -71,16 +69,10 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
@Override
public void start() {
LOGGER.info("SonarQube home: " + homeDir.getAbsolutePath());
- if (!homeDir.isDirectory() || !homeDir.exists()) {
- throw new IllegalStateException("SonarQube home directory does not exist");
- }
-
try {
if (getDeployDir() == null) {
throw new IllegalArgumentException("Web app directory does not exist: " + getDeployDir());
}
-
- LOGGER.info("Deploy dir: " + getDeployDir().getAbsolutePath());
FileUtils.forceMkdir(getDeployDir());
for (File subDirectory : getDeployDir().listFiles((FileFilter) FileFilterUtils.directoryFileFilter())) {
FileUtils.cleanDirectory(subDirectory);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java b/server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java
index 82e2e659596..0c615c531cb 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java
@@ -19,38 +19,28 @@
*/
package org.sonar.server.platform;
-import com.google.common.collect.ImmutableMap;
import org.slf4j.LoggerFactory;
-import org.sonar.core.config.Logback;
-import org.sonar.core.profiling.Profiling;
+import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
-
-import java.util.Map;
-
-import static org.apache.commons.lang.StringUtils.defaultIfEmpty;
+import java.util.Enumeration;
+import java.util.Properties;
public final class PlatformServletContextListener implements ServletContextListener {
- private static final String CONFIG_LOG_CONSOLE = "sonar.log.console";
-
- private static final String LOG_COMMON_PREFIX = "%d{yyyy.MM.dd HH:mm:ss} %-5level ";
- private static final String LOG_COMMON_SUFFIX = "%msg%n";
-
- private static final String LOG_LOGFILE_SPECIFIC_PART = "[%logger{20}] %X ";
- private static final String LOG_FULL_SPECIFIC_PART = "%thread ";
-
- private static final String LOGFILE_STANDARD_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_LOGFILE_SPECIFIC_PART + LOG_COMMON_SUFFIX;
- private static final String LOGFILE_FULL_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_FULL_SPECIFIC_PART + LOG_LOGFILE_SPECIFIC_PART + LOG_COMMON_SUFFIX;
-
- private static final String CONSOLE_STANDARD_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_COMMON_SUFFIX;
- private static final String CONSOLE_FULL_LOGGING_FORMAT = LOG_COMMON_PREFIX + LOG_FULL_SPECIFIC_PART + LOG_COMMON_SUFFIX;
-
public void contextInitialized(ServletContextEvent event) {
try {
- configureLogback(event);
- Platform.getInstance().init(System.getProperties());
+ Properties props = new Properties();
+ ServletContext context = event.getServletContext();
+ Enumeration<String> paramKeys = context.getInitParameterNames();
+ while (paramKeys.hasMoreElements()) {
+ String key = paramKeys.nextElement();
+ if (key.startsWith("sonar.")) {
+ props.put(key, context.getInitParameter(key));
+ }
+ }
+ Platform.getInstance().init(props);
Platform.getInstance().doStart();
} catch (Throwable t) {
// Tomcat 7 "limitations":
@@ -74,25 +64,4 @@ public final class PlatformServletContextListener implements ServletContextListe
public void contextDestroyed(ServletContextEvent event) {
Platform.getInstance().doStop();
}
-
- /**
- * Configure Logback from classpath, with configuration from sonar.properties
- */
- private void configureLogback(ServletContextEvent event) {
- String configProfilingLevel = defaultIfEmpty(
- event.getServletContext().getInitParameter(Profiling.CONFIG_PROFILING_LEVEL),
- System.getProperty(Profiling.CONFIG_PROFILING_LEVEL));
- Profiling.Level profilingLevel = Profiling.Level.fromConfigString(configProfilingLevel);
- String consoleEnabled = defaultIfEmpty(defaultIfEmpty(
- event.getServletContext().getInitParameter(CONFIG_LOG_CONSOLE),
- System.getProperty(CONFIG_LOG_CONSOLE)),
- // Line below used in last resort
- "false");
- Map<String, String> variables = ImmutableMap.of(
- "RAILS_LOGGER_LEVEL", profilingLevel == Profiling.Level.FULL ? "DEBUG" : "WARN",
- "LOGFILE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? LOGFILE_FULL_LOGGING_FORMAT : LOGFILE_STANDARD_LOGGING_FORMAT,
- "CONSOLE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? CONSOLE_FULL_LOGGING_FORMAT : CONSOLE_STANDARD_LOGGING_FORMAT,
- "CONSOLE_ENABLED", consoleEnabled);
- Logback.configure("/org/sonar/server/platform/logback.xml", variables);
- }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
index bcff83ded1f..f1f4a4000f2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
@@ -77,7 +77,7 @@ public final class ServerImpl extends Server implements Startable {
// Remove trailing slashes
.replaceFirst("(\\/+)$", "");
- sonarHome = new File(settings.getString(CoreProperties.SONAR_HOME));
+ sonarHome = new File(settings.getString("sonar.path.home"));
if (!sonarHome.isDirectory()) {
throw new IllegalStateException("SonarQube home directory is not valid");
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/ESNode.java b/server/sonar-server/src/main/java/org/sonar/server/search/ESNode.java
index 02e8490432d..4a3c1e2749f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/search/ESNode.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/search/ESNode.java
@@ -22,6 +22,7 @@ package org.sonar.server.search;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
@@ -36,7 +37,6 @@ import org.picocontainer.Startable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.config.Settings;
-import org.sonar.api.platform.ServerFileSystem;
import org.sonar.server.search.es.ListUpdate;
import org.sonar.server.search.es.ListUpdate.UpdateListScriptFactory;
@@ -48,13 +48,9 @@ import java.io.File;
public class ESNode implements Startable {
private static final Logger LOG = LoggerFactory.getLogger(ESNode.class);
-
private static final String HTTP_ENABLED = "http.enabled";
- static final String DATA_DIR = "data/es";
-
private static final String DEFAULT_HEALTH_TIMEOUT = "30s";
- private final ServerFileSystem fileSystem;
private final Settings settings;
private final String healthTimeout;
@@ -62,13 +58,12 @@ public class ESNode implements Startable {
private Client client;
private Node node;
- public ESNode(ServerFileSystem fileSystem, Settings settings) {
- this(fileSystem, settings, DEFAULT_HEALTH_TIMEOUT);
+ public ESNode(Settings settings) {
+ this(settings, DEFAULT_HEALTH_TIMEOUT);
}
@VisibleForTesting
- ESNode(ServerFileSystem fileSystem, Settings settings, String healthTimeout) {
- this.fileSystem = fileSystem;
+ ESNode(Settings settings, String healthTimeout) {
this.settings = settings;
this.healthTimeout = healthTimeout;
}
@@ -77,9 +72,11 @@ public class ESNode implements Startable {
public void start() {
initLogging();
- IndexProperties.ES_TYPE type = settings.hasKey(IndexProperties.TYPE) ?
- IndexProperties.ES_TYPE.valueOf(settings.getString(IndexProperties.TYPE)) :
- IndexProperties.ES_TYPE.DATA;
+ String typeValue = settings.getString(IndexProperties.TYPE);
+ IndexProperties.ES_TYPE type =
+ typeValue != null ?
+ IndexProperties.ES_TYPE.valueOf(typeValue) :
+ IndexProperties.ES_TYPE.DATA;
ImmutableSettings.Builder esSettings = ImmutableSettings.settingsBuilder()
.put("index.merge.policy.max_merge_at_once", "200")
@@ -89,31 +86,16 @@ public class ESNode implements Startable {
.put("indices.store.throttle.max_bytes_per_sec", "200mb")
.put("script.default_lang", "native")
- .put("script.native." + ListUpdate.NAME + ".type", UpdateListScriptFactory.class.getName());
+ .put("script.native." + ListUpdate.NAME + ".type", UpdateListScriptFactory.class.getName())
- initAnalysis(esSettings);
+ .put("cluster.name", StringUtils.defaultIfBlank(settings.getString(IndexProperties.CLUSTER_NAME), "sonarqube"));
+ initAnalysis(esSettings);
if (IndexProperties.ES_TYPE.TRANSPORT.equals(type)) {
- client = new TransportClient(esSettings)
- .addTransportAddress(new InetSocketTransportAddress("localhost",
- settings.getInt(IndexProperties.NODE_PORT)));
+ initRemoteClient(esSettings);
} else {
- if (IndexProperties.ES_TYPE.MEMORY.equals(type)) {
- initMemoryES(esSettings);
- } else if (IndexProperties.ES_TYPE.DATA.equals(type)) {
- initDataES(esSettings);
- }
- initDirs(esSettings);
- initRestConsole(esSettings);
- initNetwork(esSettings);
-
- node = NodeBuilder.nodeBuilder()
- .settings(esSettings)
- .node();
- node.start();
-
- client = node.client();
+ initLocalClient(type, esSettings);
}
if (client.admin().cluster().prepareHealth()
@@ -121,21 +103,46 @@ public class ESNode implements Startable {
.setTimeout(healthTimeout)
.get()
.getStatus() == ClusterHealthStatus.RED) {
- throw new IllegalStateException(
- String.format("Elasticsearch index is corrupt, please delete directory '%s/%s' and relaunch the SonarQube server.", fileSystem.getHomeDir().getAbsolutePath(), DATA_DIR));
+ throw new IllegalStateException(String.format("Elasticsearch index is corrupt, please delete directory '%s' " +
+ "and relaunch the SonarQube server.", esDataDir()));
}
addIndexTemplates();
LOG.info("Elasticsearch started");
+ }
+
+ private void initRemoteClient(ImmutableSettings.Builder esSettings) {
+ int port = settings.getInt(IndexProperties.NODE_PORT);
+ client = new TransportClient(esSettings)
+ .addTransportAddress(new InetSocketTransportAddress("localhost",
+ port));
+ LOG.info("Elasticsearch port: " + port);
+ }
+ private void initLocalClient(IndexProperties.ES_TYPE type, ImmutableSettings.Builder esSettings) {
+ if (IndexProperties.ES_TYPE.MEMORY.equals(type)) {
+ initMemoryES(esSettings);
+ } else if (IndexProperties.ES_TYPE.DATA.equals(type)) {
+ initDataES(esSettings);
+ }
+ initDirs(esSettings);
+ initRestConsole(esSettings);
+ initNetwork(esSettings);
+
+ node = NodeBuilder.nodeBuilder()
+ .settings(esSettings)
+ .node();
+ node.start();
+
+ client = node.client();
}
private void initMemoryES(ImmutableSettings.Builder builder) {
builder
- .put("node.name", "node-test-" + System.currentTimeMillis())
+ .put("node.name", "node-mem-" + System.currentTimeMillis())
.put("node.data", true)
- .put("cluster.name", "cluster-test-" + NetworkUtils.getLocalAddress().getHostName())
+ .put("cluster.name", "cluster-mem-" + NetworkUtils.getLocalAddress().getHostName())
.put("index.store.type", "memory")
.put("index.store.fs.memory.enabled", "true")
.put("gateway.type", "none")
@@ -145,10 +152,6 @@ public class ESNode implements Startable {
.put("node.local", true);
}
- private void initTransportES(ImmutableSettings.Builder builder) {
- throw new IllegalStateException("Not implemented yet");
- }
-
private void initDataES(ImmutableSettings.Builder builder) {
builder
.put("node.name", "sonarqube-" + System.currentTimeMillis())
@@ -243,16 +246,20 @@ public class ESNode implements Startable {
}
private void initDirs(ImmutableSettings.Builder esSettings) {
- File esDir = new File(fileSystem.getHomeDir(), DATA_DIR);
+ File esDir = esDataDir();
try {
FileUtils.forceMkdir(esDir);
- esSettings.put("path.home", esDir.getAbsolutePath());
+ esSettings.put("path.data", esDir.getAbsolutePath());
LOG.debug("Elasticsearch data stored in {}", esDir.getAbsolutePath());
} catch (Exception e) {
- throw new IllegalStateException("Fail to create directory " + esDir.getAbsolutePath(), e);
+ throw new IllegalStateException("Fail to create directory " + esDir, e);
}
}
+ private File esDataDir() {
+ return new File(settings.getString("sonar.path.data"), "es");
+ }
+
@Override
public void stop() {
if (client != null) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexProperties.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexProperties.java
index dd6633d5708..996c8ec477c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexProperties.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexProperties.java
@@ -29,7 +29,9 @@ public final class IndexProperties {
MEMORY, TRANSPORT, DATA
}
-public static final String TYPE = "sonar.es.type";
-public static final String HTTP_PORT = "sonar.es.http.port";
-public static final String NODE_PORT = "sonar.es.node.port";
+ public static final String TYPE = "sonar.es.type";
+ public static final String HTTP_PORT = "sonar.es.http.port";
+ public static final String NODE_PORT = "sonar.es.port";
+ public static final String CLUSTER_NAME = "sonar.es.cluster.name";
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
index 4c93d8a66cb..5e7613339d1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
@@ -369,7 +369,7 @@ public final class JRubyFacade {
}
public String getServerHome() {
- return get(Settings.class).getString(CoreProperties.SONAR_HOME);
+ return get(Settings.class).getString("sonar.path.home");
}
public ComponentContainer getContainer() {
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml b/server/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml
index a4a640e59b1..fe7c457ccda 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml
+++ b/server/sonar-server/src/main/resources/org/sonar/server/platform/logback.xml
@@ -10,9 +10,9 @@
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
<appender name="LOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
- <File>${SONAR_HOME}/logs/sonar.log</File>
+ <File>${sonar.path.logs}/sonar.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
- <param name="FileNamePattern" value="${SONAR_HOME}/logs/sonar.%i.log"/>
+ <param name="FileNamePattern" value="${sonar.path.logs}/sonar.%i.log"/>
<param name="MinIndex" value="1"/>
<param name="MaxIndex" value="3"/>
</rollingPolicy>
@@ -28,6 +28,9 @@
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>warn</level>
+ </filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
${CONSOLE_LOGGING_FORMAT}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/EmbeddedDatabaseTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/EmbeddedDatabaseTest.java
index f1550b502b2..8ad2fa58bd5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/db/EmbeddedDatabaseTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/EmbeddedDatabaseTest.java
@@ -81,7 +81,7 @@ public class EmbeddedDatabaseTest {
@Test
public void should_return_sonar_home_directory() throws Exception {
Settings settings = testSettings(0);
- settings.setProperty(CoreProperties.SONAR_HOME, ".");
+ settings.setProperty("sonar.path.home", ".");
settings.setProperty(DatabaseProperties.PROP_EMBEDDED_DATA_DIR, "");
EmbeddedDatabase database = new EmbeddedDatabase(settings);
@@ -98,7 +98,7 @@ public class EmbeddedDatabaseTest {
String testPath = getClass().getResource(".").getPath();
Settings settings = testSettings(0);
- settings.setProperty(CoreProperties.SONAR_HOME, testPath + "/unmatched_directory");
+ settings.setProperty("sonar.path.home", testPath + "/unmatched_directory");
settings.setProperty(DatabaseProperties.PROP_EMBEDDED_DATA_DIR, "");
EmbeddedDatabase database = new EmbeddedDatabase(settings);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java
index 4f86c1d81ae..a12293fdafc 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/platform/DefaultServerFileSystemTest.java
@@ -97,11 +97,4 @@ public class DefaultServerFileSystemTest {
List<File> jars = fs.getExtensions("checkstyle");
assertThat(jars).isEmpty();
}
-
- @Test(expected = IllegalStateException.class)
- public void fail_if_home_directory_not_exists() {
- DefaultServerFileSystem fs = new DefaultServerFileSystem(null, new File("/notexists"), null);
- fs.start();
- }
-
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java
index d876ec172f3..c81d952bbb3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java
@@ -47,7 +47,7 @@ public class ServerImplTest {
@Before
public void setUp() throws Exception {
- settings = new Settings().setProperty(CoreProperties.SONAR_HOME, sonarHome.getRoot().getAbsolutePath());
+ settings = new Settings().setProperty("sonar.path.home", sonarHome.getRoot().getAbsolutePath());
new File(sonarHome.getRoot(), "web/deploy").mkdirs();
server = new ServerImpl(settings, "/org/sonar/server/platform/ServerImplTest/build.properties", "/org/sonar/server/platform/ServerImplTest/version.txt");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java
index 4db7d12f277..7a1ef3335a1 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java
@@ -28,7 +28,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.config.Settings;
-import org.sonar.api.platform.ServerFileSystem;
import org.sonar.core.cluster.NullQueue;
import org.sonar.core.profiling.Profiling;
@@ -39,8 +38,6 @@ import java.util.Collections;
import java.util.Map;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class BaseIndexTest {
@@ -51,10 +48,10 @@ public class BaseIndexTest {
@Before
public void setup() throws IOException {
- File homeDir = temp.newFolder();
- ServerFileSystem fs = mock(ServerFileSystem.class);
- when(fs.getHomeDir()).thenReturn(homeDir);
- node = new ESNode(fs, new Settings());
+ File dataDir = temp.newFolder();
+ Settings settings = new Settings();
+ settings.setProperty("sonar.path.data", dataDir.getAbsolutePath());
+ node = new ESNode(settings);
node.start();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/ESNodeTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/ESNodeTest.java
index a1d0ce18241..a7a3f91908f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/search/ESNodeTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/search/ESNodeTest.java
@@ -20,7 +20,6 @@
package org.sonar.server.search;
import com.google.common.io.Resources;
-import org.apache.commons.io.FileUtils;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.client.AdminClient;
@@ -29,14 +28,12 @@ import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.mapper.StrictDynamicMappingException;
-import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.config.Settings;
-import org.sonar.api.platform.ServerFileSystem;
import org.sonar.api.utils.ZipUtils;
import java.io.File;
@@ -44,29 +41,20 @@ import java.io.IOException;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class ESNodeTest {
- ServerFileSystem fs;
- File homedir;
File dataDir;
+ Settings settings;
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Before
public void createMocks() throws IOException {
- homedir = temp.newFolder();
- fs = mock(ServerFileSystem.class);
- when(fs.getHomeDir()).thenReturn(homedir);
- dataDir = new File(homedir, ESNode.DATA_DIR);
- }
-
- @After
- public void cleanUp() {
- FileUtils.deleteQuietly(homedir);
+ dataDir = temp.newFolder();
+ settings = new Settings();
+ settings.setProperty("sonar.path.data", dataDir.getAbsolutePath());
}
@Test
@@ -74,7 +62,7 @@ public class ESNodeTest {
public void start_and_stop_es_node() throws Exception {
assertThat(dataDir).doesNotExist();
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
node.start();
ClusterAdminClient cluster = node.client().admin().cluster();
@@ -94,7 +82,7 @@ public class ESNodeTest {
@Test(expected = StrictDynamicMappingException.class)
public void should_use_default_settings_for_index() throws Exception {
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
node.start();
node.client().admin().indices().prepareCreate("strict")
@@ -114,7 +102,7 @@ public class ESNodeTest {
@Test
public void check_path_analyzer() throws Exception {
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
node.start();
node.client().admin().indices().prepareCreate("path")
@@ -136,13 +124,8 @@ public class ESNodeTest {
}
@Test
- public void check_word_analyzer() throws Exception {
-
- }
-
- @Test
public void check_sortable_analyzer() throws Exception {
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
node.start();
node.client().admin().indices().prepareCreate("sort")
@@ -160,7 +143,7 @@ public class ESNodeTest {
@Test
public void check_gram_analyzer() throws Exception {
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
node.start();
node.client().admin().indices().prepareCreate("gram")
@@ -181,9 +164,9 @@ public class ESNodeTest {
@Test
public void should_restore_status_on_startup() throws Exception {
File zip = new File(Resources.getResource(getClass(), "ESNodeTest/data-es-clean.zip").toURI());
- ZipUtils.unzip(zip, dataDir);
+ ZipUtils.unzip(zip, new File(dataDir, "es"));
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
node.start();
AdminClient admin = node.client().admin();
@@ -194,12 +177,11 @@ public class ESNodeTest {
}
@Test(expected = IllegalStateException.class)
- @Ignore("Need to update this test for remote ES.")
public void should_fail_on_corrupt_index() throws Exception {
File zip = new File(Resources.getResource(getClass(), "ESNodeTest/data-es-corrupt.zip").toURI());
- ZipUtils.unzip(zip, dataDir);
+ ZipUtils.unzip(zip, new File(dataDir, "es"));
- ESNode node = new ESNode(fs, new Settings(), "5s");
+ ESNode node = new ESNode(settings, "5s");
try {
node.start();
} finally {
@@ -209,7 +191,7 @@ public class ESNodeTest {
@Test
public void should_fail_to_get_client_if_not_started() {
- ESNode node = new ESNode(fs, new Settings());
+ ESNode node = new ESNode(settings);
try {
node.client();
fail();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java b/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
index 8c7b6812118..b1bef3250f1 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
@@ -76,7 +76,7 @@ public class ServerTester extends ExternalResource {
Properties properties = new Properties();
properties.putAll(initialProps);
properties.setProperty(IndexProperties.TYPE, IndexProperties.ES_TYPE.MEMORY.name());
- properties.setProperty(CoreProperties.SONAR_HOME, homeDir.getAbsolutePath());
+ properties.setProperty("sonar.path.home", homeDir.getAbsolutePath());
properties.setProperty(DatabaseProperties.PROP_URL, "jdbc:h2:" + homeDir.getAbsolutePath() + "/h2");
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
String key = entry.getKey().toString();
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-clean.zip b/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-clean.zip
index b6d286012c9..66aaf5ec124 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-clean.zip
+++ b/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-clean.zip
Binary files differ
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-corrupt.zip b/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-corrupt.zip
index 9f59a91cd37..37abade42ec 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-corrupt.zip
+++ b/server/sonar-server/src/test/resources/org/sonar/server/search/ESNodeTest/data-es-corrupt.zip
Binary files differ
diff --git a/server/sonar-web/pom.xml b/server/sonar-web/pom.xml
index e61cb39ede6..a90aad2498f 100644
--- a/server/sonar-web/pom.xml
+++ b/server/sonar-web/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>
<groupId>org.codehaus.sonar</groupId>
@@ -362,12 +363,6 @@
<artifactId>h2</artifactId>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-server</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
<!-- core plugins -->
<dependency>
<groupId>org.codehaus.sonar.plugins</groupId>
diff --git a/server/sonar-web/src/main/webapp/META-INF/context.xml b/server/sonar-web/src/main/webapp/META-INF/context.xml
deleted file mode 100644
index db181e0854e..00000000000
--- a/server/sonar-web/src/main/webapp/META-INF/context.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<Context useHttpOnly="true" reloadable="false" processTlds="false"
- useNaming="false" xmlValidation="false" xmlNamespaceAware="false" tldValidation="false"
- tldNamespaceAware="false">
-</Context>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/config/logback-access.xml b/server/sonar-web/src/main/webapp/WEB-INF/config/logback-access.xml
index e516fcfbbea..504f5fc6797 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/config/logback-access.xml
+++ b/server/sonar-web/src/main/webapp/WEB-INF/config/logback-access.xml
@@ -14,9 +14,9 @@
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
- <file>${SONAR_HOME}/logs/access.log</file>
+ <file>${sonar.path.logs}/access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
- <param name="FileNamePattern" value="${SONAR_HOME}/logs/access.%i.log"/>
+ <param name="FileNamePattern" value="${sonar.path.logs}/access.%i.log"/>
<param name="MinIndex" value="1"/>
<param name="MaxIndex" value="3"/>
</rollingPolicy>