]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9590 move Es/JavaCommand to module sonar-process-monitor
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 18 Aug 2017 08:23:26 +0000 (10:23 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 5 Sep 2017 12:24:12 +0000 (14:24 +0200)
32 files changed:
server/sonar-process-monitor/src/main/java/org/sonar/application/SchedulerImpl.java
server/sonar-process-monitor/src/main/java/org/sonar/application/process/AbstractCommand.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/CommandFactory.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/CommandFactoryImpl.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsCommand.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsLogging.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsProcessMonitor.java
server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsSettings.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/JavaCommand.java [deleted file]
server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessCommandsProcessMonitor.java
server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessLauncher.java
server/sonar-process-monitor/src/main/java/org/sonar/application/process/ProcessLauncherImpl.java
server/sonar-process-monitor/src/test/java/org/sonar/application/SchedulerImplTest.java
server/sonar-process-monitor/src/test/java/org/sonar/application/process/AbstractCommandTest.java [deleted file]
server/sonar-process-monitor/src/test/java/org/sonar/application/process/EsLoggingTest.java [deleted file]
server/sonar-process-monitor/src/test/java/org/sonar/application/process/EsSettingsTest.java [deleted file]
server/sonar-process-monitor/src/test/java/org/sonar/application/process/JavaCommandTest.java [deleted file]
server/sonar-process-monitor/src/test/java/org/sonar/application/process/ProcessLauncherImplTest.java
server/sonar-process/src/main/java/org/sonar/process/command/AbstractCommand.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/command/CommandFactory.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/command/CommandFactoryImpl.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/command/EsCommand.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/command/JavaCommand.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/command/package-info.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/es/EsLogging.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/es/EsSettings.java [new file with mode: 0644]
server/sonar-process/src/main/java/org/sonar/process/es/package-info.java [new file with mode: 0644]
server/sonar-process/src/test/java/org/sonar/process/command/AbstractCommandTest.java [new file with mode: 0644]
server/sonar-process/src/test/java/org/sonar/process/command/JavaCommandTest.java [new file with mode: 0644]
server/sonar-process/src/test/java/org/sonar/process/es/EsLoggingTest.java [new file with mode: 0644]
server/sonar-process/src/test/java/org/sonar/process/es/EsSettingsTest.java [new file with mode: 0644]
sonar-application/src/main/java/org/sonar/application/App.java

index a62c0d36eae64fe3d0c7a0a195aac41a7fd0c408..7311297dd3c938536dd0815f7b50854624e54e97 100644 (file)
@@ -30,9 +30,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.application.config.AppSettings;
 import org.sonar.application.config.ClusterSettings;
-import org.sonar.application.process.CommandFactory;
-import org.sonar.application.process.EsCommand;
-import org.sonar.application.process.JavaCommand;
+import org.sonar.process.command.CommandFactory;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
 import org.sonar.application.process.ProcessLauncher;
 import org.sonar.application.process.Lifecycle;
 import org.sonar.application.process.ProcessEventListener;
@@ -107,7 +107,7 @@ public class SchedulerImpl implements Scheduler, ProcessEventListener, ProcessLi
   private void tryToStartEs() {
     SQProcess process = processesById.get(ProcessId.ELASTICSEARCH);
     if (process != null) {
-      tryToStartEsProcess(process, () -> commandFactory.createEsCommand(settings));
+      tryToStartEsProcess(process, commandFactory::createEsCommand);
     }
   }
 
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/AbstractCommand.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/AbstractCommand.java
deleted file mode 100644 (file)
index ad44865..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Properties;
-import javax.annotation.Nullable;
-import org.sonar.process.ProcessId;
-
-public abstract class AbstractCommand<T extends AbstractCommand> {
-  // unique key among the group of commands to launch
-  private final ProcessId id;
-  // program arguments
-  private final Map<String, String> arguments = new LinkedHashMap<>();
-  private final Map<String, String> envVariables = new HashMap<>(System.getenv());
-  private File workDir;
-
-  protected AbstractCommand(ProcessId id) {
-    this.id = id;
-  }
-
-  public ProcessId getProcessId() {
-    return id;
-  }
-
-  public File getWorkDir() {
-    return workDir;
-  }
-
-  public T setWorkDir(File workDir) {
-    this.workDir = workDir;
-    return castThis();
-  }
-
-  @SuppressWarnings("unchecked")
-  private T castThis() {
-    return (T) this;
-  }
-
-  public Map<String, String> getArguments() {
-    return arguments;
-  }
-
-  public T setArgument(String key, @Nullable String value) {
-    if (value == null) {
-      arguments.remove(key);
-    } else {
-      arguments.put(key, value);
-    }
-    return castThis();
-  }
-
-  public T setArguments(Properties args) {
-    for (Map.Entry<Object, Object> entry : args.entrySet()) {
-      setArgument(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString() : null);
-    }
-    return castThis();
-  }
-
-  public Map<String, String> getEnvVariables() {
-    return envVariables;
-  }
-
-  public T setEnvVariable(String key, @Nullable String value) {
-    if (value == null) {
-      envVariables.remove(key);
-    } else {
-      envVariables.put(key, value);
-    }
-    return castThis();
-  }
-}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/CommandFactory.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/CommandFactory.java
deleted file mode 100644 (file)
index 0e8f9a7..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import org.sonar.application.config.AppSettings;
-
-public interface CommandFactory {
-
-  EsCommand createEsCommand(AppSettings settings);
-
-  JavaCommand createWebCommand(boolean leader);
-
-  JavaCommand createCeCommand();
-
-}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/CommandFactoryImpl.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/CommandFactoryImpl.java
deleted file mode 100644 (file)
index de2174d..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.util.Map;
-import java.util.Optional;
-import org.sonar.application.config.AppSettings;
-import org.sonar.process.ProcessId;
-import org.sonar.process.ProcessProperties;
-
-import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
-import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
-import static org.sonar.process.ProcessProperties.HTTP_PROXY_HOST;
-import static org.sonar.process.ProcessProperties.HTTP_PROXY_PORT;
-
-public class CommandFactoryImpl implements CommandFactory {
-  /**
-   * Properties about proxy that must be set as system properties
-   */
-  private static final String[] PROXY_PROPERTY_KEYS = new String[] {
-    HTTP_PROXY_HOST,
-    HTTP_PROXY_PORT,
-    "http.nonProxyHosts",
-    HTTPS_PROXY_HOST,
-    HTTPS_PROXY_PORT,
-    "http.auth.ntlm.domain",
-    "socksProxyHost",
-    "socksProxyPort"};
-
-  private final AppSettings settings;
-
-  public CommandFactoryImpl(AppSettings settings) {
-    this.settings = settings;
-  }
-
-  @Override
-  public EsCommand createEsCommand(AppSettings settings) {
-    File homeDir = this.settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
-    File executable = new File(homeDir, getExecutable());
-    if (!executable.exists()) {
-      throw new IllegalStateException("Cannot find elasticsearch binary");
-    }
-
-    Map<String, String> settingsMap = new EsSettings(this.settings.getProps()).build();
-
-    File logDir = new File(settingsMap.get("path.logs"));
-    File confDir = new File(settingsMap.get("path.conf"));
-    EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH)
-      .setWorkDir(executable.getParentFile().getParentFile())
-      .setExecutable(executable)
-      .setConfDir(confDir)
-      .setLog4j2Properties(new EsLogging().createProperties(settings.getProps(), logDir))
-      .setArguments(this.settings.getProps().rawProperties())
-      .setClusterName(settingsMap.get("cluster.name"))
-      .setHost(settingsMap.get("network.host"))
-      .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
-      .addJvmOption(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
-      .addJvmOption(settings.getProps().nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
-      .setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"));
-
-    settingsMap.forEach((key, value) -> res.addEsOption("-E" + key + "=" + value));
-
-    return res;
-  }
-
-  private static String getExecutable() {
-    if (System.getProperty("os.name").startsWith("Windows")) {
-      return "elasticsearch/bin/elasticsearch.bat";
-    }
-    return "elasticsearch/bin/elasticsearch";
-  }
-
-  @Override
-  public JavaCommand createWebCommand(boolean leader) {
-    File homeDir = settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
-    JavaCommand command = newJavaCommand(ProcessId.WEB_SERVER, homeDir)
-      .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
-      .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
-      .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
-      // required for logback tomcat valve
-      .setEnvVariable(ProcessProperties.PATH_LOGS, settings.getProps().nonNullValue(ProcessProperties.PATH_LOGS))
-      .setArgument("sonar.cluster.web.startupLeader", Boolean.toString(leader))
-      .setClassName("org.sonar.server.app.WebServer")
-      .addClasspath("./lib/common/*")
-      .addClasspath("./lib/server/*");
-    String driverPath = settings.getProps().value(ProcessProperties.JDBC_DRIVER_PATH);
-    if (driverPath != null) {
-      command.addClasspath(driverPath);
-    }
-    return command;
-  }
-
-  @Override
-  public JavaCommand createCeCommand() {
-    File homeDir = settings.getProps().nonNullValueAsFile(ProcessProperties.PATH_HOME);
-    JavaCommand command = newJavaCommand(ProcessId.COMPUTE_ENGINE, homeDir)
-      .addJavaOptions(ProcessProperties.CE_ENFORCED_JVM_ARGS)
-      .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.CE_JAVA_OPTS))
-      .addJavaOptions(settings.getProps().nonNullValue(ProcessProperties.CE_JAVA_ADDITIONAL_OPTS))
-      .setClassName("org.sonar.ce.app.CeServer")
-      .addClasspath("./lib/common/*")
-      .addClasspath("./lib/server/*")
-      .addClasspath("./lib/ce/*");
-    String driverPath = settings.getProps().value(ProcessProperties.JDBC_DRIVER_PATH);
-    if (driverPath != null) {
-      command.addClasspath(driverPath);
-    }
-    return command;
-  }
-
-  private JavaCommand newJavaCommand(ProcessId id, File homeDir) {
-    JavaCommand command = new JavaCommand(id)
-      .setWorkDir(homeDir)
-      .setArguments(settings.getProps().rawProperties());
-
-    for (String key : PROXY_PROPERTY_KEYS) {
-      settings.getValue(key).ifPresent(val -> command.addJavaOption("-D" + key + "=" + val));
-    }
-
-    // defaults of HTTPS are the same than HTTP defaults
-    setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
-    setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
-    return command;
-  }
-
-  private void setSystemPropertyToDefaultIfNotSet(JavaCommand command,
-    String httpsProperty, String httpProperty) {
-    Optional<String> httpValue = settings.getValue(httpProperty);
-    Optional<String> httpsValue = settings.getValue(httpsProperty);
-    if (!httpsValue.isPresent() && httpValue.isPresent()) {
-      command.addJavaOption("-D" + httpsProperty + "=" + httpValue.get());
-    }
-  }
-}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsCommand.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsCommand.java
deleted file mode 100644 (file)
index 43974a5..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import org.sonar.process.ProcessId;
-
-public class EsCommand extends AbstractCommand<EsCommand> {
-  private File executable;
-  private File confDir;
-  private String clusterName;
-  private String host;
-  private int port;
-  private Properties log4j2Properties;
-  private List<String> esOptions = new ArrayList<>();
-  private List<String> jvmOptions = new ArrayList<>();
-
-  public EsCommand(ProcessId id) {
-    super(id);
-  }
-
-  public File getExecutable() {
-    return executable;
-  }
-
-  public EsCommand setExecutable(File executable) {
-    this.executable = executable;
-    return this;
-  }
-
-  public File getConfDir() {
-    return confDir;
-  }
-
-  public EsCommand setConfDir(File confDir) {
-    this.confDir = confDir;
-    return this;
-  }
-
-  public String getClusterName() {
-    return clusterName;
-  }
-
-  public EsCommand setClusterName(String clusterName) {
-    this.clusterName = clusterName;
-    return this;
-  }
-
-  public String getHost() {
-    return host;
-  }
-
-  public EsCommand setHost(String host) {
-    this.host = host;
-    return this;
-  }
-
-  public int getPort() {
-    return port;
-  }
-
-  public EsCommand setPort(int port) {
-    this.port = port;
-    return this;
-  }
-
-  public Properties getLog4j2Properties() {
-    return log4j2Properties;
-  }
-
-  public EsCommand setLog4j2Properties(Properties log4j2Properties) {
-    this.log4j2Properties = log4j2Properties;
-    return this;
-  }
-
-  public List<String> getEsOptions() {
-    return esOptions;
-  }
-
-  public EsCommand addEsOption(String s) {
-    if (!s.isEmpty()) {
-      esOptions.add(s);
-    }
-    return this;
-  }
-
-  public List<String> getJvmOptions() {
-    return jvmOptions;
-  }
-
-  public EsCommand addJvmOption(String s) {
-    if (!s.isEmpty()) {
-      jvmOptions.add(s);
-    }
-    return this;
-  }
-
-}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsLogging.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsLogging.java
deleted file mode 100644 (file)
index 4dadb95..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import ch.qos.logback.classic.Level;
-import java.io.File;
-import java.util.Properties;
-import org.sonar.process.ProcessId;
-import org.sonar.process.Props;
-import org.sonar.process.logging.Log4JPropertiesBuilder;
-import org.sonar.process.logging.LogLevelConfig;
-import org.sonar.process.logging.RootLoggerConfig;
-
-import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
-
-public class EsLogging {
-
-  public Properties createProperties(Props props, File logDir) {
-    Log4JPropertiesBuilder log4JPropertiesBuilder = new Log4JPropertiesBuilder(props);
-    RootLoggerConfig config = newRootLoggerConfigBuilder().setProcessId(ProcessId.ELASTICSEARCH).build();
-    String logPattern = log4JPropertiesBuilder.buildLogPattern(config);
-
-    log4JPropertiesBuilder.internalLogLevel(Level.ERROR);
-    log4JPropertiesBuilder.configureGlobalFileLog(config, logDir, logPattern);
-    log4JPropertiesBuilder.apply(
-      LogLevelConfig.newBuilder(log4JPropertiesBuilder.getRootLoggerName())
-        .rootLevelFor(ProcessId.ELASTICSEARCH)
-        .build());
-
-    return log4JPropertiesBuilder.get();
-  }
-
-}
index 4572af62128359f647fdfa1769653ff9003373fa..52c0a9d917a618a18a731a443b2465d9794021de 100644 (file)
@@ -41,6 +41,7 @@ import org.elasticsearch.transport.Netty4Plugin;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.process.ProcessId;
+import org.sonar.process.command.EsCommand;
 
 import static java.util.Collections.singletonList;
 import static java.util.Collections.unmodifiableList;
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsSettings.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/EsSettings.java
deleted file mode 100644 (file)
index d791a4c..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static java.lang.String.valueOf;
-
-public class EsSettings {
-
-  private static final Logger LOGGER = LoggerFactory.getLogger(EsSettings.class);
-  private static final String PROP_MARVEL_HOSTS = "sonar.search.marvelHosts";
-  private static final String CLUSTER_SEARCH_NODE_NAME = "sonar.cluster.search.nodeName";
-  private static final String STANDALONE_NODE_NAME = "sonarqube";
-
-  private final Props props;
-
-  private final boolean clusterEnabled;
-  private final String clusterName;
-  private final String nodeName;
-
-  EsSettings(Props props) {
-    this.props = props;
-
-    this.clusterName = props.nonNullValue(ProcessProperties.CLUSTER_NAME);
-    this.clusterEnabled = props.valueAsBoolean(ProcessProperties.CLUSTER_ENABLED);
-    if (this.clusterEnabled) {
-      this.nodeName = props.value(CLUSTER_SEARCH_NODE_NAME, "sonarqube-" + UUID.randomUUID().toString());
-    } else {
-      this.nodeName = STANDALONE_NODE_NAME;
-    }
-  }
-
-  Map<String, String> build() {
-    Map<String, String> builder = new HashMap<>();
-    configureFileSystem(builder);
-    configureNetwork(builder);
-    configureCluster(builder);
-    configureMarvel(builder);
-    configureAction(builder);
-    return builder;
-  }
-
-  private void configureFileSystem(Map<String, String> builder) {
-    File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
-
-    builder.put("path.data", buildDataPath(homeDir).getAbsolutePath());
-    builder.put("path.conf", buildConfDir().getAbsolutePath());
-    builder.put("path.logs", buildLogPath(homeDir).getAbsolutePath());
-  }
-
-  private File buildDataPath(File homeDir) {
-    String dataPath = props.value(ProcessProperties.PATH_DATA);
-    if (StringUtils.isNotEmpty(dataPath)) {
-      return new File(dataPath, "es");
-    }
-    return new File(homeDir, "data/es");
-  }
-
-  private File buildLogPath(File homeDir) {
-    String logPath = props.value(ProcessProperties.PATH_LOGS);
-    if (StringUtils.isNotEmpty(logPath)) {
-      return new File(logPath);
-    }
-    return new File(homeDir, "log");
-  }
-
-  private File buildConfDir() {
-    String tempPath = props.value(ProcessProperties.PATH_TEMP);
-    return new File(new File(tempPath, "conf"), "es");
-  }
-
-  private void configureNetwork(Map<String, String> builder) {
-    InetAddress host = readHost();
-    int port = Integer.parseInt(props.nonNullValue(ProcessProperties.SEARCH_PORT));
-    LOGGER.info("Elasticsearch listening on {}:{}", host, port);
-
-    builder.put("transport.tcp.port", valueOf(port));
-    builder.put("transport.host", valueOf(host.getHostAddress()));
-    builder.put("network.host", valueOf(host.getHostAddress()));
-
-    // Elasticsearch sets the default value of TCP reuse address to true only on non-MSWindows machines, but why ?
-    builder.put("network.tcp.reuse_address", valueOf(true));
-
-    int httpPort = props.valueAsInt(ProcessProperties.SEARCH_HTTP_PORT, -1);
-    if (httpPort < 0) {
-      // standard configuration
-      builder.put("http.enabled", valueOf(false));
-    } else {
-      LOGGER.warn("Elasticsearch HTTP connector is enabled on port {}. MUST NOT BE USED FOR PRODUCTION", httpPort);
-      // see https://github.com/lmenezes/elasticsearch-kopf/issues/195
-      builder.put("http.cors.enabled", valueOf(true));
-      builder.put("http.cors.allow-origin", "*");
-      builder.put("http.enabled", valueOf(true));
-      builder.put("http.host", host.getHostAddress());
-      builder.put("http.port", valueOf(httpPort));
-    }
-  }
-
-  private InetAddress readHost() {
-    String hostProperty = props.nonNullValue(ProcessProperties.SEARCH_HOST);
-    try {
-      return InetAddress.getByName(hostProperty);
-    } catch (UnknownHostException e) {
-      throw new IllegalStateException("Can not resolve host [" + hostProperty + "]. Please check network settings and property " + ProcessProperties.SEARCH_HOST, e);
-    }
-  }
-
-  private void configureCluster(Map<String, String> builder) {
-    // Default value in a standalone mode, not overridable
-
-    int minimumMasterNodes = 1;
-    String initialStateTimeOut = "30s";
-
-    if (clusterEnabled) {
-      minimumMasterNodes = props.valueAsInt(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, 2);
-      initialStateTimeOut = props.value(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "120s");
-
-      String hosts = props.value(ProcessProperties.CLUSTER_SEARCH_HOSTS, "");
-      LOGGER.info("Elasticsearch cluster enabled. Connect to hosts [{}]", hosts);
-      builder.put("discovery.zen.ping.unicast.hosts", hosts);
-    }
-
-    builder.put("discovery.zen.minimum_master_nodes", valueOf(minimumMasterNodes));
-    builder.put("discovery.initial_state_timeout", initialStateTimeOut);
-    builder.put("cluster.name", clusterName);
-    builder.put("cluster.routing.allocation.awareness.attributes", "rack_id");
-    builder.put("node.attr.rack_id", nodeName);
-    builder.put("node.name", nodeName);
-    builder.put("node.data", valueOf(true));
-    builder.put("node.master", valueOf(true));
-  }
-
-  private void configureMarvel(Map<String, String> builder) {
-    Set<String> marvels = new TreeSet<>();
-    marvels.addAll(Arrays.asList(StringUtils.split(props.value(PROP_MARVEL_HOSTS, ""), ",")));
-
-    // If we're collecting indexing data send them to the Marvel host(s)
-    if (!marvels.isEmpty()) {
-      String hosts = StringUtils.join(marvels, ",");
-      LOGGER.info("Elasticsearch Marvel is enabled for %s", hosts);
-      builder.put("marvel.agent.exporter.es.hosts", hosts);
-    }
-  }
-
-  private static void configureAction(Map<String, String> builder) {
-    builder.put("action.auto_create_index", String.valueOf(false));
-  }
-}
diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/application/process/JavaCommand.java b/server/sonar-process-monitor/src/main/java/org/sonar/application/process/JavaCommand.java
deleted file mode 100644 (file)
index f22bd7b..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.sonar.process.ProcessId;
-
-public class JavaCommand extends AbstractCommand<JavaCommand> {
-  // entry point
-  private String className;
-  // for example -Xmx1G
-  private final List<String> javaOptions = new ArrayList<>();
-  // relative path to JAR files
-  private final List<String> classpath = new ArrayList<>();
-
-  public JavaCommand(ProcessId id) {
-    super(id);
-  }
-
-  public List<String> getJavaOptions() {
-    return javaOptions;
-  }
-
-  public JavaCommand addJavaOption(String s) {
-    if (!s.isEmpty()) {
-      javaOptions.add(s);
-    }
-    return this;
-  }
-
-  public JavaCommand addJavaOptions(String s) {
-    for (String opt : s.split(" ")) {
-      addJavaOption(opt);
-    }
-    return this;
-  }
-
-  public String getClassName() {
-    return className;
-  }
-
-  public JavaCommand setClassName(String className) {
-    this.className = className;
-    return this;
-  }
-
-  public List<String> getClasspath() {
-    return classpath;
-  }
-
-  public JavaCommand addClasspath(String s) {
-    classpath.add(s);
-    return this;
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("JavaCommand{");
-    sb.append("workDir=").append(getWorkDir());
-    sb.append(", javaOptions=").append(javaOptions);
-    sb.append(", className='").append(className).append('\'');
-    sb.append(", classpath=").append(classpath);
-    sb.append(", arguments=").append(getArguments());
-    sb.append(", envVariables=").append(getEnvVariables());
-    sb.append('}');
-    return sb.toString();
-  }
-}
index 672debcadc0ee517761a09a30a5e14b24299b803..a54f53f7f8fb5eb77f732ff87aa01735ce00fd05 100644 (file)
@@ -19,8 +19,8 @@
  */
 package org.sonar.application.process;
 
