From 1fd258f12f256aea2359d3469f9e6308608485f2 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 22 Jul 2014 17:59:49 +0200 Subject: [PATCH] SONAR-4898 some refactoring --- server/sonar-process/pom.xml | 9 +- .../java/org/sonar/process/AesCipher.java | 12 +- .../java/org/sonar/process/Encryption.java | 10 +- .../main/java/org/sonar/process/Process.java | 29 +- .../org/sonar/process/ProcessWrapper.java | 320 +++++++++--------- .../main/java/org/sonar/process/Props.java | 18 +- .../java/org/sonar/process/ProcessTest.java | 40 +-- .../java/org/sonar/search/ElasticSearch.java | 2 - .../org/sonar/search/ElasticSearchTest.java | 2 +- .../src/main/assembly/conf/sonar.properties | 18 +- .../src/main/assembly/conf/wrapper.conf | 30 +- .../java/org/sonar/application/AesCipher.java | 138 -------- .../org/sonar/application/Base64Cipher.java | 35 -- .../java/org/sonar/application/Cipher.java | 26 -- .../org/sonar/application/Encryption.java | 66 ---- .../main/java/org/sonar/application/Env.java | 75 ---- .../java/org/sonar/application/Props.java | 117 ------- .../{ForkProcesses.java => StartServer.java} | 46 +-- .../org/sonar/application/AesCipherTest.java | 186 ---------- .../org/sonar/application/EncryptionTest.java | 59 ---- .../java/org/sonar/application/EnvTest.java | 99 ------ .../java/org/sonar/application/PropsTest.java | 135 -------- 22 files changed, 236 insertions(+), 1236 deletions(-) delete mode 100644 sonar-application/src/main/java/org/sonar/application/AesCipher.java delete mode 100644 sonar-application/src/main/java/org/sonar/application/Base64Cipher.java delete mode 100644 sonar-application/src/main/java/org/sonar/application/Cipher.java delete mode 100644 sonar-application/src/main/java/org/sonar/application/Encryption.java delete mode 100644 sonar-application/src/main/java/org/sonar/application/Env.java delete mode 100644 sonar-application/src/main/java/org/sonar/application/Props.java rename sonar-application/src/main/java/org/sonar/application/{ForkProcesses.java => StartServer.java} (65%) delete mode 100644 sonar-application/src/test/java/org/sonar/application/AesCipherTest.java delete mode 100644 sonar-application/src/test/java/org/sonar/application/EncryptionTest.java delete mode 100644 sonar-application/src/test/java/org/sonar/application/EnvTest.java delete mode 100644 sonar-application/src/test/java/org/sonar/application/PropsTest.java 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 @@ -37,10 +37,6 @@ commons-lang commons-lang - - com.google.guava - guava - com.google.code.findbugs jsr305 @@ -67,5 +63,10 @@ hamcrest-all test + + com.google.guava + guava + test + 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/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 ciphers; + private final Map ciphers = new HashMap(); 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/Process.java b/server/sonar-process/src/main/java/org/sonar/process/Process.java index d003bf153b9..b3824c79606 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; @@ -95,24 +94,20 @@ public abstract class Process implements ProcessMXBean { } catch (IOException e) { throw new IllegalStateException("Could not read properties from file '" + args[0] + "'", e); } - props = Props.create(properties); + props = new Props(properties); init(); } - @VisibleForTesting public 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); @@ -194,24 +189,4 @@ public abstract class Process implements ProcessMXBean { 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); - } - } -} \ No newline at end of file +} 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 d2640f827b7..d08163d56fd 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,12 @@ */ 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,89 +40,122 @@ 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 properties; - - final java.lang.Process process; + private String processName, className; + private int jmxPort = -1; + private final List javaOpts = new ArrayList(); + private final List classpath = new ArrayList(); + private final Map envProperties = new HashMap(); + private final Map arguments = new HashMap(); + private File workDir; + private File propertiesFile; + private java.lang.Process process; private volatile Thread processThread; - private StreamGobbler errorGobbler; private StreamGobbler outputGobbler; + private ProcessMXBean processMXBean; - final ProcessMXBean processMXBean; + public ProcessWrapper(String processName) { + this.processThread = this; + this.processName = processName; + } - public ProcessWrapper(String workDir, String className, Map 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 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 setArgument(String key, String value) { + arguments.put(key, value); + return this; + } - processMXBean = waitForJMX(name, port); + public ProcessWrapper setArguments(Map args) { + arguments.clear(); + arguments.putAll(args); + return this; } - public ProcessMXBean getProcessMXBean() { - return processMXBean; + public ProcessWrapper setJavaOpts(List opts) { + for (String command : opts) { + addJavaOpts(command); + } + return this; } - private ProcessMXBean waitForJMX(String name, Integer port) { + public ProcessWrapper addJavaOpts(String s) { + Collections.addAll(javaOpts, s.split(" ")); + 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().getHostAddress() + ":" + port + "/jmxrmi"; - JMXServiceURL jmxUrl = new JMXServiceURL(protocol, InetAddress.getLocalHost().getHostAddress(), 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 setClasspath(List l) { + classpath.addAll(l); + return this; } - public boolean isReady() { - return processMXBean != null && processMXBean.isReady(); + public ProcessWrapper addClasspath(String s) { + classpath.add(s); + return this; } + public ProcessWrapper setJmxPort(int i) { + this.jmxPort = i; + return this; + } + + public ProcessWrapper setWorkDir(File d) { + this.workDir = d; + return this; + } + + public void execute() { + List command = new ArrayList(); + 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); + + try { + LOGGER.debug("ProcessWrapper::executeProcess() -- Starting process with command '{}'", StringUtils.join(command, " ")); + process = processBuilder.start(); + LOGGER.debug("ProcessWrapper::executeProcess() -- Process started: {}", process.toString()); + errorGobbler = new StreamGobbler(process.getErrorStream(), this.getName() + "-ERROR"); + outputGobbler = new StreamGobbler(process.getInputStream(), this.getName()); + outputGobbler.start(); + errorGobbler.start(); + processMXBean = waitForJMX(); + + } catch (IOException e) { + throw new IllegalStateException("Fail to start process: " + StringUtils.join(command, " "), e); + } + } + + @Override public void run() { LOGGER.trace("ProcessWrapper::run() START"); try { @@ -135,131 +166,114 @@ public class ProcessWrapper extends Thread { waitUntilFinish(outputGobbler); waitUntilFinish(errorGobbler); closeStreams(process); + ProcessWrapper.this.processThread = null; } - ProcessWrapper.this.processThread = null; LOGGER.trace("ProcessWrapper::run() END"); } - private String getJavaCommand() { - String separator = System.getProperty("file.separator"); - return System.getProperty("java.home") - + separator + "bin" + separator + "java"; + public boolean isReady() { + return processMXBean != null && processMXBean.isReady(); } - private List getJMXOptions() { - return ImmutableList.of( - "-Dcom.sun.management.jmxremote", - "-Dcom.sun.management.jmxremote.port=" + this.port, - "-Dcom.sun.management.jmxremote.authenticate=false", - "-Dcom.sun.management.jmxremote.ssl=false"); + public ProcessMXBean getProcessMXBean() { + return processMXBean; } - private List getClassPath() { - // java specification : "multiple path entries are separated by semi-colons", not by - // system file separator, - String separator = System.getProperty("file.separator"); - - return ImmutableList.of("-cp", StringUtils.join(classPath, ":")); + public Object getThread() { + return this.processThread; } - private String getPropertyFile() { - File propertyFile = new File(FileUtils.getTempDirectory(), UUID.randomUUID().toString()); - try { - Properties props = new Properties(); - for (Map.Entry property : properties.entrySet()) { - props.put(property.getKey(), property.getValue()); + private void waitUntilFinish(@Nullable Thread thread) { + if (thread != null) { + try { + thread.join(); + } catch (InterruptedException e) { + LOGGER.error("InterruptedException while waiting finish of " + thread.toString(), e); } - 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.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.environment().put("SONAR_HOME", workDir); - 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)); + private void closeStreams(@Nullable java.lang.Process process) { + if (process != null) { + IOUtils.closeQuietly(process.getInputStream()); + IOUtils.closeQuietly(process.getOutputStream()); + IOUtils.closeQuietly(process.getErrorStream()); } + } - 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; - } catch (IOException e) { - throw new IllegalStateException("Io Exception in ProcessWrapper", e); - } + private String buildJavaCommand() { + String separator = System.getProperty("file.separator"); + return System.getProperty("java.home") + + separator + "bin" + separator + "java"; + } + private List buildJMXOptions() { + if (jmxPort < 1) { + throw new IllegalStateException("JMX port is not set"); + } + return Arrays.asList( + "-Dcom.sun.management.jmxremote", + "-Dcom.sun.management.jmxremote.port=" + jmxPort, + "-Dcom.sun.management.jmxremote.authenticate=false", + "-Dcom.sun.management.jmxremote.ssl=false"); } - @Override - public String toString() { - return ReflectionToStringBuilder.toString(this); + private List buildClasspath() { + return Arrays.asList("-cp", StringUtils.join(classpath, ";")); } - private void closeStreams(java.lang.Process process) { - if (process != null) { - Closeables.closeQuietly(process.getInputStream()); - Closeables.closeQuietly(process.getOutputStream()); - Closeables.closeQuietly(process.getErrorStream()); + private File buildPropertiesFile() { + try { + propertiesFile = File.createTempFile("sq-conf", "properties"); + Properties props = new Properties(); + props.putAll(arguments); + 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 propertiesFile; + } catch (IOException e) { + throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e); } } - 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(); + if (processMXBean != null) { + processMXBean.terminate(); waitUntilFinish(this); } } - public Object getThread() { - return this.processThread; - } - private static class StreamGobbler extends Thread { private final InputStream is; private volatile Exception exception; @@ -284,8 +298,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 cd752403a86..506f6b927f1 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 @@ -27,7 +27,7 @@ public class Props { private final Properties props; - Props(Properties props) { + public Props(Properties props) { this.props = props; } @@ -71,22 +71,6 @@ public class Props { return props; } - 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); - } static Properties decrypt(Properties properties) { Encryption encryption = new Encryption(properties.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH)); 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/src/main/java/org/sonar/search/ElasticSearch.java b/server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java index 34ef016fe96..28a60a430f9 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,7 +19,6 @@ */ 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; @@ -46,7 +45,6 @@ public class ElasticSearch extends Process { super(args); } - @VisibleForTesting public ElasticSearch(Props props) { super(props); } 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 d06de0fac40..a53268205bd 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 @@ -87,7 +87,7 @@ public class ElasticSearchTest { properties.setProperty("sonar.path.data", tempDirectory.getAbsolutePath()); properties.setProperty(ElasticSearch.ES_PORT_PROPERTY, Integer.toString(freeESPort)); - elasticSearch = new ElasticSearch(Props.create(properties)); + elasticSearch = new ElasticSearch(new Props(properties)); new Thread(new Runnable() { @Override public void run() { diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index b8b255f94bb..e2ab2aad13a 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -9,9 +9,21 @@ # See also the file conf/wrapper.conf for JVM advanced settings #-------------------------------------------------------------------------------------------------- -# ELASTICSEARCH -sonar.es.javaOpts=-Xmx256m -Xms256m -sonar.web.java_opts=-Xmx76m +# TO BE DOCUMENTED - WORK IN PROGRESS +#sonar.es.javaOpts=-server -Xmx256m -Xms256m -Xss256k -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly +#sonar.es.port=0 +#sonar.es.jmxPort=0 + +# For debug only +#sonar.es.httpPort= + +#sonar.web.javaOpts=-server -Xmx768m -Djava.awt.headless=true -XX:MaxPermSize=160m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Djruby.management.enabled=false +#sonar.web.jmxPort=0 + +# Paths are absolute or relative to installation root directory +#sonar.path.data=data +#sonar.path.logs=logs +#sonar.path.temp=temp #-------------------------------------------------------------------------------------------------- diff --git a/sonar-application/src/main/assembly/conf/wrapper.conf b/sonar-application/src/main/assembly/conf/wrapper.conf index 751038ab671..25f3fcd6d2a 100644 --- a/sonar-application/src/main/assembly/conf/wrapper.conf +++ b/sonar-application/src/main/assembly/conf/wrapper.conf @@ -1,16 +1,5 @@ # Java Additional Parameters -wrapper.java.additional.1=-Djava.awt.headless=true -#wrapper.java.additional.2=-XX:MaxPermSize=160m -wrapper.java.additional.3=-XX:+HeapDumpOnOutOfMemoryError -wrapper.java.additional.4=-Dfile.encoding=UTF-8 -wrapper.java.additional.5=-Djruby.management.enabled=false - -# Maximum amount of memory of Java VM -wrapper.java.additional.2=-Xmx32M - -# RECOMMENDED : uncomment if Java Virtual Machine is a JDK but not a JRE. To know which JVM you use, execute -# 'java -version'. JDK displays 'Server VM'. -#wrapper.java.additional.7=-server +wrapper.java.additional.1=-Xmx32M # Initial JVM heap size (in MB) wrapper.java.initmemory=16 @@ -39,25 +28,12 @@ wrapper.java.classpath.2=../../lib/*.jar wrapper.java.library.path.1=./lib # Application parameters. Add parameters as needed starting from 1 -wrapper.app.parameter.1=org.sonar.application.ForkProcesses +wrapper.app.parameter.1=org.sonar.application.StartServer # Do not touch the following property. Max memory is set with -Xmx (see above). # See https://jira.codehaus.org/browse/SONAR-5204 wrapper.java.maxmemory=0 -#******************************************************************** -# Profiling and debbuging - for development only -# If wrapper.java.additional.7=-server is not commented, parameter ids should start from 8 instead of 7. -#******************************************************************** -# Java remote debugging -#wrapper.java.additional.7=-agentlib:jdwp=transport=dt_socket,server=y,address=8000 - -# JMX remote monitoring on Sun JVM (warning, security is disabled) -#wrapper.java.additional.7=-Dcom.sun.management.jmxremote -#wrapper.java.additional.8=-Dcom.sun.management.jmxremote.port=9005 -#wrapper.java.additional.9=-Dcom.sun.management.jmxremote.authenticate=false -#wrapper.java.additional.10=-Dcom.sun.management.jmxremote.ssl=false - #******************************************************************** # Wrapper Logging Properties #******************************************************************** @@ -126,4 +102,4 @@ wrapper.ntservice.interactive=false #******************************************************************** # restart the process if CPU is heavily loaded during 240 seconds. -wrapper.ping.timeout=240 +wrapper.ping.timeout=0 diff --git a/sonar-application/src/main/java/org/sonar/application/AesCipher.java b/sonar-application/src/main/java/org/sonar/application/AesCipher.java deleted file mode 100644 index e778b0ebc18..00000000000 --- a/sonar-application/src/main/java/org/sonar/application/AesCipher.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.application; - -import 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; - -import javax.annotation.Nullable; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import java.io.File; -import java.io.IOException; -import java.security.Key; -import java.security.SecureRandom; - -final class AesCipher extends Cipher { - - // Can't be increased because of Java 6 policy files : - // https://confluence.terena.org/display/~visser/No+256+bit+ciphers+for+Java+apps - // http://java.sun.com/javase/6/webnotes/install/jre/README - public static final int KEY_SIZE_IN_BITS = 128; - - private static final String CRYPTO_KEY = "AES"; - - /** - * Duplication from CoreProperties.ENCRYPTION_SECRET_KEY_PATH - */ - static final String ENCRYPTION_SECRET_KEY_PATH = "sonar.secretKeyPath"; - - private String pathToSecretKey; - - AesCipher(@Nullable String pathToSecretKey) { - this.pathToSecretKey = pathToSecretKey; - } - - @Override - String encrypt(String clearText) { - try { - 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 (Exception e) { - throw Throwables.propagate(e); - } - } - - @Override - String decrypt(String encryptedText) { - try { - javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY); - cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile()); - byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText))); - return new String(cipherData); - } catch (Exception e) { - throw Throwables.propagate(e); - } - } - - /** - * This method checks the existence of the file, but not the validity of the contained key. - */ - boolean hasSecretKey() { - String path = getPathToSecretKey(); - if (StringUtils.isNotBlank(path)) { - File file = new File(path); - return file.exists() && file.isFile(); - } - return false; - } - - private Key loadSecretFile() throws IOException { - String path = getPathToSecretKey(); - 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); - } - File file = new File(path); - if (!file.exists() || !file.isFile()) { - throw new IllegalStateException("The property " + ENCRYPTION_SECRET_KEY_PATH + " does not link to a valid file: " + path); - } - String s = FileUtils.readFileToString(file); - if (StringUtils.isBlank(s)) { - throw new IllegalStateException("No secret key in the file: " + path); - } - return new SecretKeySpec(Base64.decodeBase64(StringUtils.trim(s)), CRYPTO_KEY); - } - - String generateRandomSecretKey() { - try { - KeyGenerator keyGen = KeyGenerator.getInstance(CRYPTO_KEY); - keyGen.init(KEY_SIZE_IN_BITS, new SecureRandom()); - SecretKey secretKey = keyGen.generateKey(); - return new String(Base64.encodeBase64(secretKey.getEncoded())); - - } catch (Exception e) { - throw new IllegalStateException("Fail to generate secret key", e); - } - } - - @VisibleForTesting - String getPathToSecretKey() { - if (StringUtils.isBlank(pathToSecretKey)) { - pathToSecretKey = new File(FileUtils.getUserDirectoryPath(), ".sonar/sonar-secret.txt").getPath(); - } - return pathToSecretKey; - } - - public void setPathToSecretKey(@Nullable String pathToSecretKey) { - this.pathToSecretKey = pathToSecretKey; - } -} diff --git a/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java b/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java deleted file mode 100644 index 5abbeb85ac2..00000000000 --- a/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.application; - -import org.apache.commons.codec.binary.Base64; - -final class Base64Cipher extends Cipher { - @Override - String encrypt(String clearText) { - return new String(Base64.encodeBase64(clearText.getBytes())); - } - - @Override - String decrypt(String encryptedText) { - return new String(Base64.decodeBase64(encryptedText)); - } -} diff --git a/sonar-application/src/main/java/org/sonar/application/Cipher.java b/sonar-application/src/main/java/org/sonar/application/Cipher.java deleted file mode 100644 index 44abfbb3176..00000000000 --- a/sonar-application/src/main/java/org/sonar/application/Cipher.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.application; - -abstract class Cipher { - abstract String encrypt(String clearText); - abstract String decrypt(String encryptedText); -} diff --git a/sonar-application/src/main/java/org/sonar/application/Encryption.java b/sonar-application/src/main/java/org/sonar/application/Encryption.java deleted file mode 100644 index 60e732fc716..00000000000 --- a/sonar-application/src/main/java/org/sonar/application/Encryption.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.application; - -import com.google.common.collect.ImmutableMap; - -import javax.annotation.Nullable; - -import java.util.Locale; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @since 3.0 - */ -public final class Encryption { - - private static final String BASE64_ALGORITHM = "b64"; - - private static final String AES_ALGORITHM = "aes"; - private final AesCipher aesCipher; - - private final Map ciphers; - 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); - } - public boolean isEncrypted(String value) { - return value.indexOf('{') == 0 && value.indexOf('}') > 1; - } - - public String decrypt(String encryptedText) { - Matcher matcher = ENCRYPTED_PATTERN.matcher(encryptedText); - if (matcher.matches()) { - Cipher cipher = ciphers.get(matcher.group(1).toLowerCase(Locale.ENGLISH)); - if (cipher != null) { - return cipher.decrypt(matcher.group(2)); - } - } - return encryptedText; - } - -} diff --git a/sonar-application/src/main/java/org/sonar/application/Env.java b/sonar-application/src/main/java/org/sonar/application/Env.java deleted file mode 100644 index 19861c7a1c5..00000000000 --- a/sonar-application/src/main/java/org/sonar/application/Env.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.application; - -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; - -class Env { - - static final String ERROR_MESSAGE = "Do not copy-paste the configuration files (conf directory) from the old version. Update the content of the new files instead."; - private final File confFile; - - // visible for testing - Env(URL confUrl) throws URISyntaxException { - if (confUrl == null) { - throw new IllegalStateException(ERROR_MESSAGE); - } - this.confFile = new File(confUrl.toURI()); - } - - Env() throws URISyntaxException { - this(Env.class.getResource("/sonar.properties")); - } - - File rootDir() { - return confFile.getParentFile().getParentFile(); - } - - File file(String relativePath) { - return new File(rootDir(), relativePath); - } - - File freshDir(String relativePath) { - File dir = new File(rootDir(), relativePath); - FileUtils.deleteQuietly(dir); - dir.mkdirs(); - return dir; - } - - /** - * This check is required in order to provide more meaningful message than JRuby - see SONAR-2715 - */ - void verifyWritableTempDir() { - File file = null; - try { - file = File.createTempFile("sonarqube-check", "tmp"); - } catch (IOException e) { - throw new IllegalStateException("Unable to create file in temporary directory, please check existence " + - "and permissions of: " + FileUtils.getTempDirectory(), e); - } finally { - FileUtils.deleteQuietly(file); - } - } -} diff --git a/sonar-application/src/main/java/org/sonar/application/Props.java b/sonar-application/src/main/java/org/sonar/application/Props.java deleted file mode 100644 index dbe24636d0e..00000000000 --- a/sonar-application/src/main/java/org/sonar/application/Props.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.application; - -import org.apache.commons.io.IOUtils; - -import javax.annotation.Nullable; - -import java.io.File; -import java.io.FileReader; -import java.util.Map; -import java.util.Properties; - -class Props { - - private final Properties props; - - Props(Properties props) { - this.props = props; - } - - String of(String key) { - return props.getProperty(key); - } - - String of(String key, @Nullable String defaultValue) { - String s = of(key); - return s == null ? defaultValue : s; - } - - boolean booleanOf(String key) { - String s = of(key); - return s != null && Boolean.parseBoolean(s); - } - - boolean booleanOf(String key, boolean defaultValue) { - String s = of(key); - return s != null ? Boolean.parseBoolean(s) : defaultValue; - } - - Integer intOf(String key) { - String s = of(key); - if (s != null && !"".equals(s)) { - try { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - throw new IllegalStateException("Value of property " + key + " is not an integer: " + s, e); - } - } - return null; - } - - int intOf(String key, int defaultValue) { - Integer i = intOf(key); - return i == null ? defaultValue : i; - } - - static Props create(Env env) { - File propsFile = env.file("conf/sonar.properties"); - Properties p = new Properties(); - FileReader reader = null; - try { - reader = new FileReader(propsFile); - - // order is important : the last override the first - p.load(reader); - p.putAll(System.getenv()); - p.putAll(System.getProperties()); - - 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); - - } catch (Exception e) { - throw new IllegalStateException("File does not exist or can't be open: " + propsFile, e); - - } finally { - IOUtils.closeQuietly(reader); - } - } - - static Properties decrypt(Properties properties) { - Encryption encryption = new Encryption(properties.getProperty(AesCipher.ENCRYPTION_SECRET_KEY_PATH)); - Properties result = new Properties(); - - for (Map.Entry 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); - } - return result; - } -} diff --git a/sonar-application/src/main/java/org/sonar/application/ForkProcesses.java b/sonar-application/src/main/java/org/sonar/application/StartServer.java similarity index 65% rename from sonar-application/src/main/java/org/sonar/application/ForkProcesses.java rename to sonar-application/src/main/java/org/sonar/application/StartServer.java index c6110baf3b3..18cee9bdcaa 100644 --- a/sonar-application/src/main/java/org/sonar/application/ForkProcesses.java +++ b/sonar-application/src/main/java/org/sonar/application/StartServer.java @@ -25,25 +25,23 @@ import org.sonar.process.ProcessWrapper; import javax.annotation.Nullable; -public class ForkProcesses { +public class StartServer { private Monitor monitor; private final Thread shutdownHook; private ProcessWrapper elasticsearch; private ProcessWrapper server; - public ForkProcesses() throws Exception { + public StartServer() throws Exception { Installation installation = new Installation(); String esPort = installation.prop("sonar.es.node.port", null); if (esPort == null) { esPort = String.valueOf(NetworkUtils.freePort()); - installation.setProp("sonar.es.node.port", esPort); } String esCluster = installation.prop("sonar.es.cluster.name", null); - if(esCluster == null){ + if (esCluster == null) { installation.setProp("sonar.es.cluster.name", "sonarqube"); } - installation.setProp("sonar.es.type", "TRANSPORT"); shutdownHook = new Thread(new Runnable() { @Override @@ -58,25 +56,28 @@ public class ForkProcesses { monitor = new Monitor(); - elasticsearch = new ProcessWrapper( - installation.homeDir().getAbsolutePath(), - installation.prop("sonar.es.javaOpts", "-server -Xmx256m -Xms128m -Xss256k -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"), - "org.sonar.search.ElasticSearch", - installation.props(), - "ES", - installation.starPath("lib/common"), - installation.starPath("lib/search")); + String opts = installation.prop("sonar.es.javaOpts", "-server -Xmx256m -Xms128m -Xss256k -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"); + elasticsearch = new ProcessWrapper("ES") + .setWorkDir(installation.homeDir()) + .addJavaOpts(opts) + .setClassName("org.sonar.search.ElasticSearch") + .setArguments(installation.props()) + .setArgument("sonar.es.node.port", esPort) + .addClasspath(installation.starPath("lib/common")) + .addClasspath(installation.starPath("lib/search")); monitor.registerProcess(elasticsearch); - server = new ProcessWrapper( - installation.homeDir().getAbsolutePath(), - installation.prop("sonar.web.javaOpts", "-Xmx768m -server -XX:MaxPermSize=160m -Djava.awt.headless=true -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Djruby.management.enabled=false"), - "org.sonar.server.app.ServerProcess", - installation.props(), - "SQ", - installation.starPath("lib/common"), - installation.starPath("lib/server")); + opts = installation.prop("sonar.web.javaOpts", "-Xmx768m -server -XX:MaxPermSize=160m -Djava.awt.headless=true -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Djruby.management.enabled=false"); + server = new ProcessWrapper("SQ") + .setWorkDir(installation.homeDir()) + .addJavaOpts(opts) + .setClassName("org.sonar.server.app.ServerProcess") + .setEnvProperty("SONAR_HOME", installation.homeDir().getAbsolutePath()) + .setArguments(installation.props()) + .setArgument("sonar.es.type", "TRANSPORT") + .addClasspath(installation.starPath("lib/common")) + .addClasspath(installation.starPath("lib/server")); monitor.registerProcess(server); monitor.start(); @@ -99,6 +100,7 @@ public class ForkProcesses { } } } + private void terminateAndWait(@Nullable ProcessWrapper process) { if (process != null && process.getThread() != null) { process.terminate(); @@ -106,6 +108,6 @@ public class ForkProcesses { } public static void main(String[] args) throws Exception { - new ForkProcesses(); + new StartServer(); } } diff --git a/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java b/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java deleted file mode 100644 index 9f097093105..00000000000 --- a/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.application; - -import com.google.common.io.Resources; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import javax.crypto.BadPaddingException; - -import java.io.File; -import java.security.InvalidKeyException; -import java.security.Key; - -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; - - -public class AesCipherTest { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void generateRandomSecretKey() { - AesCipher cipher = new AesCipher(null); - - String key = cipher.generateRandomSecretKey(); - - assertThat(StringUtils.isNotBlank(key)).isTrue(); - assertThat(Base64.isArrayByteBase64(key.getBytes())).isTrue(); - } - - @Test - public void encrypt() throws Exception { - AesCipher cipher = new AesCipher(pathToSecretKey()); - - String encryptedText = cipher.encrypt("this is a secret"); - - assertThat(StringUtils.isNotBlank(encryptedText)).isTrue(); - assertThat(Base64.isArrayByteBase64(encryptedText.getBytes())).isTrue(); - } - - @Test - public void encrypt_bad_key() throws Exception { - thrown.expect(RuntimeException.class); - thrown.expectMessage("Invalid AES key"); - - AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt")); - - cipher.encrypt("this is a secret"); - } - - @Test - public void decrypt() throws Exception { - AesCipher cipher = new AesCipher(pathToSecretKey()); - - // the following value has been encrypted with the key /org/sonar/api/config/AesCipherTest/aes_secret_key.txt - String clearText = cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY="); - - assertThat(clearText).isEqualTo("this is a secret"); - } - - @Test - public void decrypt_bad_key() throws Exception { - AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt")); - - try { - cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY="); - fail(); - - } catch (RuntimeException e) { - assertThat(e.getCause()).isInstanceOf(InvalidKeyException.class); - } - } - - @Test - public void decrypt_other_key() throws Exception { - AesCipher cipher = new AesCipher(getPath("other_secret_key.txt")); - - try { - // text encrypted with another key - cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY="); - fail(); - - } catch (RuntimeException e) { - assertThat(e.getCause()).isInstanceOf(BadPaddingException.class); - } - } - - @Test - public void encryptThenDecrypt() throws Exception { - AesCipher cipher = new AesCipher(pathToSecretKey()); - - assertThat(cipher.decrypt(cipher.encrypt("foo"))).isEqualTo("foo"); - } - - @Test - public void testDefaultPathToSecretKey() { - AesCipher cipher = new AesCipher(null); - - String path = cipher.getPathToSecretKey(); - - assertThat(StringUtils.isNotBlank(path)).isTrue(); - assertThat(new File(path).getName()).isEqualTo("sonar-secret.txt"); - } - - @Test - public void loadSecretKeyFromFile() throws Exception { - AesCipher cipher = new AesCipher(null); - Key secretKey = cipher.loadSecretFileFromFile(pathToSecretKey()); - assertThat(secretKey.getAlgorithm()).isEqualTo("AES"); - assertThat(secretKey.getEncoded().length).isGreaterThan(10); - } - - @Test - public void loadSecretKeyFromFile_trim_content() throws Exception { - String path = getPath("non_trimmed_secret_key.txt"); - AesCipher cipher = new AesCipher(null); - - Key secretKey = cipher.loadSecretFileFromFile(path); - - assertThat(secretKey.getAlgorithm()).isEqualTo("AES"); - assertThat(secretKey.getEncoded().length).isGreaterThan(10); - } - - @Test - public void loadSecretKeyFromFile_file_does_not_exist() throws Exception { - thrown.expect(IllegalStateException.class); - - AesCipher cipher = new AesCipher(null); - cipher.loadSecretFileFromFile("/file/does/not/exist"); - } - - @Test - public void loadSecretKeyFromFile_no_property() throws Exception { - thrown.expect(IllegalStateException.class); - - AesCipher cipher = new AesCipher(null); - cipher.loadSecretFileFromFile(null); - } - - @Test - public void hasSecretKey() throws Exception { - AesCipher cipher = new AesCipher(pathToSecretKey()); - - assertThat(cipher.hasSecretKey()).isTrue(); - } - - @Test - public void doesNotHaveSecretKey() throws Exception { - AesCipher cipher = new AesCipher("/my/twitter/id/is/SimonBrandhof"); - - assertThat(cipher.hasSecretKey()).isFalse(); - } - - private static String getPath(String file){ - return Resources.getResource(AesCipherTest.class, "AesCipherTest/" + file).getPath(); - } - - private static String pathToSecretKey() throws Exception { - return getPath("aes_secret_key.txt"); - } - -} diff --git a/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java b/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java deleted file mode 100644 index 80bc4f1d819..00000000000 --- a/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.application; - -import org.junit.Test; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class EncryptionTest { - - @Test - public void isEncrypted() { - Encryption encryption = new Encryption(null); - assertThat(encryption.isEncrypted("{aes}ADASDASAD"), is(true)); - assertThat(encryption.isEncrypted("{b64}ADASDASAD"), is(true)); - assertThat(encryption.isEncrypted("{abc}ADASDASAD"), is(true)); - - assertThat(encryption.isEncrypted("{}"), is(false)); - assertThat(encryption.isEncrypted("{foo"), is(false)); - assertThat(encryption.isEncrypted("foo{aes}"), is(false)); - } - - @Test - public void decrypt() { - Encryption encryption = new Encryption(null); - assertThat(encryption.decrypt("{b64}Zm9v"), is("foo")); - } - - @Test - public void decrypt_unknown_algorithm() { - Encryption encryption = new Encryption(null); - assertThat(encryption.decrypt("{xxx}Zm9v"), is("{xxx}Zm9v")); - } - - @Test - public void decrypt_uncrypted_text() { - Encryption encryption = new Encryption(null); - assertThat(encryption.decrypt("foo"), is("foo")); - } -} diff --git a/sonar-application/src/test/java/org/sonar/application/EnvTest.java b/sonar-application/src/test/java/org/sonar/application/EnvTest.java deleted file mode 100644 index 0ffc768a48d..00000000000 --- a/sonar-application/src/test/java/org/sonar/application/EnvTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.application; - -import org.apache.commons.io.FileUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.File; - -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; - -public class EnvTest { - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - @Test - public void testName() throws Exception { - File file = new File(getClass().getResource("/org/sonar/application/LoggingTest/logback-access.xml").toURI()); - assertThat(file.exists()).isTrue(); - } - - @Test - public void files() throws Exception { - File home = temp.newFolder(); - File confFile = new File(home, "conf/sonar.properties"); - File logFile = new File(home, "logs/sonar.log"); - - FileUtils.touch(confFile); - FileUtils.touch(logFile); - - Env env = new Env(confFile.toURL()); - - assertThat(env.rootDir()).isDirectory().exists().isEqualTo(home); - assertThat(env.file("conf/sonar.properties")).isFile().exists().isEqualTo(confFile); - assertThat(env.file("logs/sonar.log")).isFile().exists().isEqualTo(logFile); - assertThat(env.file("xxx/unknown.log")).doesNotExist(); - } - - @Test - public void fresh_dir() throws Exception { - File home = temp.newFolder(); - File confFile = new File(home, "conf/sonar.properties"); - File logFile = new File(home, "logs/sonar.log"); - - FileUtils.touch(confFile); - FileUtils.touch(logFile); - - Env env = new Env(confFile.toURL()); - - File data = env.freshDir("data/h2"); - assertThat(data).isDirectory().exists(); - assertThat(data.getParentFile().getName()).isEqualTo("data"); - assertThat(data.getParentFile().getParentFile()).isEqualTo(home); - - // clean directory - File logs = env.freshDir("logs"); - assertThat(logs).isDirectory().exists(); - assertThat(logs.listFiles()).isEmpty(); - } - - @Test - public void temp_dir_should_be_writable() throws Exception { - new Env(temp.newFile().toURL()).verifyWritableTempDir(); - // do not fail - } - - @Test - public void fail_if_conf_file_not_found() throws Exception { - try { - // note that "new Env(null)" would be exact, but let's - // keep "new Env()" for increasing code coverage :-) - new Env(); - fail(); - } catch (IllegalStateException e) { - assertThat(e).hasMessage(Env.ERROR_MESSAGE); - } - } -} diff --git a/sonar-application/src/test/java/org/sonar/application/PropsTest.java b/sonar-application/src/test/java/org/sonar/application/PropsTest.java deleted file mode 100644 index 46ca5e8dd26..00000000000 --- a/sonar-application/src/test/java/org/sonar/application/PropsTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.application; - -import com.google.common.io.Resources; -import org.apache.commons.io.FilenameUtils; -import org.junit.Test; - -import java.io.File; -import java.util.Properties; - -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 PropsTest { - - @Test - public void of() throws Exception { - Properties p = new Properties(); - p.setProperty("foo", "bar"); - Props props = new Props(p); - - assertThat(props.of("foo")).isEqualTo("bar"); - assertThat(props.of("foo", "default value")).isEqualTo("bar"); - assertThat(props.of("unknown")).isNull(); - assertThat(props.of("unknown", "default value")).isEqualTo("default value"); - } - - @Test - public void intOf() throws Exception { - Properties p = new Properties(); - p.setProperty("foo", "33"); - p.setProperty("blank", ""); - Props props = new Props(p); - - assertThat(props.intOf("foo")).isEqualTo(33); - assertThat(props.intOf("foo", 44)).isEqualTo(33); - assertThat(props.intOf("blank")).isNull(); - assertThat(props.intOf("blank", 55)).isEqualTo(55); - assertThat(props.intOf("unknown")).isNull(); - assertThat(props.intOf("unknown", 44)).isEqualTo(44); - } - - @Test - public void intOf_not_integer() throws Exception { - Properties p = new Properties(); - p.setProperty("foo", "bar"); - Props props = new Props(p); - - try { - props.intOf("foo"); - fail(); - } catch (IllegalStateException e) { - assertThat(e).hasMessage("Value of property foo is not an integer: bar"); - } - } - - @Test - public void booleanOf() throws Exception { - Properties p = new Properties(); - p.setProperty("foo", "True"); - p.setProperty("bar", "false"); - Props props = new Props(p); - - assertThat(props.booleanOf("foo")).isTrue(); - assertThat(props.booleanOf("bar")).isFalse(); - assertThat(props.booleanOf("unknown")).isFalse(); - } - - @Test - public void booleanOf_default_value() throws Exception { - Properties p = new Properties(); - p.setProperty("foo", "true"); - p.setProperty("bar", "false"); - Props props = new Props(p); - - assertThat(props.booleanOf("unset", false)).isFalse(); - assertThat(props.booleanOf("unset", true)).isTrue(); - assertThat(props.booleanOf("foo", false)).isTrue(); - assertThat(props.booleanOf("bar", true)).isFalse(); - } - - @Test - public void load_file_and_system_properties() throws Exception { - System.setProperty("hello", "bar"); - - Env env = mock(Env.class); - File propsFile = new File(Resources.getResource(getClass(), "PropsTest/sonar.properties").getFile()); - when(env.file("conf/sonar.properties")).thenReturn(propsFile); - - Props props = Props.create(env); - - assertThat(props.of("foo")).isEqualTo("bar"); - assertThat(props.of("java.version")).isNotNull(); - - // system properties override file properties - assertThat(props.of("hello")).isEqualTo("bar"); - assertThat(props.of("java.io.tmpdir")).isNotEmpty().isNotEqualTo("/should/be/overridden"); - - assertThat(System.getProperty("foo")).isEqualTo("bar"); - assertThat(System.getProperty("hello")).isEqualTo("bar"); - } - - @Test - public void fail_if_file_does_not_exist() throws Exception { - Env env = mock(Env.class); - when(env.file("conf/sonar.properties")).thenReturn(new File("target/not_exist/sonar.properties")); - - try { - Props.create(env); - fail(); - } catch (IllegalStateException e) { - assertThat(e).hasMessage("File does not exist or can't be open: " + FilenameUtils.separatorsToSystem("target/not_exist/sonar.properties")); - } - } -} -- 2.39.5