Browse Source

Experimental fork

tags/4.5-RC1
Simon Brandhof 10 years ago
parent
commit
313c75ea72
30 changed files with 1043 additions and 119 deletions
  1. 1
    0
      server/pom.xml
  2. 8
    11
      server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java
  3. 4
    0
      server/sonar-process/src/main/java/org/sonar/process/Props.java
  4. 6
    20
      server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java
  5. 2
    32
      server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java
  6. 61
    0
      server/sonar-server-app/pom.xml
  7. 167
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/Connectors.java
  8. 130
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/EmbeddedTomcat.java
  9. 69
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/Env.java
  10. 81
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/Logging.java
  11. 36
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/NullJarScanner.java
  12. 52
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/ServerProcess.java
  13. 77
    0
      server/sonar-server-app/src/main/java/org/sonar/server/app/Webapp.java
  14. 2
    25
      server/sonar-server/pom.xml
  15. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java
  16. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
  17. 21
    10
      server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java
  18. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
  19. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
  20. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
  21. 7
    6
      server/sonar-web/pom.xml
  22. 11
    1
      sonar-application/assembly.xml
  23. 13
    1
      sonar-application/pom.xml
  24. 2
    2
      sonar-application/src/main/assembly/conf/sonar.properties
  25. 4
    4
      sonar-application/src/main/assembly/conf/wrapper.conf
  26. 7
    1
      sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java
  27. 104
    0
      sonar-application/src/main/java/org/sonar/application/ForkProcesses.java
  28. 161
    0
      sonar-application/src/main/java/org/sonar/application/Installation.java
  29. 1
    0
      sonar-application/src/main/java/org/sonar/application/Webapp.java
  30. 11
    1
      sonar-application/src/main/resources/logback.xml

+ 1
- 0
server/pom.xml View File

@@ -14,6 +14,7 @@
<module>sonar-process</module>
<module>sonar-search</module>
<module>sonar-server</module>
<module>sonar-server-app</module>
<module>sonar-web</module>
<module>sonar-ws-client</module>
</modules>

+ 8
- 11
server/sonar-process/src/main/java/org/sonar/process/ProcessWrapper.java View File