-import org.sonar.process.sharedmemoryfile.ProcessCommands;
 import org.sonar.process.ProcessId;
+import org.sonar.process.sharedmemoryfile.ProcessCommands;
 
 import static java.util.Objects.requireNonNull;
 
index eb25eccc9ca79c9fbb505da8f24ef2d1e9b87d47..c39f91bc8fa75d9696fc4e3ab455db85fde31517 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.application.process;
 
 import java.io.Closeable;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
 
 public interface ProcessLauncher extends Closeable {
 
index 4bb42a72f4563b415b063132f8db84818ed19b7c..aa5c6117c00cfcdc3cd4462e2fc4679c50791556 100644 (file)
@@ -35,6 +35,9 @@ import java.util.stream.Collectors;
 import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.process.command.AbstractCommand;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
 import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
 import org.sonar.process.sharedmemoryfile.ProcessCommands;
 import org.sonar.process.ProcessId;
index c2a94d039f768f9f24d1822e19d5c91b1f14f82b..231c2627450197ac0416749759ad76a295b5c885 100644 (file)
@@ -34,16 +34,15 @@ import org.junit.rules.ExpectedException;
 import org.junit.rules.TestRule;
 import org.junit.rules.Timeout;
 import org.mockito.Mockito;
-import org.sonar.application.config.AppSettings;
 import org.sonar.application.config.TestAppSettings;
-import org.sonar.application.process.AbstractCommand;
-import org.sonar.application.process.CommandFactory;
-import org.sonar.application.process.EsCommand;
-import org.sonar.application.process.JavaCommand;
 import org.sonar.application.process.ProcessLauncher;
 import org.sonar.application.process.ProcessMonitor;
 import org.sonar.process.ProcessId;
 import org.sonar.process.ProcessProperties;
+import org.sonar.process.command.AbstractCommand;
+import org.sonar.process.command.CommandFactory;
+import org.sonar.process.command.EsCommand;
+import org.sonar.process.command.JavaCommand;
 
 import static java.util.Collections.synchronizedList;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -310,7 +309,7 @@ public class SchedulerImplTest {
 
   private static class TestCommandFactory implements CommandFactory {
     @Override
-    public EsCommand createEsCommand(AppSettings settings) {
+    public EsCommand createEsCommand() {
       return ES_COMMAND;
     }
 
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/AbstractCommandTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/AbstractCommandTest.java
deleted file mode 100644 (file)
index 327f7b7..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessId;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class AbstractCommandTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  @Test
-  public void test_command_with_complete_information() throws Exception {
-    AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH) {
-
-    };
-
-    command.setArgument("first_arg", "val1");
-    Properties args = new Properties();
-    args.setProperty("second_arg", "val2");
-    command.setArguments(args);
-
-    command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
-    File workDir = temp.newFolder();
-    command.setWorkDir(workDir);
-
-    assertThat(command.toString()).isNotNull();
-    assertThat(command.getWorkDir()).isSameAs(workDir);
-
-    // copy current env variables
-    assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
-    assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
-  }
-
-}
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/EsLoggingTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/EsLoggingTest.java
deleted file mode 100644 (file)
index a5f55d1..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class EsLoggingTest {
-  @Rule
-  public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
-  private EsLogging underTest = new EsLogging();
-
-  @Test
-  public void createProperties_with_empty_props() throws IOException {
-    File logDir = temporaryFolder.newFolder();
-    Properties properties = underTest.createProperties(newProps(), logDir);
-
-    verifyProperties(properties,
-      "status", "ERROR",
-      "appender.file_es.type", "RollingFile",
-      "appender.file_es.name", "file_es",
-      "appender.file_es.filePattern", new File(logDir, "es.%d{yyyy-MM-dd}.log").getAbsolutePath(),
-      "appender.file_es.fileName", new File(logDir, "es.log").getAbsolutePath(),
-      "appender.file_es.layout.type", "PatternLayout",
-      "appender.file_es.layout.pattern", "%d{yyyy.MM.dd HH:mm:ss} %-5level es[][%logger{1.}] %msg%n",
-      "appender.file_es.policies.type", "Policies",
-      "appender.file_es.policies.time.type", "TimeBasedTriggeringPolicy",
-      "appender.file_es.policies.time.interval", "1",
-      "appender.file_es.policies.time.modulate", "true",
-      "appender.file_es.strategy.type", "DefaultRolloverStrategy",
-      "appender.file_es.strategy.fileIndex", "nomax",
-      "appender.file_es.strategy.action.type", "Delete",
-      "appender.file_es.strategy.action.basepath", logDir.getAbsolutePath(),
-      "appender.file_es.strategy.action.maxDepth", "1",
-      "appender.file_es.strategy.action.condition.type", "IfFileName",
-      "appender.file_es.strategy.action.condition.glob", "es*",
-      "appender.file_es.strategy.action.condition.nested_condition.type", "IfAccumulatedFileCount",
-      "appender.file_es.strategy.action.condition.nested_condition.exceeds", "7",
-      "rootLogger.level", "INFO",
-      "rootLogger.appenderRef.file_es.ref", "file_es");
-  }
-
-  @Test
-  public void createProperties_sets_root_logger_to_INFO_if_no_property_is_set() throws IOException {
-    File logDir = temporaryFolder.newFolder();
-    Properties properties = underTest.createProperties(newProps(), logDir);
-
-    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("INFO");
-  }
-
-  @Test
-  public void createProperties_sets_root_logger_to_global_property_if_set() throws IOException {
-    File logDir = temporaryFolder.newFolder();
-    Properties properties = underTest.createProperties(newProps("sonar.log.level", "TRACE"), logDir);
-
-    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
-  }
-
-  @Test
-  public void createProperties_sets_root_logger_to_process_property_if_set() throws IOException {
-    File logDir = temporaryFolder.newFolder();
-    Properties properties = underTest.createProperties(newProps("sonar.log.level.es", "DEBUG"), logDir);
-
-    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("DEBUG");
-  }
-
-  @Test
-  public void createProperties_sets_root_logger_to_process_property_over_global_property_if_both_set() throws IOException {
-    File logDir = temporaryFolder.newFolder();
-    Properties properties = underTest.createProperties(
-      newProps(
-        "sonar.log.level", "DEBUG",
-        "sonar.log.level.es", "TRACE"),
-      logDir);
-
-    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
-  }
-
-  private static Props newProps(String... propertyKeysAndValues) {
-    assertThat(propertyKeysAndValues.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
-    Properties properties = new Properties();
-    for (int i = 0; i < propertyKeysAndValues.length; i++) {
-      properties.put(propertyKeysAndValues[i++], propertyKeysAndValues[i]);
-    }
-    return new Props(properties);
-  }
-
-  private void verifyProperties(Properties properties, String... expectedPropertyKeysAndValuesOrdered) {
-    if (expectedPropertyKeysAndValuesOrdered.length == 0) {
-      assertThat(properties.size()).isEqualTo(0);
-    } else {
-      assertThat(expectedPropertyKeysAndValuesOrdered.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
-      Set<String> keys = new HashSet<>(expectedPropertyKeysAndValuesOrdered.length / 2 + 1);
-      keys.add("status");
-      for (int i = 0; i < expectedPropertyKeysAndValuesOrdered.length; i++) {
-        String key = expectedPropertyKeysAndValuesOrdered[i++];
-        String value = expectedPropertyKeysAndValuesOrdered[i];
-        assertThat(properties.get(key)).describedAs("Unexpected value for property " + key).isEqualTo(value);
-        keys.add(key);
-      }
-      assertThat(properties.keySet()).containsOnly(keys.toArray());
-    }
-  }
-}
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/EsSettingsTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/EsSettingsTest.java
deleted file mode 100644 (file)
index 5b20696..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessProperties;
-import org.sonar.process.Props;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class EsSettingsTest {
-
-  private static final boolean CLUSTER_ENABLED = true;
-  private static final boolean CLUSTER_DISABLED = false;
-  
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Test
-  public void test_default_settings() throws Exception {
-    File homeDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    props.set(ProcessProperties.SEARCH_PORT, "1234");
-    props.set(ProcessProperties.SEARCH_HOST, "127.0.0.1");
-    props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
-    props.set(ProcessProperties.CLUSTER_NAME, "sonarqube");
-
-    EsSettings esSettings = new EsSettings(props);
-
-    Map<String, String> generated = esSettings.build();
-    assertThat(generated.get("transport.tcp.port")).isEqualTo("1234");
-    assertThat(generated.get("transport.host")).isEqualTo("127.0.0.1");
-
-    // no cluster, but cluster and node names are set though
-    assertThat(generated.get("cluster.name")).isEqualTo("sonarqube");
-    assertThat(generated.get("node.name")).isEqualTo("sonarqube");
-
-    assertThat(generated.get("path.data")).isNotNull();
-    assertThat(generated.get("path.logs")).isNotNull();
-    assertThat(generated.get("path.home")).isNull();
-    assertThat(generated.get("path.conf")).isNotNull();
-
-    // http is disabled for security reasons
-    assertThat(generated.get("http.enabled")).isEqualTo("false");
-
-    assertThat(generated.get("discovery.zen.ping.unicast.hosts")).isNull();
-    assertThat(generated.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
-    assertThat(generated.get("discovery.initial_state_timeout")).isEqualTo("30s");
-
-    assertThat(generated.get("action.auto_create_index")).isEqualTo("false");
-  }
-
-  @Test
-  public void override_dirs() throws Exception {
-    File dataDir = temp.newFolder();
-    File logDir = temp.newFolder();
-    File tempDir = temp.newFolder();
-    Props props = minProps(CLUSTER_DISABLED);
-    props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());
-    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
-
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("path.data")).isEqualTo(new File(dataDir, "es").getAbsolutePath());
-    assertThat(settings.get("path.logs")).isEqualTo(logDir.getAbsolutePath());
-    assertThat(settings.get("path.home")).isNull();
-    assertThat(settings.get("path.conf")).isEqualTo(new File(tempDir, "conf/es").getAbsolutePath());
-  }
-
-  @Test
-  public void set_discovery_settings_if_cluster_is_enabled() throws Exception {
-    Props props = minProps(CLUSTER_ENABLED);
-    props.set(ProcessProperties.CLUSTER_SEARCH_HOSTS, "1.2.3.4:9000,1.2.3.5:8080");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
-    assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("2");
-    assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("120s");
-  }
-
-  @Test
-  public void incorrect_values_of_minimum_master_nodes() throws Exception {
-    Props props = minProps(CLUSTER_ENABLED);
-    props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "ꝱꝲꝳପ");
-
-    EsSettings underTest = new EsSettings(props);
-
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Value of property sonar.search.minimumMasterNodes is not an integer:");
-    underTest.build();
-  }
-
-  @Test
-  public void cluster_is_enabled_with_defined_minimum_master_nodes() throws Exception {
-    Props props = minProps(CLUSTER_ENABLED);
-    props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("5");
-  }
-
-  @Test
-  public void cluster_is_enabled_with_defined_initialTimeout() throws Exception {
-    Props props = minProps(CLUSTER_ENABLED);
-    props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("10s");
-  }
-
-  @Test
-  public void in_standalone_initialTimeout_is_not_overridable() throws Exception {
-    Props props = minProps(CLUSTER_DISABLED);
-    props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("30s");
-  }
-
-  @Test
-  public void in_standalone_minimumMasterNodes_is_not_overridable() throws Exception {
-    Props props = minProps(CLUSTER_DISABLED);
-    props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
-  }
-
-
-
-  @Test
-  public void enable_marvel() throws Exception {
-    Props props = minProps(CLUSTER_DISABLED);
-    props.set("sonar.search.marvelHosts", "127.0.0.2,127.0.0.3");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("marvel.agent.exporter.es.hosts")).isEqualTo("127.0.0.2,127.0.0.3");
-  }
-
-  @Test
-  public void enable_http_connector() throws Exception {
-    Props props = minProps(CLUSTER_DISABLED);
-    props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("http.port")).isEqualTo("9010");
-    assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
-    assertThat(settings.get("http.enabled")).isEqualTo("true");
-  }
-
-  @Test
-  public void enable_http_connector_different_host() throws Exception {
-    Props props = minProps(CLUSTER_DISABLED);
-    props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
-    props.set(ProcessProperties.SEARCH_HOST, "127.0.0.2");
-    Map<String, String> settings = new EsSettings(props).build();
-
-    assertThat(settings.get("http.port")).isEqualTo("9010");
-    assertThat(settings.get("http.host")).isEqualTo("127.0.0.2");
-    assertThat(settings.get("http.enabled")).isEqualTo("true");
-  }
-
-  private Props minProps(boolean cluster) throws IOException {
-    File homeDir = temp.newFolder();
-    Props props = new Props(new Properties());
-    ProcessProperties.completeDefaults(props);
-    props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
-    props.set(ProcessProperties.CLUSTER_ENABLED, Boolean.toString(cluster));
-    return props;
-  }
-}
diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/application/process/JavaCommandTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/application/process/JavaCommandTest.java
deleted file mode 100644 (file)
index 4d8d1f8..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.application.process;
-
-import java.io.File;
-import java.util.Properties;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.process.ProcessId;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class JavaCommandTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  @Test
-  public void test_command_with_complete_information() throws Exception {
-    JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
-
-    command.setArgument("first_arg", "val1");
-    Properties args = new Properties();
-    args.setProperty("second_arg", "val2");
-    command.setArguments(args);
-
-    command.setClassName("org.sonar.ElasticSearch");
-    command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
-    File workDir = temp.newFolder();
-    command.setWorkDir(workDir);
-    command.addClasspath("lib/*.jar");
-    command.addClasspath("conf/*.xml");
-    command.addJavaOption("-Xmx128m");
-
-    assertThat(command.toString()).isNotNull();
-    assertThat(command.getClasspath()).containsOnly("lib/*.jar", "conf/*.xml");
-    assertThat(command.getJavaOptions()).containsOnly("-Xmx128m");
-    assertThat(command.getWorkDir()).isSameAs(workDir);
-    assertThat(command.getClassName()).isEqualTo("org.sonar.ElasticSearch");
-
-    // copy current env variables
-    assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
-    assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
-  }
-
-  @Test
-  public void addJavaOptions_adds_jvm_options() {
-    JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
-    assertThat(command.getJavaOptions()).isEmpty();
-
-    command.addJavaOptions("");
-    assertThat(command.getJavaOptions()).isEmpty();
-
-    command.addJavaOptions("-Xmx512m -Xms256m -Dfoo");
-    assertThat(command.getJavaOptions()).containsOnly("-Xmx512m", "-Xms256m", "-Dfoo");
-  }
-}
index d45927b589f1e871a84a39534f7a2d4128059953..044aa7b43104e4fe20d4e8e12d2a2878da26fc7b 100644 (file)
@@ -30,6 +30,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
+import org.sonar.process.command.JavaCommand;
 import org.sonar.process.sharedmemoryfile.AllProcessesCommands;
 import org.sonar.process.ProcessId;
 
diff --git a/server/sonar-process/src/main/java/org/sonar/process/command/AbstractCommand.java b/server/sonar-process/src/main/java/org/sonar/process/command/AbstractCommand.java
new file mode 100644 (file)
index 0000000..83cfc0f
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import javax.annotation.Nullable;
+import org.sonar.process.ProcessId;
+
+public abstract class AbstractCommand<T extends AbstractCommand> {
+  // unique key among the group of commands to launch
+  private final ProcessId id;
+  // program arguments
+  private final Map<String, String> arguments = new LinkedHashMap<>();
+  private final Map<String, String> envVariables = new HashMap<>(System.getenv());
+  private File workDir;
+
+  protected AbstractCommand(ProcessId id) {
+    this.id = id;
+  }
+
+  public ProcessId getProcessId() {
+    return id;
+  }
+
+  public File getWorkDir() {
+    return workDir;
+  }
+
+  public T setWorkDir(File workDir) {
+    this.workDir = workDir;
+    return castThis();
+  }
+
+  @SuppressWarnings("unchecked")
+  private T castThis() {
+    return (T) this;
+  }
+
+  public Map<String, String> getArguments() {
+    return arguments;
+  }
+
+  public T setArgument(String key, @Nullable String value) {
+    if (value == null) {
+      arguments.remove(key);
+    } else {
+      arguments.put(key, value);
+    }
+    return castThis();
+  }
+
+  public T setArguments(Properties args) {
+    for (Map.Entry<Object, Object> entry : args.entrySet()) {
+      setArgument(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString() : null);
+    }
+    return castThis();
+  }
+
+  public Map<String, String> getEnvVariables() {
+    return envVariables;
+  }
+
+  public T setEnvVariable(String key, @Nullable String value) {
+    if (value == null) {
+      envVariables.remove(key);
+    } else {
+      envVariables.put(key, value);
+    }
+    return castThis();
+  }
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/command/CommandFactory.java b/server/sonar-process/src/main/java/org/sonar/process/command/CommandFactory.java
new file mode 100644 (file)
index 0000000..f30537e
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+public interface CommandFactory {
+
+  EsCommand createEsCommand();
+
+  JavaCommand createWebCommand(boolean leader);
+
+  JavaCommand createCeCommand();
+
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/command/CommandFactoryImpl.java b/server/sonar-process/src/main/java/org/sonar/process/command/CommandFactoryImpl.java
new file mode 100644 (file)
index 0000000..24995b8
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Optional;
+import org.sonar.process.ProcessId;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+import org.sonar.process.es.EsLogging;
+import org.sonar.process.es.EsSettings;
+
+import static org.sonar.process.ProcessProperties.HTTPS_PROXY_HOST;
+import static org.sonar.process.ProcessProperties.HTTPS_PROXY_PORT;
+import static org.sonar.process.ProcessProperties.HTTP_PROXY_HOST;
+import static org.sonar.process.ProcessProperties.HTTP_PROXY_PORT;
+
+public class CommandFactoryImpl implements CommandFactory {
+  /**
+   * Properties about proxy that must be set as system properties
+   */
+  private static final String[] PROXY_PROPERTY_KEYS = new String[] {
+    HTTP_PROXY_HOST,
+    HTTP_PROXY_PORT,
+    "http.nonProxyHosts",
+    HTTPS_PROXY_HOST,
+    HTTPS_PROXY_PORT,
+    "http.auth.ntlm.domain",
+    "socksProxyHost",
+    "socksProxyPort"};
+
+  private final Props props;
+
+  public CommandFactoryImpl(Props props) {
+    this.props = props;
+  }
+
+  @Override
+  public EsCommand createEsCommand() {
+    File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+    File executable = new File(homeDir, getExecutable());
+    if (!executable.exists()) {
+      throw new IllegalStateException("Cannot find elasticsearch binary");
+    }
+
+    Map<String, String> settingsMap = new EsSettings(props).build();
+
+    File logDir = new File(settingsMap.get("path.logs"));
+    File confDir = new File(settingsMap.get("path.conf"));
+    EsCommand res = new EsCommand(ProcessId.ELASTICSEARCH)
+      .setWorkDir(executable.getParentFile().getParentFile())
+      .setExecutable(executable)
+      .setConfDir(confDir)
+      .setLog4j2Properties(new EsLogging().createProperties(props, logDir))
+      .setArguments(props.rawProperties())
+      .setClusterName(settingsMap.get("cluster.name"))
+      .setHost(settingsMap.get("network.host"))
+      .setPort(Integer.valueOf(settingsMap.get("transport.tcp.port")))
+      .addJvmOption(props.nonNullValue(ProcessProperties.SEARCH_JAVA_OPTS))
+      .addJvmOption(props.nonNullValue(ProcessProperties.SEARCH_JAVA_ADDITIONAL_OPTS))
+      .setEnvVariable("JAVA_HOME", System.getProperties().getProperty("java.home"));
+
+    settingsMap.forEach((key, value) -> res.addEsOption("-E" + key + "=" + value));
+
+    return res;
+  }
+
+  private static String getExecutable() {
+    if (System.getProperty("os.name").startsWith("Windows")) {
+      return "elasticsearch/bin/elasticsearch.bat";
+    }
+    return "elasticsearch/bin/elasticsearch";
+  }
+
+  @Override
+  public JavaCommand createWebCommand(boolean leader) {
+    File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+    JavaCommand command = newJavaCommand(ProcessId.WEB_SERVER, homeDir)
+      .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS)
+      .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_OPTS))
+      .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_ADDITIONAL_OPTS))
+      // required for logback tomcat valve
+      .setEnvVariable(ProcessProperties.PATH_LOGS, props.nonNullValue(ProcessProperties.PATH_LOGS))
+      .setArgument("sonar.cluster.web.startupLeader", Boolean.toString(leader))
+      .setClassName("org.sonar.server.app.WebServer")
+      .addClasspath("./lib/common/*")
+      .addClasspath("./lib/server/*");
+    String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
+    if (driverPath != null) {
+      command.addClasspath(driverPath);
+    }
+    return command;
+  }
+
+  @Override
+  public JavaCommand createCeCommand() {
+    File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+    JavaCommand command = newJavaCommand(ProcessId.COMPUTE_ENGINE, homeDir)
+      .addJavaOptions(ProcessProperties.CE_ENFORCED_JVM_ARGS)
+      .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_OPTS))
+      .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_ADDITIONAL_OPTS))
+      .setClassName("org.sonar.ce.app.CeServer")
+      .addClasspath("./lib/common/*")
+      .addClasspath("./lib/server/*")
+      .addClasspath("./lib/ce/*");
+    String driverPath = props.value(ProcessProperties.JDBC_DRIVER_PATH);
+    if (driverPath != null) {
+      command.addClasspath(driverPath);
+    }
+    return command;
+  }
+
+  private JavaCommand newJavaCommand(ProcessId id, File homeDir) {
+    JavaCommand command = new JavaCommand(id)
+      .setWorkDir(homeDir)
+      .setArguments(props.rawProperties());
+
+    for (String key : PROXY_PROPERTY_KEYS) {
+      getPropsValue(key).ifPresent(val -> command.addJavaOption("-D" + key + "=" + val));
+    }
+
+    // defaults of HTTPS are the same than HTTP defaults
+    setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_HOST, HTTP_PROXY_HOST);
+    setSystemPropertyToDefaultIfNotSet(command, HTTPS_PROXY_PORT, HTTP_PROXY_PORT);
+    return command;
+  }
+
+  private void setSystemPropertyToDefaultIfNotSet(JavaCommand command,
+    String httpsProperty, String httpProperty) {
+    Optional<String> httpValue = getPropsValue(httpProperty);
+    Optional<String> httpsValue = getPropsValue(httpsProperty);
+    if (!httpsValue.isPresent() && httpValue.isPresent()) {
+      command.addJavaOption("-D" + httpsProperty + "=" + httpValue.get());
+    }
+  }
+
+  private Optional<String> getPropsValue(String key) {
+    return Optional.ofNullable(props.value(key));
+  }
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/command/EsCommand.java b/server/sonar-process/src/main/java/org/sonar/process/command/EsCommand.java
new file mode 100644 (file)
index 0000000..cd6877d
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import org.sonar.process.ProcessId;
+
+public class EsCommand extends AbstractCommand<EsCommand> {
+  private File executable;
+  private File confDir;
+  private String clusterName;
+  private String host;
+  private int port;
+  private Properties log4j2Properties;
+  private List<String> esOptions = new ArrayList<>();
+  private List<String> jvmOptions = new ArrayList<>();
+
+  public EsCommand(ProcessId id) {
+    super(id);
+  }
+
+  public File getExecutable() {
+    return executable;
+  }
+
+  public EsCommand setExecutable(File executable) {
+    this.executable = executable;
+    return this;
+  }
+
+  public File getConfDir() {
+    return confDir;
+  }
+
+  public EsCommand setConfDir(File confDir) {
+    this.confDir = confDir;
+    return this;
+  }
+
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public EsCommand setClusterName(String clusterName) {
+    this.clusterName = clusterName;
+    return this;
+  }
+
+  public String getHost() {
+    return host;
+  }
+
+  public EsCommand setHost(String host) {
+    this.host = host;
+    return this;
+  }
+
+  public int getPort() {
+    return port;
+  }
+
+  public EsCommand setPort(int port) {
+    this.port = port;
+    return this;
+  }
+
+  public Properties getLog4j2Properties() {
+    return log4j2Properties;
+  }
+
+  public EsCommand setLog4j2Properties(Properties log4j2Properties) {
+    this.log4j2Properties = log4j2Properties;
+    return this;
+  }
+
+  public List<String> getEsOptions() {
+    return esOptions;
+  }
+
+  public EsCommand addEsOption(String s) {
+    if (!s.isEmpty()) {
+      esOptions.add(s);
+    }
+    return this;
+  }
+
+  public List<String> getJvmOptions() {
+    return jvmOptions;
+  }
+
+  public EsCommand addJvmOption(String s) {
+    if (!s.isEmpty()) {
+      jvmOptions.add(s);
+    }
+    return this;
+  }
+
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/command/JavaCommand.java b/server/sonar-process/src/main/java/org/sonar/process/command/JavaCommand.java
new file mode 100644 (file)
index 0000000..253c629
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.sonar.process.ProcessId;
+
+public class JavaCommand extends AbstractCommand<JavaCommand> {
+  // entry point
+  private String className;
+  // for example -Xmx1G
+  private final List<String> javaOptions = new ArrayList<>();
+  // relative path to JAR files
+  private final List<String> classpath = new ArrayList<>();
+
+  public JavaCommand(ProcessId id) {
+    super(id);
+  }
+
+  public List<String> getJavaOptions() {
+    return javaOptions;
+  }
+
+  public JavaCommand addJavaOption(String s) {
+    if (!s.isEmpty()) {
+      javaOptions.add(s);
+    }
+    return this;
+  }
+
+  public JavaCommand addJavaOptions(String s) {
+    for (String opt : s.split(" ")) {
+      addJavaOption(opt);
+    }
+    return this;
+  }
+
+  public String getClassName() {
+    return className;
+  }
+
+  public JavaCommand setClassName(String className) {
+    this.className = className;
+    return this;
+  }
+
+  public List<String> getClasspath() {
+    return classpath;
+  }
+
+  public JavaCommand addClasspath(String s) {
+    classpath.add(s);
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("JavaCommand{");
+    sb.append("workDir=").append(getWorkDir());
+    sb.append(", javaOptions=").append(javaOptions);
+    sb.append(", className='").append(className).append('\'');
+    sb.append(", classpath=").append(classpath);
+    sb.append(", arguments=").append(getArguments());
+    sb.append(", envVariables=").append(getEnvVariables());
+    sb.append('}');
+    return sb.toString();
+  }
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/command/package-info.java b/server/sonar-process/src/main/java/org/sonar/process/command/package-info.java
new file mode 100644 (file)
index 0000000..248864d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.process.command;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-process/src/main/java/org/sonar/process/es/EsLogging.java b/server/sonar-process/src/main/java/org/sonar/process/es/EsLogging.java
new file mode 100644 (file)
index 0000000..b5a6727
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.es;
+
+import ch.qos.logback.classic.Level;
+import java.io.File;
+import java.util.Properties;
+import org.sonar.process.ProcessId;
+import org.sonar.process.Props;
+import org.sonar.process.logging.Log4JPropertiesBuilder;
+import org.sonar.process.logging.LogLevelConfig;
+import org.sonar.process.logging.RootLoggerConfig;
+
+import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
+
+public class EsLogging {
+
+  public Properties createProperties(Props props, File logDir) {
+    Log4JPropertiesBuilder log4JPropertiesBuilder = new Log4JPropertiesBuilder(props);
+    RootLoggerConfig config = newRootLoggerConfigBuilder().setProcessId(ProcessId.ELASTICSEARCH).build();
+    String logPattern = log4JPropertiesBuilder.buildLogPattern(config);
+
+    log4JPropertiesBuilder.internalLogLevel(Level.ERROR);
+    log4JPropertiesBuilder.configureGlobalFileLog(config, logDir, logPattern);
+    log4JPropertiesBuilder.apply(
+      LogLevelConfig.newBuilder(log4JPropertiesBuilder.getRootLoggerName())
+        .rootLevelFor(ProcessId.ELASTICSEARCH)
+        .build());
+
+    return log4JPropertiesBuilder.get();
+  }
+
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/es/EsSettings.java b/server/sonar-process/src/main/java/org/sonar/process/es/EsSettings.java
new file mode 100644 (file)
index 0000000..ae58355
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.es;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static java.lang.String.valueOf;
+
+public class EsSettings {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(EsSettings.class);
+  private static final String PROP_MARVEL_HOSTS = "sonar.search.marvelHosts";
+  private static final String CLUSTER_SEARCH_NODE_NAME = "sonar.cluster.search.nodeName";
+  private static final String STANDALONE_NODE_NAME = "sonarqube";
+
+  private final Props props;
+
+  private final boolean clusterEnabled;
+  private final String clusterName;
+  private final String nodeName;
+
+  public EsSettings(Props props) {
+    this.props = props;
+
+    this.clusterName = props.nonNullValue(ProcessProperties.CLUSTER_NAME);
+    this.clusterEnabled = props.valueAsBoolean(ProcessProperties.CLUSTER_ENABLED);
+    if (this.clusterEnabled) {
+      this.nodeName = props.value(CLUSTER_SEARCH_NODE_NAME, "sonarqube-" + UUID.randomUUID().toString());
+    } else {
+      this.nodeName = STANDALONE_NODE_NAME;
+    }
+  }
+
+  public Map<String, String> build() {
+    Map<String, String> builder = new HashMap<>();
+    configureFileSystem(builder);
+    configureNetwork(builder);
+    configureCluster(builder);
+    configureMarvel(builder);
+    configureAction(builder);
+    return builder;
+  }
+
+  private void configureFileSystem(Map<String, String> builder) {
+    File homeDir = props.nonNullValueAsFile(ProcessProperties.PATH_HOME);
+
+    builder.put("path.data", buildDataPath(homeDir).getAbsolutePath());
+    builder.put("path.conf", buildConfDir().getAbsolutePath());
+    builder.put("path.logs", buildLogPath(homeDir).getAbsolutePath());
+  }
+
+  private File buildDataPath(File homeDir) {
+    String dataPath = props.value(ProcessProperties.PATH_DATA);
+    if (StringUtils.isNotEmpty(dataPath)) {
+      return new File(dataPath, "es");
+    }
+    return new File(homeDir, "data/es");
+  }
+
+  private File buildLogPath(File homeDir) {
+    String logPath = props.value(ProcessProperties.PATH_LOGS);
+    if (StringUtils.isNotEmpty(logPath)) {
+      return new File(logPath);
+    }
+    return new File(homeDir, "log");
+  }
+
+  private File buildConfDir() {
+    String tempPath = props.value(ProcessProperties.PATH_TEMP);
+    return new File(new File(tempPath, "conf"), "es");
+  }
+
+  private void configureNetwork(Map<String, String> builder) {
+    InetAddress host = readHost();
+    int port = Integer.parseInt(props.nonNullValue(ProcessProperties.SEARCH_PORT));
+    LOGGER.info("Elasticsearch listening on {}:{}", host, port);
+
+    builder.put("transport.tcp.port", valueOf(port));
+    builder.put("transport.host", valueOf(host.getHostAddress()));
+    builder.put("network.host", valueOf(host.getHostAddress()));
+
+    // Elasticsearch sets the default value of TCP reuse address to true only on non-MSWindows machines, but why ?
+    builder.put("network.tcp.reuse_address", valueOf(true));
+
+    int httpPort = props.valueAsInt(ProcessProperties.SEARCH_HTTP_PORT, -1);
+    if (httpPort < 0) {
+      // standard configuration
+      builder.put("http.enabled", valueOf(false));
+    } else {
+      LOGGER.warn("Elasticsearch HTTP connector is enabled on port {}. MUST NOT BE USED FOR PRODUCTION", httpPort);
+      // see https://github.com/lmenezes/elasticsearch-kopf/issues/195
+      builder.put("http.cors.enabled", valueOf(true));
+      builder.put("http.cors.allow-origin", "*");
+      builder.put("http.enabled", valueOf(true));
+      builder.put("http.host", host.getHostAddress());
+      builder.put("http.port", valueOf(httpPort));
+    }
+  }
+
+  private InetAddress readHost() {
+    String hostProperty = props.nonNullValue(ProcessProperties.SEARCH_HOST);
+    try {
+      return InetAddress.getByName(hostProperty);
+    } catch (UnknownHostException e) {
+      throw new IllegalStateException("Can not resolve host [" + hostProperty + "]. Please check network settings and property " + ProcessProperties.SEARCH_HOST, e);
+    }
+  }
+
+  private void configureCluster(Map<String, String> builder) {
+    // Default value in a standalone mode, not overridable
+
+    int minimumMasterNodes = 1;
+    String initialStateTimeOut = "30s";
+
+    if (clusterEnabled) {
+      minimumMasterNodes = props.valueAsInt(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, 2);
+      initialStateTimeOut = props.value(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "120s");
+
+      String hosts = props.value(ProcessProperties.CLUSTER_SEARCH_HOSTS, "");
+      LOGGER.info("Elasticsearch cluster enabled. Connect to hosts [{}]", hosts);
+      builder.put("discovery.zen.ping.unicast.hosts", hosts);
+    }
+
+    builder.put("discovery.zen.minimum_master_nodes", valueOf(minimumMasterNodes));
+    builder.put("discovery.initial_state_timeout", initialStateTimeOut);
+    builder.put("cluster.name", clusterName);
+    builder.put("cluster.routing.allocation.awareness.attributes", "rack_id");
+    builder.put("node.attr.rack_id", nodeName);
+    builder.put("node.name", nodeName);
+    builder.put("node.data", valueOf(true));
+    builder.put("node.master", valueOf(true));
+  }
+
+  private void configureMarvel(Map<String, String> builder) {
+    Set<String> marvels = new TreeSet<>();
+    marvels.addAll(Arrays.asList(StringUtils.split(props.value(PROP_MARVEL_HOSTS, ""), ",")));
+
+    // If we're collecting indexing data send them to the Marvel host(s)
+    if (!marvels.isEmpty()) {
+      String hosts = StringUtils.join(marvels, ",");
+      LOGGER.info("Elasticsearch Marvel is enabled for %s", hosts);
+      builder.put("marvel.agent.exporter.es.hosts", hosts);
+    }
+  }
+
+  private static void configureAction(Map<String, String> builder) {
+    builder.put("action.auto_create_index", String.valueOf(false));
+  }
+}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/es/package-info.java b/server/sonar-process/src/main/java/org/sonar/process/es/package-info.java
new file mode 100644 (file)
index 0000000..35e9b4a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.process.es;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-process/src/test/java/org/sonar/process/command/AbstractCommandTest.java b/server/sonar-process/src/test/java/org/sonar/process/command/AbstractCommandTest.java
new file mode 100644 (file)
index 0000000..7ddb9c5
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+import java.io.File;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessId;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AbstractCommandTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Test
+  public void test_command_with_complete_information() throws Exception {
+    AbstractCommand command = new AbstractCommand(ProcessId.ELASTICSEARCH) {
+
+    };
+
+    command.setArgument("first_arg", "val1");
+    Properties args = new Properties();
+    args.setProperty("second_arg", "val2");
+    command.setArguments(args);
+
+    command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
+    File workDir = temp.newFolder();
+    command.setWorkDir(workDir);
+
+    assertThat(command.toString()).isNotNull();
+    assertThat(command.getWorkDir()).isSameAs(workDir);
+
+    // copy current env variables
+    assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
+    assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
+  }
+
+}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/command/JavaCommandTest.java b/server/sonar-process/src/test/java/org/sonar/process/command/JavaCommandTest.java
new file mode 100644 (file)
index 0000000..96d266a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.command;
+
+import java.io.File;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessId;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class JavaCommandTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Test
+  public void test_command_with_complete_information() throws Exception {
+    JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+
+    command.setArgument("first_arg", "val1");
+    Properties args = new Properties();
+    args.setProperty("second_arg", "val2");
+    command.setArguments(args);
+
+    command.setClassName("org.sonar.ElasticSearch");
+    command.setEnvVariable("JAVA_COMMAND_TEST", "1000");
+    File workDir = temp.newFolder();
+    command.setWorkDir(workDir);
+    command.addClasspath("lib/*.jar");
+    command.addClasspath("conf/*.xml");
+    command.addJavaOption("-Xmx128m");
+
+    assertThat(command.toString()).isNotNull();
+    assertThat(command.getClasspath()).containsOnly("lib/*.jar", "conf/*.xml");
+    assertThat(command.getJavaOptions()).containsOnly("-Xmx128m");
+    assertThat(command.getWorkDir()).isSameAs(workDir);
+    assertThat(command.getClassName()).isEqualTo("org.sonar.ElasticSearch");
+
+    // copy current env variables
+    assertThat(command.getEnvVariables().get("JAVA_COMMAND_TEST")).isEqualTo("1000");
+    assertThat(command.getEnvVariables().size()).isEqualTo(System.getenv().size() + 1);
+  }
+
+  @Test
+  public void addJavaOptions_adds_jvm_options() {
+    JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH);
+    assertThat(command.getJavaOptions()).isEmpty();
+
+    command.addJavaOptions("");
+    assertThat(command.getJavaOptions()).isEmpty();
+
+    command.addJavaOptions("-Xmx512m -Xms256m -Dfoo");
+    assertThat(command.getJavaOptions()).containsOnly("-Xmx512m", "-Xms256m", "-Dfoo");
+  }
+}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/es/EsLoggingTest.java b/server/sonar-process/src/test/java/org/sonar/process/es/EsLoggingTest.java
new file mode 100644 (file)
index 0000000..4f511ef
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.es;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.Props;
+import org.sonar.process.es.EsLogging;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EsLoggingTest {
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  private EsLogging underTest = new EsLogging();
+
+  @Test
+  public void createProperties_with_empty_props() throws IOException {
+    File logDir = temporaryFolder.newFolder();
+    Properties properties = underTest.createProperties(newProps(), logDir);
+
+    verifyProperties(properties,
+      "status", "ERROR",
+      "appender.file_es.type", "RollingFile",
+      "appender.file_es.name", "file_es",
+      "appender.file_es.filePattern", new File(logDir, "es.%d{yyyy-MM-dd}.log").getAbsolutePath(),
+      "appender.file_es.fileName", new File(logDir, "es.log").getAbsolutePath(),
+      "appender.file_es.layout.type", "PatternLayout",
+      "appender.file_es.layout.pattern", "%d{yyyy.MM.dd HH:mm:ss} %-5level es[][%logger{1.}] %msg%n",
+      "appender.file_es.policies.type", "Policies",
+      "appender.file_es.policies.time.type", "TimeBasedTriggeringPolicy",
+      "appender.file_es.policies.time.interval", "1",
+      "appender.file_es.policies.time.modulate", "true",
+      "appender.file_es.strategy.type", "DefaultRolloverStrategy",
+      "appender.file_es.strategy.fileIndex", "nomax",
+      "appender.file_es.strategy.action.type", "Delete",
+      "appender.file_es.strategy.action.basepath", logDir.getAbsolutePath(),
+      "appender.file_es.strategy.action.maxDepth", "1",
+      "appender.file_es.strategy.action.condition.type", "IfFileName",
+      "appender.file_es.strategy.action.condition.glob", "es*",
+      "appender.file_es.strategy.action.condition.nested_condition.type", "IfAccumulatedFileCount",
+      "appender.file_es.strategy.action.condition.nested_condition.exceeds", "7",
+      "rootLogger.level", "INFO",
+      "rootLogger.appenderRef.file_es.ref", "file_es");
+  }
+
+  @Test
+  public void createProperties_sets_root_logger_to_INFO_if_no_property_is_set() throws IOException {
+    File logDir = temporaryFolder.newFolder();
+    Properties properties = underTest.createProperties(newProps(), logDir);
+
+    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("INFO");
+  }
+
+  @Test
+  public void createProperties_sets_root_logger_to_global_property_if_set() throws IOException {
+    File logDir = temporaryFolder.newFolder();
+    Properties properties = underTest.createProperties(newProps("sonar.log.level", "TRACE"), logDir);
+
+    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
+  }
+
+  @Test
+  public void createProperties_sets_root_logger_to_process_property_if_set() throws IOException {
+    File logDir = temporaryFolder.newFolder();
+    Properties properties = underTest.createProperties(newProps("sonar.log.level.es", "DEBUG"), logDir);
+
+    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("DEBUG");
+  }
+
+  @Test
+  public void createProperties_sets_root_logger_to_process_property_over_global_property_if_both_set() throws IOException {
+    File logDir = temporaryFolder.newFolder();
+    Properties properties = underTest.createProperties(
+      newProps(
+        "sonar.log.level", "DEBUG",
+        "sonar.log.level.es", "TRACE"),
+      logDir);
+
+    assertThat(properties.getProperty("rootLogger.level")).isEqualTo("TRACE");
+  }
+
+  private static Props newProps(String... propertyKeysAndValues) {
+    assertThat(propertyKeysAndValues.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
+    Properties properties = new Properties();
+    for (int i = 0; i < propertyKeysAndValues.length; i++) {
+      properties.put(propertyKeysAndValues[i++], propertyKeysAndValues[i]);
+    }
+    return new Props(properties);
+  }
+
+  private void verifyProperties(Properties properties, String... expectedPropertyKeysAndValuesOrdered) {
+    if (expectedPropertyKeysAndValuesOrdered.length == 0) {
+      assertThat(properties.size()).isEqualTo(0);
+    } else {
+      assertThat(expectedPropertyKeysAndValuesOrdered.length % 2).describedAs("Number of parameters must be even").isEqualTo(0);
+      Set<String> keys = new HashSet<>(expectedPropertyKeysAndValuesOrdered.length / 2 + 1);
+      keys.add("status");
+      for (int i = 0; i < expectedPropertyKeysAndValuesOrdered.length; i++) {
+        String key = expectedPropertyKeysAndValuesOrdered[i++];
+        String value = expectedPropertyKeysAndValuesOrdered[i];
+        assertThat(properties.get(key)).describedAs("Unexpected value for property " + key).isEqualTo(value);
+        keys.add(key);
+      }
+      assertThat(properties.keySet()).containsOnly(keys.toArray());
+    }
+  }
+}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/es/EsSettingsTest.java b/server/sonar-process/src/test/java/org/sonar/process/es/EsSettingsTest.java
new file mode 100644 (file)
index 0000000..f58ce46
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.process.es;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.process.ProcessProperties;
+import org.sonar.process.Props;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EsSettingsTest {
+
+  private static final boolean CLUSTER_ENABLED = true;
+  private static final boolean CLUSTER_DISABLED = false;
+  
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Test
+  public void test_default_settings() throws Exception {
+    File homeDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    props.set(ProcessProperties.SEARCH_PORT, "1234");
+    props.set(ProcessProperties.SEARCH_HOST, "127.0.0.1");
+    props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
+    props.set(ProcessProperties.CLUSTER_NAME, "sonarqube");
+
+    EsSettings esSettings = new EsSettings(props);
+
+    Map<String, String> generated = esSettings.build();
+    assertThat(generated.get("transport.tcp.port")).isEqualTo("1234");
+    assertThat(generated.get("transport.host")).isEqualTo("127.0.0.1");
+
+    // no cluster, but cluster and node names are set though
+    assertThat(generated.get("cluster.name")).isEqualTo("sonarqube");
+    assertThat(generated.get("node.name")).isEqualTo("sonarqube");
+
+    assertThat(generated.get("path.data")).isNotNull();
+    assertThat(generated.get("path.logs")).isNotNull();
+    assertThat(generated.get("path.home")).isNull();
+    assertThat(generated.get("path.conf")).isNotNull();
+
+    // http is disabled for security reasons
+    assertThat(generated.get("http.enabled")).isEqualTo("false");
+
+    assertThat(generated.get("discovery.zen.ping.unicast.hosts")).isNull();
+    assertThat(generated.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
+    assertThat(generated.get("discovery.initial_state_timeout")).isEqualTo("30s");
+
+    assertThat(generated.get("action.auto_create_index")).isEqualTo("false");
+  }
+
+  @Test
+  public void override_dirs() throws Exception {
+    File dataDir = temp.newFolder();
+    File logDir = temp.newFolder();
+    File tempDir = temp.newFolder();
+    Props props = minProps(CLUSTER_DISABLED);
+    props.set(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath());
+    props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath());
+
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("path.data")).isEqualTo(new File(dataDir, "es").getAbsolutePath());
+    assertThat(settings.get("path.logs")).isEqualTo(logDir.getAbsolutePath());
+    assertThat(settings.get("path.home")).isNull();
+    assertThat(settings.get("path.conf")).isEqualTo(new File(tempDir, "conf/es").getAbsolutePath());
+  }
+
+  @Test
+  public void set_discovery_settings_if_cluster_is_enabled() throws Exception {
+    Props props = minProps(CLUSTER_ENABLED);
+    props.set(ProcessProperties.CLUSTER_SEARCH_HOSTS, "1.2.3.4:9000,1.2.3.5:8080");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isEqualTo("1.2.3.4:9000,1.2.3.5:8080");
+    assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("2");
+    assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("120s");
+  }
+
+  @Test
+  public void incorrect_values_of_minimum_master_nodes() throws Exception {
+    Props props = minProps(CLUSTER_ENABLED);
+    props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "ꝱꝲꝳପ");
+
+    EsSettings underTest = new EsSettings(props);
+
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage("Value of property sonar.search.minimumMasterNodes is not an integer:");
+    underTest.build();
+  }
+
+  @Test
+  public void cluster_is_enabled_with_defined_minimum_master_nodes() throws Exception {
+    Props props = minProps(CLUSTER_ENABLED);
+    props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("5");
+  }
+
+  @Test
+  public void cluster_is_enabled_with_defined_initialTimeout() throws Exception {
+    Props props = minProps(CLUSTER_ENABLED);
+    props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("10s");
+  }
+
+  @Test
+  public void in_standalone_initialTimeout_is_not_overridable() throws Exception {
+    Props props = minProps(CLUSTER_DISABLED);
+    props.set(ProcessProperties.SEARCH_INITIAL_STATE_TIMEOUT, "10s");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("discovery.initial_state_timeout")).isEqualTo("30s");
+  }
+
+  @Test
+  public void in_standalone_minimumMasterNodes_is_not_overridable() throws Exception {
+    Props props = minProps(CLUSTER_DISABLED);
+    props.set(ProcessProperties.SEARCH_MINIMUM_MASTER_NODES, "5");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("discovery.zen.minimum_master_nodes")).isEqualTo("1");
+  }
+
+
+
+  @Test
+  public void enable_marvel() throws Exception {
+    Props props = minProps(CLUSTER_DISABLED);
+    props.set("sonar.search.marvelHosts", "127.0.0.2,127.0.0.3");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("marvel.agent.exporter.es.hosts")).isEqualTo("127.0.0.2,127.0.0.3");
+  }
+
+  @Test
+  public void enable_http_connector() throws Exception {
+    Props props = minProps(CLUSTER_DISABLED);
+    props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("http.port")).isEqualTo("9010");
+    assertThat(settings.get("http.host")).isEqualTo("127.0.0.1");
+    assertThat(settings.get("http.enabled")).isEqualTo("true");
+  }
+
+  @Test
+  public void enable_http_connector_different_host() throws Exception {
+    Props props = minProps(CLUSTER_DISABLED);
+    props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010");
+    props.set(ProcessProperties.SEARCH_HOST, "127.0.0.2");
+    Map<String, String> settings = new EsSettings(props).build();
+
+    assertThat(settings.get("http.port")).isEqualTo("9010");
+    assertThat(settings.get("http.host")).isEqualTo("127.0.0.2");
+    assertThat(settings.get("http.enabled")).isEqualTo("true");
+  }
+
+  private Props minProps(boolean cluster) throws IOException {
+    File homeDir = temp.newFolder();
+    Props props = new Props(new Properties());
+    ProcessProperties.completeDefaults(props);
+    props.set(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath());
+    props.set(ProcessProperties.CLUSTER_ENABLED, Boolean.toString(cluster));
+    return props;
+  }
+}
index 69098a0a7560d3a2ad6b7c8842301b5bc8f31fc9..b2010900e58cec1c1bc8fb46ccc3e572c2e9316e 100644 (file)
@@ -23,8 +23,8 @@ import java.io.IOException;
 import org.sonar.application.config.AppSettings;
 import org.sonar.application.config.AppSettingsLoader;
 import org.sonar.application.config.AppSettingsLoaderImpl;
-import org.sonar.application.process.CommandFactory;
-import org.sonar.application.process.CommandFactoryImpl;
+import org.sonar.process.command.CommandFactory;
+import org.sonar.process.command.CommandFactoryImpl;
 import org.sonar.application.process.ProcessLauncher;
 import org.sonar.application.process.ProcessLauncherImpl;
 import org.sonar.application.process.StopRequestWatcher;
@@ -49,7 +49,7 @@ public class App {
     try (AppState appState = new AppStateFactory(settings).create()) {
       appState.registerSonarQubeVersion(getSonarqubeVersion());
       AppReloader appReloader = new AppReloaderImpl(settingsLoader, fileSystem, appState, logging);
-      CommandFactory commandFactory = new CommandFactoryImpl(settings);
+      CommandFactory commandFactory = new CommandFactoryImpl(settings.getProps());
       fileSystem.reset();
 
       try (ProcessLauncher processLauncher = new ProcessLauncherImpl(fileSystem.getTempDir())) {