@@ -103,8 +103,8 @@ public class ProcessWrapper extends Thread {
LOGGER.info("Try #{} to connect to JMX server for process '{}'", i, name);
try {
String protocol = "rmi";
String path = "/jndi/rmi://" + InetAddress.getLocalHost().getHostName() + ":" + port + "/jmxrmi";
JMXServiceURL jmxUrl = new JMXServiceURL(protocol, InetAddress.getLocalHost().getHostName(), port, path);
String path = "/jndi/rmi://" + InetAddress.getLocalHost().getHostAddress() + ":" + port + "/jmxrmi";
JMXServiceURL jmxUrl = new JMXServiceURL(protocol, InetAddress.getLocalHost().getHostAddress(), port, path);
JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxUrl, null);
MBeanServerConnection mBeanServer = jmxConnector.getMBeanServerConnection();
ProcessMXBean bean = JMX.newMBeanProxy(mBeanServer, Process.objectNameFor(name), ProcessMXBean.class);
@@ -147,7 +147,7 @@ public class ProcessWrapper extends Thread {
}

private List<String> getJMXOptions() {
return ImmutableList.<String>of(
return ImmutableList.of(
"-Dcom.sun.management.jmxremote",
"-Dcom.sun.management.jmxremote.port=" + this.port,
"-Dcom.sun.management.jmxremote.authenticate=false",
@@ -155,18 +155,13 @@ public class ProcessWrapper extends Thread {
}

private List<String> getClassPath() {
String separator = System.getProperty("file.separator");
return ImmutableList.<String>of(
"-cp",
StringUtils.join(classPath, separator));
// java specification : "multiple path entries are separated by semi-colons", not by
// system file separator,
return ImmutableList.of("-cp", StringUtils.join(classPath, ";"));
}

private String getPropertyFile() {
File propertyFile = new File(FileUtils.getTempDirectory(), UUID.randomUUID().toString());
// if (!propertyFile.canWrite()) {
// throw new IllegalStateException("Cannot write temp propertyFile to '" +
// propertyFile.getAbsolutePath() + "'");
// }
try {
Properties props = new Properties();
for (Map.Entry<String, String> property : properties.entrySet()) {
@@ -189,6 +184,7 @@ public class ProcessWrapper extends Thread {
LOGGER.info("ProcessWrapper::executeProcess() START");

ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.environment().put("SONAR_HOME", workDir);
processBuilder.command().add(getJavaCommand());

if (!StringUtils.isEmpty(javaOpts)) {
@@ -199,6 +195,7 @@ public class ProcessWrapper extends Thread {
}
processBuilder.command().addAll(getJMXOptions());
processBuilder.command().addAll(getClassPath());

processBuilder.command().add(className);
processBuilder.command().add(getPropertyFile());


+ 4
- 0
server/sonar-process/src/main/java/org/sonar/process/Props.java View File

@@ -67,6 +67,10 @@ public class Props {
return i == null ? defaultValue : i;
}

public Properties properties() {
return props;
}

public static Props create(Properties properties) {
Properties p = new Properties();


+ 6
- 20
server/sonar-search/src/main/java/org/sonar/search/ElasticSearch.java View File

@@ -25,23 +25,18 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.process.Process;
import org.sonar.process.Props;
import org.sonar.search.script.ListUpdate;

public class ElasticSearch extends Process {
import java.io.File;

private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearch.class);
public class ElasticSearch extends Process {

public static final String ES_DEBUG_PROPERTY = "esDebug";
public static final String ES_PORT_PROPERTY = "esPort";
public static final String ES_PORT_PROPERTY = "sonar.es.node.port";
public static final String ES_CLUSTER_PROPERTY = "esCluster";
public static final String ES_HOME_PROPERTY = "esHome";

public static final String MISSING_ES_PORT = "Missing ES port Argument";
public static final String MISSING_ES_HOME = "Missing ES home directory Argument";

public static final String DEFAULT_CLUSTER_NAME = "sonarqube";

@@ -72,22 +67,13 @@ public class ElasticSearch extends Process {

@Override
public void onStart() {
String home = props.of(ES_HOME_PROPERTY);
if (home == null) {
throw new IllegalStateException(MISSING_ES_HOME);
}

String dataDir = props.of("sonar.path.data");
Integer port = props.intOf(ES_PORT_PROPERTY);
if (port == null) {
throw new IllegalStateException(MISSING_ES_PORT);
}

String clusterName = props.of(ES_CLUSTER_PROPERTY, DEFAULT_CLUSTER_NAME);

LOGGER.info("Starting ES[{}] on port: {}", clusterName, port);
LoggerFactory.getLogger(ElasticSearch.class).info("Starting ES[{}] on port: {}", clusterName, port);

ImmutableSettings.Builder esSettings = ImmutableSettings.settingsBuilder()

.put("es.foreground", "yes")

.put("discovery.zen.ping.multicast.enabled", "false")
@@ -108,7 +94,7 @@ public class ElasticSearch extends Process {
.put("node.data", true)
.put("node.local", false)
.put("transport.tcp.port", port)
.put("path.home", home);
.put("path.data", new File(dataDir, "es").getAbsolutePath());

if (props.booleanOf(ES_DEBUG_PROPERTY, false)) {
esSettings

+ 2
- 32
server/sonar-search/src/test/java/org/sonar/search/ElasticSearchTest.java View File

@@ -79,42 +79,12 @@ public class ElasticSearchTest {
}
}


@Test
public void missing_properties() throws IOException, MBeanRegistrationException, InstanceNotFoundException {

Properties properties = new Properties();
properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
properties.setProperty(Process.NAME_PROPERTY, "ES");
properties.setProperty(Process.PORT_PROPERTY, Integer.toString(freePort));

try {
elasticSearch = new ElasticSearch(Props.create(properties));
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo(ElasticSearch.MISSING_ES_HOME);
}

properties.setProperty(ElasticSearch.ES_HOME_PROPERTY, tempDirectory.getAbsolutePath());
try {
resetMBeanServer();
elasticSearch = new ElasticSearch(Props.create(properties));
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo(ElasticSearch.MISSING_ES_PORT);
}
resetMBeanServer();

properties.setProperty(ElasticSearch.ES_PORT_PROPERTY, Integer.toString(freeESPort));
elasticSearch = new ElasticSearch(Props.create(properties));
assertThat(elasticSearch).isNotNull();
}

@Test
public void can_connect() throws SocketException {

Properties properties = new Properties();
properties.setProperty(Process.SONAR_HOME, FileUtils.getTempDirectoryPath());
properties.setProperty(Process.NAME_PROPERTY, "ES");
properties.setProperty(ElasticSearch.ES_HOME_PROPERTY, tempDirectory.getAbsolutePath());
properties.setProperty("sonar.path.data", tempDirectory.getAbsolutePath());
properties.setProperty(ElasticSearch.ES_PORT_PROPERTY, Integer.toString(freeESPort));

elasticSearch = new ElasticSearch(Props.create(properties));
@@ -157,4 +127,4 @@ public class ElasticSearchTest {
assertThat(e.getMessage()).isEqualTo("No node available");
}
}
}
}

+ 61
- 0
server/sonar-server-app/pom.xml View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>server</artifactId>
<version>4.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>sonar-server-app</artifactId>
<packaging>jar</packaging>
<name>SonarQube :: Server :: App</name>
<description>Temporary bootstrapper of SonarQube web server</description>

<dependencies>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-process</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

+ 167
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/Connectors.java View File

@@ -0,0 +1,167 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.LoggerFactory;
import org.sonar.process.Props;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

class Connectors {

private static final int DISABLED_PORT = -1;
static final String HTTP_PROTOCOL = "HTTP/1.1";
static final String AJP_PROTOCOL = "AJP/1.3";

static void configure(Tomcat tomcat, Props props) {
configureShutdown(tomcat, props);
configureConnectors(tomcat, props);
}

private static void configureConnectors(Tomcat tomcat, Props props) {
List<Connector> connectors = new ArrayList<Connector>();
connectors.addAll(Arrays.asList(newHttpConnector(props), newAjpConnector(props), newHttpsConnector(props)));
connectors.removeAll(Collections.singleton(null));

verify(connectors);

tomcat.setConnector(connectors.get(0));
for (Connector connector : connectors) {
tomcat.getService().addConnector(connector);
}
}

private static void verify(List<Connector> connectors) {
if (connectors.isEmpty()) {
throw new IllegalStateException("HTTP connectors are disabled");
}
Set<Integer> ports = new HashSet<Integer>();
for (Connector connector : connectors) {
int port = connector.getPort();
if (ports.contains(port)) {
throw new IllegalStateException(String.format("HTTP, AJP and HTTPS must not use the same port %d", port));
}
ports.add(port);
}
}

private static void configureShutdown(Tomcat tomcat, Props props) {
String shutdownToken = props.of("sonar.web.shutdown.token");
Integer shutdownPort = props.intOf("sonar.web.shutdown.port");
if (shutdownToken != null && !"".equals(shutdownToken) && shutdownPort != null) {
tomcat.getServer().setPort(shutdownPort);
tomcat.getServer().setShutdown(shutdownToken);
info("Shutdown command is enabled on port " + shutdownPort);
}
}

@Nullable
private static Connector newHttpConnector(Props props) {
Connector connector = null;
// Not named "sonar.web.http.port" to keep backward-compatibility
int port = props.intOf("sonar.web.port", 9000);
if (port > DISABLED_PORT) {
connector = newConnector(props, HTTP_PROTOCOL, "http");
connector.setPort(port);
info("HTTP connector is enabled on port " + port);
}
return connector;
}

@Nullable
private static Connector newAjpConnector(Props props) {
Connector connector = null;
int port = props.intOf("sonar.ajp.port", DISABLED_PORT);
if (port > DISABLED_PORT) {
connector = newConnector(props, AJP_PROTOCOL, "http");
connector.setPort(port);
info("AJP connector is enabled on port " + port);
}
return connector;
}
@Nullable
private static Connector newHttpsConnector(Props props) {
Connector connector = null;
int port = props.intOf("sonar.web.https.port", DISABLED_PORT);
if (port > DISABLED_PORT) {
connector = newConnector(props, HTTP_PROTOCOL, "https");
connector.setPort(port);
connector.setSecure(true);
connector.setScheme("https");
setConnectorAttribute(connector, "keyAlias", props.of("sonar.web.https.keyAlias"));
String keyPassword = props.of("sonar.web.https.keyPass", "changeit");
setConnectorAttribute(connector, "keyPass", keyPassword);
setConnectorAttribute(connector, "keystorePass", props.of("sonar.web.https.keystorePass", keyPassword));
setConnectorAttribute(connector, "keystoreFile", props.of("sonar.web.https.keystoreFile"));
setConnectorAttribute(connector, "keystoreType", props.of("sonar.web.https.keystoreType", "JKS"));
setConnectorAttribute(connector, "keystoreProvider", props.of("sonar.web.https.keystoreProvider"));
setConnectorAttribute(connector, "truststorePass", props.of("sonar.web.https.truststorePass", "changeit"));
setConnectorAttribute(connector, "truststoreFile", props.of("sonar.web.https.truststoreFile"));
setConnectorAttribute(connector, "truststoreType", props.of("sonar.web.https.truststoreType", "JKS"));
setConnectorAttribute(connector, "truststoreProvider", props.of("sonar.web.https.truststoreProvider"));
setConnectorAttribute(connector, "clientAuth", props.of("sonar.web.https.clientAuth", "false"));
setConnectorAttribute(connector, "sslProtocol", "TLS");
setConnectorAttribute(connector, "SSLEnabled", true);
info("HTTPS connector is enabled on port " + port);
}
return connector;
}

private static Connector newConnector(Props props, String protocol, String scheme) {
Connector connector = new Connector(protocol);
connector.setURIEncoding("UTF-8");
connector.setProperty("address", props.of("sonar.web.host", "0.0.0.0"));
configurePool(props, connector, scheme);
configureCompression(connector);
return connector;
}

private static void configurePool(Props props, Connector connector, String scheme) {
connector.setProperty("acceptorThreadCount", String.valueOf(2));
connector.setProperty("minSpareThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".minThreads", 5)));
connector.setProperty("maxThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".maxThreads", 50)));
connector.setProperty("acceptCount", String.valueOf(props.intOf("sonar.web." + scheme + ".acceptCount", 25)));
}

private static void configureCompression(Connector connector) {
connector.setProperty("compression", "on");
connector.setProperty("compressionMinSize", "1024");
connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,text/css,application/json,application/javascript");
}

private static void setConnectorAttribute(Connector c, String key, @Nullable Object value) {
if (value != null) {
c.setAttribute(key, value);
}
}

private static void info(String message) {
LoggerFactory.getLogger(Connectors.class).info(message);
}
}

+ 130
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/EmbeddedTomcat.java View File

@@ -0,0 +1,130 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.io.FileUtils;

import java.io.File;

class EmbeddedTomcat {

public static final String TEMP_RELATIVE_PATH = "temp/tomcat";

private final Env env;
private Tomcat tomcat = null;
private Thread hook = null;
private boolean stopping = false, ready = false;

EmbeddedTomcat(Env env) {
this.env = env;
}

void start() {
if (tomcat != null || hook != null) {
throw new IllegalStateException("Server is already started");
}

try {
// '%2F' (slash /) and '%5C' (backslash \) are permitted as path delimiters in URLs
// See Ruby on Rails url_for
System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");

System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");

tomcat = new Tomcat();

// Initialize directories
String basedir = env.freshDir(TEMP_RELATIVE_PATH).getCanonicalPath();
tomcat.setBaseDir(basedir);
tomcat.getHost().setAppBase(basedir);
tomcat.getHost().setAutoDeploy(false);
tomcat.getHost().setCreateDirs(false);
tomcat.getHost().setDeployOnStartup(true);

Logging.configure(tomcat, env, env.props());
Connectors.configure(tomcat, env.props());
Webapp.configure(tomcat, env, env.props());
tomcat.start();
addShutdownHook();
ready = true;
tomcat.getServer().await();
} catch (Exception e) {
throw new IllegalStateException("Fail to start web server", e);
}
// Shutdown command received
stop();
}

private void addShutdownHook() {
hook = new Thread() {
@Override
public void run() {
EmbeddedTomcat.this.doStop();
}
};
Runtime.getRuntime().addShutdownHook(hook);
}


void stop() {
removeShutdownHook();
doStop();
}

private synchronized void doStop() {
try {
if (tomcat != null && !stopping) {
stopping = true;
tomcat.stop();
tomcat.destroy();
}
tomcat = null;
stopping = false;
ready = false;
File tempDir = env.file(TEMP_RELATIVE_PATH);
FileUtils.deleteQuietly(tempDir);

} catch (LifecycleException e) {
throw new IllegalStateException("Fail to stop web server", e);
}
}

private void removeShutdownHook() {
if (hook != null && !hook.isAlive()) {
Runtime.getRuntime().removeShutdownHook(hook);
hook = null;
}
}

boolean isReady( ){
return ready;
}

int port() {
Connector[] connectors = tomcat.getService().findConnectors();
if (connectors.length > 0) {
return connectors[0].getLocalPort();
}
return -1;
}
}

+ 69
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/Env.java View File

@@ -0,0 +1,69 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.commons.io.FileUtils;
import org.sonar.process.Props;

import java.io.File;
import java.io.IOException;

class Env {

private final Props props;

Env(Props props) {
this.props = props;
}

Props props() {
return props;
}

File rootDir() {
return new File(props.of("sonar.path.home"));
}

File file(String relativePath) {
return new File(rootDir(), relativePath);
}

File freshDir(String relativePath) {
File dir = new File(rootDir(), relativePath);
FileUtils.deleteQuietly(dir);
dir.mkdirs();
return dir;
}

/**
* This check is required in order to provide more meaningful message than JRuby - see SONAR-2715
*/
void verifyWritableTempDir() {
File file = null;
try {
file = File.createTempFile("sonarqube-check", "tmp");
} catch (IOException e) {
throw new IllegalStateException("Unable to create file in temporary directory, please check existence " +
"and permissions of: " + FileUtils.getTempDirectory(), e);
} finally {
FileUtils.deleteQuietly(file);
}
}
}

+ 81
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/Logging.java View File

@@ -0,0 +1,81 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import ch.qos.logback.access.tomcat.LogbackValve;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.sonar.process.Props;

import java.util.logging.LogManager;

class Logging {

static final String ACCESS_RELATIVE_PATH = "web/WEB-INF/config/logback-access.xml";
static final String PROPERTY_ENABLE_ACCESS_LOGS = "sonar.web.accessLogs.enable";

static void init() {
// Configure java.util.logging, used by Tomcat, in order to forward to slf4j
LogManager.getLogManager().reset();
SLF4JBridgeHandler.install();
}

static void configure(Tomcat tomcat, Env env, Props props) {
tomcat.setSilent(false);
tomcat.getService().addLifecycleListener(new LifecycleLogger(console()));
configureLogbackAccess(tomcat, env, props);
}

static Logger console() {
return LoggerFactory.getLogger("console");
}

private static void configureLogbackAccess(Tomcat tomcat, Env env, Props props) {
if (props.booleanOf(PROPERTY_ENABLE_ACCESS_LOGS, true)) {
LogbackValve valve = new LogbackValve();
valve.setQuiet(true);
valve.setFilename(env.file(ACCESS_RELATIVE_PATH).getAbsolutePath());
tomcat.getHost().getPipeline().addValve(valve);
}
}

static class LifecycleLogger implements LifecycleListener {
private Logger logger;

LifecycleLogger(Logger logger) {
this.logger = logger;
}

@Override
public void lifecycleEvent(LifecycleEvent event) {
if ("after_start".equals(event.getType())) {
logger.info("Web server is started");

} else if ("after_destroy".equals(event.getType())) {
logger.info("Web server is stopped");
}
}
}

}

+ 36
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/NullJarScanner.java View File

@@ -0,0 +1,36 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;

import javax.servlet.ServletContext;
import java.util.Set;

/**
* Disable taglib and web-fragment.xml scanning of Tomcat. Should speed up startup.
*/
class NullJarScanner implements JarScanner {
@Override
public void scan(ServletContext context, ClassLoader classloader, JarScannerCallback callback, Set<String> jarsToSkip) {
// doing nothing is fast!
}
}

+ 52
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/ServerProcess.java View File

@@ -0,0 +1,52 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

public class ServerProcess extends org.sonar.process.Process {

private final EmbeddedTomcat tomcat;

public ServerProcess(String[] args) {
super(args);
Logging.init();
Env env = new Env(props);
env.verifyWritableTempDir();
this.tomcat = new EmbeddedTomcat(env);
}

@Override
public void onStart() {
tomcat.start();
}

@Override
public void onTerminate() {
tomcat.stop();
}

@Override
public boolean isReady() {
return tomcat.isReady();
}

public static void main(String[] args) {
new ServerProcess(args).start();
}
}

+ 77
- 0
server/sonar-server-app/src/main/java/org/sonar/server/app/Webapp.java View File

@@ -0,0 +1,77 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.LoggerFactory;
import org.sonar.process.Props;

import java.util.Map;

class Webapp {

private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes";
private static final String RAILS_ENV = "rails.env";
private static final String PROPERTY_CONTEXT = "sonar.web.context";
private static final String PROPERTY_LOG_PROFILING_LEVEL = "sonar.log.profilingLevel";
private static final String PROPERTY_LOG_CONSOLE = "sonar.log.console";

static void configure(Tomcat tomcat, Env env, Props props) {
try {
Context context = tomcat.addWebapp(getContextPath(props), env.file("web").getAbsolutePath());
context.setConfigFile(env.file("web/META-INF/context.xml").toURI().toURL());
context.addParameter(PROPERTY_LOG_PROFILING_LEVEL, props.of(PROPERTY_LOG_PROFILING_LEVEL, "NONE"));
context.addParameter(PROPERTY_LOG_CONSOLE, props.of(PROPERTY_LOG_CONSOLE, "false"));
for (Map.Entry<Object, Object> entry : props.properties().entrySet()) {
String key = entry.getKey().toString();
if (key.startsWith("sonar.")) {
context.addParameter(key, entry.getValue().toString());
}
}
configureRailsMode(props, context);
context.setJarScanner(new NullJarScanner());

} catch (Exception e) {
throw new IllegalStateException("Fail to configure webapp", e);
}
}

static String getContextPath(Props props) {
String context = props.of(PROPERTY_CONTEXT, "");
if ("/".equals(context)) {
context = "";
} else if (!"".equals(context) && !context.startsWith("/")) {
throw new IllegalStateException(String.format("Value of '%s' must start with a forward slash: '%s'", PROPERTY_CONTEXT, context));
}
return context;
}

static void configureRailsMode(Props props, Context context) {
if (props.booleanOf("sonar.rails.dev")) {
context.addParameter(RAILS_ENV, "development");
context.addParameter(JRUBY_MAX_RUNTIMES, "3");
LoggerFactory.getLogger(Webapp.class).warn("\n\n\n------ RAILS DEVELOPMENT MODE IS ENABLED ------\n\n\n");
} else {
context.addParameter(RAILS_ENV, "production");
context.addParameter(JRUBY_MAX_RUNTIMES, "1");
}
}
}

+ 2
- 25
server/sonar-server/pom.xml View File

@@ -151,32 +151,9 @@
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>7.0.42</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java View File

@@ -100,7 +100,7 @@ public class EmbeddedDatabase implements Startable {
}

private File getSonarHomeDataDirectory(Settings settings) {
File sonarHome = new File(settings.getString(CoreProperties.SONAR_HOME));
File sonarHome = new File(settings.getString("sonar.path.home"));
if (!sonarHome.isDirectory()) {
throw new IllegalStateException("SonarQube home directory is not valid");
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java View File

@@ -56,7 +56,7 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
public DefaultServerFileSystem(Database database, Settings settings, Server server) {
this.database = database;
this.server = server;
this.homeDir = new File(settings.getString(CoreProperties.SONAR_HOME));
this.homeDir = new File(settings.getString("sonar.path.home"));
}

/**

+ 21
- 10
server/sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java View File

@@ -24,10 +24,12 @@ import org.slf4j.LoggerFactory;
import org.sonar.core.config.Logback;
import org.sonar.core.profiling.Profiling;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

import static org.apache.commons.lang.StringUtils.defaultIfEmpty;

@@ -50,7 +52,16 @@ public final class PlatformServletContextListener implements ServletContextListe
public void contextInitialized(ServletContextEvent event) {
try {
configureLogback(event);
Platform.getInstance().init(System.getProperties());
Properties props = new Properties();
ServletContext context = event.getServletContext();
Enumeration<String> paramKeys = context.getInitParameterNames();
while (paramKeys.hasMoreElements()) {
String key = paramKeys.nextElement();
if (key.startsWith("sonar.")) {
props.put(key, context.getInitParameter(key));
}
}
Platform.getInstance().init(props);
Platform.getInstance().doStart();
} catch (Throwable t) {
// Tomcat 7 "limitations":
@@ -80,19 +91,19 @@ public final class PlatformServletContextListener implements ServletContextListe
*/
private void configureLogback(ServletContextEvent event) {
String configProfilingLevel = defaultIfEmpty(
event.getServletContext().getInitParameter(Profiling.CONFIG_PROFILING_LEVEL),
System.getProperty(Profiling.CONFIG_PROFILING_LEVEL));
event.getServletContext().getInitParameter(Profiling.CONFIG_PROFILING_LEVEL),
System.getProperty(Profiling.CONFIG_PROFILING_LEVEL));
Profiling.Level profilingLevel = Profiling.Level.fromConfigString(configProfilingLevel);
String consoleEnabled = defaultIfEmpty(defaultIfEmpty(
event.getServletContext().getInitParameter(CONFIG_LOG_CONSOLE),
System.getProperty(CONFIG_LOG_CONSOLE)),
// Line below used in last resort
"false");
// Line below used in last resort
"false");
Map<String, String> variables = ImmutableMap.of(
"RAILS_LOGGER_LEVEL", profilingLevel == Profiling.Level.FULL ? "DEBUG" : "WARN",
"LOGFILE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? LOGFILE_FULL_LOGGING_FORMAT : LOGFILE_STANDARD_LOGGING_FORMAT,
"CONSOLE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? CONSOLE_FULL_LOGGING_FORMAT : CONSOLE_STANDARD_LOGGING_FORMAT,
"CONSOLE_ENABLED", consoleEnabled);
"RAILS_LOGGER_LEVEL", profilingLevel == Profiling.Level.FULL ? "DEBUG" : "WARN",
"LOGFILE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? LOGFILE_FULL_LOGGING_FORMAT : LOGFILE_STANDARD_LOGGING_FORMAT,
"CONSOLE_LOGGING_FORMAT", profilingLevel == Profiling.Level.FULL ? CONSOLE_FULL_LOGGING_FORMAT : CONSOLE_STANDARD_LOGGING_FORMAT,
"CONSOLE_ENABLED", consoleEnabled);
Logback.configure("/org/sonar/server/platform/logback.xml", variables);
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java View File

@@ -77,7 +77,7 @@ public final class ServerImpl extends Server implements Startable {
// Remove trailing slashes
.replaceFirst("(\\/+)$", "");

sonarHome = new File(settings.getString(CoreProperties.SONAR_HOME));
sonarHome = new File(settings.getString("sonar.path.home"));
if (!sonarHome.isDirectory()) {
throw new IllegalStateException("SonarQube home directory is not valid");
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java View File

@@ -369,7 +369,7 @@ public final class JRubyFacade {
}

public String getServerHome() {
return get(Settings.class).getString(CoreProperties.SONAR_HOME);
return get(Settings.class).getString("sonar.path.home");
}

public ComponentContainer getContainer() {

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java View File

@@ -76,7 +76,7 @@ public class ServerTester extends ExternalResource {
Properties properties = new Properties();
properties.putAll(initialProps);
properties.setProperty(IndexProperties.TYPE, IndexProperties.ES_TYPE.MEMORY.name());
properties.setProperty(CoreProperties.SONAR_HOME, homeDir.getAbsolutePath());
properties.setProperty("sonar.path.home", homeDir.getAbsolutePath());
properties.setProperty(DatabaseProperties.PROP_URL, "jdbc:h2:" + homeDir.getAbsolutePath() + "/h2");
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
String key = entry.getKey().toString();

+ 7
- 6
server/sonar-web/pom.xml View File

@@ -19,6 +19,13 @@
<sonar.exclusions>src/main/js/third-party/**/*,src/main/js/require.js,src/main/js/tests/**/*</sonar.exclusions>
</properties>

<dependencies>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-server</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
@@ -361,12 +368,6 @@
<artifactId>h2</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-server</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- core plugins -->
<dependency>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 11
- 1
sonar-application/assembly.xml View File

@@ -14,11 +14,21 @@
<exclude>mysql:mysql-connector-java</exclude>
<exclude>org.postgresql:postgresql</exclude>
<exclude>net.sourceforge.jtds:jtds</exclude>
<exclude>org.codehaus.sonar:sonar-search</exclude>
<exclude>org.codehaus.sonar:sonar-web</exclude>
<exclude>org.codehaus.sonar.plugins:*</exclude>
<exclude>org.codehaus.sonar-plugins.*:*</exclude>
<exclude>org.codehaus.sonar-plugins.java:*</exclude>
<exclude>org.codehaus.sonar:sonar-batch-maven-compat</exclude>
</excludes>
</dependencySet>
<dependencySet>
<outputDirectory>lib/search</outputDirectory>
<useTransitiveDependencies>true</useTransitiveDependencies>
<useTransitiveFiltering>false</useTransitiveFiltering>
<includes>
<include>org.codehaus.sonar:sonar-search</include>
</includes>
</dependencySet>
<dependencySet>
<outputDirectory>lib/batch</outputDirectory>
<useTransitiveDependencies>false</useTransitiveDependencies>

+ 13
- 1
sonar-application/pom.xml View File

@@ -77,14 +77,26 @@

<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-server</artifactId>
<artifactId>sonar-process</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-server-app</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-search</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-web</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>

+ 2
- 2
sonar-application/src/main/assembly/conf/sonar.properties View File

@@ -10,8 +10,8 @@

#--------------------------------------------------------------------------------------------------
# ELASTICSEARCH
sonar.es.java_opts="-Xmx1G -Xms1G"
sonar.web.java_opts="-Xmx128m -Xms128m"
sonar.es.javaOpts=-Xmx256m -Xms256m
sonar.web.java_opts=-Xmx76m


#--------------------------------------------------------------------------------------------------

+ 4
- 4
sonar-application/src/main/assembly/conf/wrapper.conf View File

@@ -1,19 +1,19 @@
# Java Additional Parameters
wrapper.java.additional.1=-Djava.awt.headless=true
wrapper.java.additional.2=-XX:MaxPermSize=160m
#wrapper.java.additional.2=-XX:MaxPermSize=160m
wrapper.java.additional.3=-XX:+HeapDumpOnOutOfMemoryError
wrapper.java.additional.4=-Dfile.encoding=UTF-8
wrapper.java.additional.5=-Djruby.management.enabled=false

# Maximum amount of memory of Java VM
wrapper.java.additional.6=-Xmx1024M
wrapper.java.additional.2=-Xmx32M

# RECOMMENDED : uncomment if Java Virtual Machine is a JDK but not a JRE. To know which JVM you use, execute
# 'java -version'. JDK displays 'Server VM'.
#wrapper.java.additional.7=-server

# Initial JVM heap size (in MB)
wrapper.java.initmemory=256
wrapper.java.initmemory=16

#********************************************************************
# Wrapper Java Properties
@@ -42,7 +42,7 @@ wrapper.java.classpath.6=../../extensions/jdbc-driver/mssql/*.jar
wrapper.java.library.path.1=./lib

# Application parameters. Add parameters as needed starting from 1
wrapper.app.parameter.1=org.sonar.application.StartServer
wrapper.app.parameter.1=org.sonar.application.ForkProcesses

# Do not touch the following property. Max memory is set with -Xmx (see above).
# See https://jira.codehaus.org/browse/SONAR-5204

+ 7
- 1
sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java View File

@@ -34,7 +34,7 @@ class EmbeddedTomcat {
private final Env env;
private Tomcat tomcat = null;
private Thread hook = null;
private boolean stopping = false;
private boolean stopping = false, ready = false;

EmbeddedTomcat(Env env) {
this.env = env;
@@ -71,6 +71,7 @@ class EmbeddedTomcat {
Webapp.configure(tomcat, env, props);
tomcat.start();
addShutdownHook();
ready = true;
tomcat.getServer().await();

// Shutdown command received
@@ -102,6 +103,7 @@ class EmbeddedTomcat {
}
tomcat = null;
stopping = false;
ready = false;
File tempDir = env.file(TEMP_RELATIVE_PATH);
FileUtils.deleteQuietly(tempDir);

@@ -117,6 +119,10 @@ class EmbeddedTomcat {
}
}

boolean isReady( ){
return ready;
}

int port() {
Connector[] connectors = tomcat.getService().findConnectors();
if (connectors.length > 0) {

+ 104
- 0
sonar-application/src/main/java/org/sonar/application/ForkProcesses.java View File

@@ -0,0 +1,104 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.application;

import org.sonar.process.Monitor;
import org.sonar.process.NetworkUtils;
import org.sonar.process.ProcessWrapper;

import javax.annotation.Nullable;

public class ForkProcesses {
private Monitor monitor;
private final Thread shutdownHook;
private ProcessWrapper elasticsearch;
private ProcessWrapper server;

public ForkProcesses() throws Exception {
Installation installation = new Installation();

String esPort = installation.prop("sonar.es.node.port", null);
if (esPort == null) {
esPort = String.valueOf(NetworkUtils.freePort());
installation.setProp("sonar.es.node.port", esPort);
}
installation.setProp("sonar.es.type", "TRANSPORT");

shutdownHook = new Thread(new Runnable() {
@Override
public void run() {
monitor.interrupt();
terminateAndWait(elasticsearch);
terminateAndWait(server);
}
});

Runtime.getRuntime().addShutdownHook(shutdownHook);

elasticsearch = new ProcessWrapper(
installation.homeDir().getAbsolutePath(),
installation.prop("sonar.es.javaOpts", "-server -Xmx256m -Xms128m -Xss256k -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly"),
"org.sonar.search.ElasticSearch",
installation.props(),
"ES",
installation.starPath("lib/search"));


server = new ProcessWrapper(
installation.homeDir().getAbsolutePath(),
installation.prop("sonar.web.javaOpts", "-Xmx768m -server -XX:MaxPermSize=160m -Djava.awt.headless=true -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -Djruby.management.enabled=false"),
"org.sonar.server.app.ServerProcess",
installation.props(),
"SQ",
installation.starPath("lib"));

monitor = new Monitor();
monitor.registerProcess(elasticsearch);
monitor.registerProcess(server);
monitor.start();
try {
monitor.join();
} catch (InterruptedException e) {
stop(true);
}
stop(true);
}

public void stop(boolean waitForCompletion) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
shutdownHook.start();
if (waitForCompletion) {
try {
shutdownHook.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void terminateAndWait(@Nullable ProcessWrapper process) {
if (process != null && process.getThread() != null) {
process.terminate();
}
}

public static void main(String[] args) throws Exception {
new ForkProcesses();
}
}

+ 161
- 0
sonar-application/src/main/java/org/sonar/application/Installation.java View File

@@ -0,0 +1,161 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.application;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class Installation {

// guessed from location of sonar-application.jar
private final File homeDir;
private final File tempDir, dataDir, logsDir, webDir;
private final Map<String, String> props = new HashMap<String, String>();

Installation() throws URISyntaxException, IOException {
// TODO make it configurable with sonar.path.home ?
// lib/sonar-application.jar
File appJar = new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
homeDir = appJar.getParentFile().getParentFile();

Properties p = new Properties();
File propsFile = new File(homeDir, "conf/sonar.properties");
if (propsFile.exists()) {
FileReader reader = new FileReader(propsFile);
try {
p.load(reader);
} finally {
IOUtils.closeQuietly(reader);
}
}
p.putAll(System.getenv());
p.putAll(System.getProperties());
p = ConfigurationUtils.interpolateEnvVariables(p);
for (Map.Entry<Object, Object> entry : p.entrySet()) {
Object val = entry.getValue();
if (val != null) {
this.props.put(entry.getKey().toString(), val.toString());
}
}

props.put("sonar.path.home", homeDir.getAbsolutePath());
this.dataDir = existingDir("sonar.path.data", "data");
this.tempDir = freshDir("sonar.path.temp", "temp");
this.logsDir = existingDir("sonar.path.logs", "logs");
this.webDir = existingDir("sonar.path.web", "web");
}

File homeDir() {
return homeDir;
}

File esDir() {
return new File(homeDir, "data/es");
}

File webDir() {
return webDir;
}

File tempDir() {
return tempDir;
}

String starPath(String relativePath) {
File dir = new File(homeDir, relativePath);
return FilenameUtils.concat(dir.getAbsolutePath(), "*");
}

private File freshDir(String propKey, String defaultRelativePath) throws IOException {
File dir = configuredDir(propKey, defaultRelativePath);
FileUtils.deleteQuietly(dir);
FileUtils.forceMkdir(dir);
return dir;
}

private File existingDir(String propKey, String defaultRelativePath) throws IOException {
File dir = configuredDir(propKey, defaultRelativePath);
if (!dir.exists()) {
// TODO replace by MessageException
throw new IllegalStateException(String.format("Directory does not exist: %s. Please check property %s", dir.getAbsolutePath(), propKey));
}
if (!dir.isDirectory()) {
// TODO replace by MessageException
throw new IllegalStateException(String.format("Not a directory: %s. Please check property %s", dir.getAbsolutePath(), propKey));
}
return dir;
}

private File configuredDir(String propKey, String defaultRelativePath) {
String path = prop(propKey, defaultRelativePath);
File d = new File(path);
if (!d.isAbsolute()) {
d = new File(homeDir, path);
}
props.put(propKey, d.getAbsolutePath());
return d;
}

Map<String, String> props() {
return props;
}

@CheckForNull
String prop(String key, @Nullable String defaultValue) {
String s = props.get(key);
return s != null ? s : defaultValue;
}

@CheckForNull
Integer propAsInt(String key) {
String s = prop(key, null);
if (s != null && !"".equals(s)) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
throw new IllegalStateException(String.format("Value of property %s is not an integer: %s", key, s), e);
}
}
return null;
}

void setProp(String key, String value) {
props.put(key, value);
}

void logInfo(String message) {
System.out.println(message);
}

void logError(String message) {
System.err.println(message);
}
}

+ 1
- 0
sonar-application/src/main/java/org/sonar/application/Webapp.java View File

@@ -37,6 +37,7 @@ class Webapp {
context.setConfigFile(env.file("web/META-INF/context.xml").toURI().toURL());
context.addParameter(PROPERTY_LOG_PROFILING_LEVEL, props.of(PROPERTY_LOG_PROFILING_LEVEL, "NONE"));
context.addParameter(PROPERTY_LOG_CONSOLE, props.of(PROPERTY_LOG_CONSOLE, "false"));

configureRailsMode(props, context);
context.setJarScanner(new NullJarScanner());


+ 11
- 1
sonar-application/src/main/resources/logback.xml View File

@@ -40,9 +40,19 @@
<appender-ref ref="CONSOLE"/>
</logger>

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
</pattern>
</encoder>
</appender>

<root>
<level value="INFO"/>
<level value="DEBUG"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="LOGFILE"/>
</root>


</configuration>

Loading…
Cancel
Save