From f07a28f60ad0cc18f4e318109389032fb30bba06 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 24 Jul 2014 21:50:19 +0200 Subject: SONAR-4898 move sonar-server-app to sonar-server --- fork.sh | 2 +- server/pom.xml | 1 - .../main/java/org/sonar/server/app/Connectors.java | 153 --------------------- .../java/org/sonar/server/app/EmbeddedTomcat.java | 133 ------------------ .../main/java/org/sonar/server/app/Logging.java | 82 ----------- .../java/org/sonar/server/app/NullJarScanner.java | 36 ----- .../java/org/sonar/server/app/ServerProcess.java | 51 ------- .../src/main/java/org/sonar/server/app/Webapp.java | 75 ---------- server/sonar-server/pom.xml | 39 +++++- .../main/java/org/sonar/server/app/Connectors.java | 153 +++++++++++++++++++++ .../java/org/sonar/server/app/EmbeddedTomcat.java | 133 ++++++++++++++++++ .../main/java/org/sonar/server/app/Logging.java | 82 +++++++++++ .../java/org/sonar/server/app/NullJarScanner.java | 36 +++++ .../java/org/sonar/server/app/ServerProcess.java | 51 +++++++ .../src/main/java/org/sonar/server/app/Webapp.java | 83 +++++++++++ .../java/org/sonar/server/app/package-info.java | 24 ++++ server/sonar-web/pom.xml | 10 +- .../sonar-web/src/main/webapp/META-INF/context.xml | 4 - sonar-application/assembly.xml | 21 ++- sonar-application/pom.xml | 8 +- sonar-batch-protocol/pom.xml | 5 - 21 files changed, 620 insertions(+), 562 deletions(-) delete mode 100644 server/sonar-server-app/src/main/java/org/sonar/server/app/Connectors.java delete mode 100644 server/sonar-server-app/src/main/java/org/sonar/server/app/EmbeddedTomcat.java delete mode 100644 server/sonar-server-app/src/main/java/org/sonar/server/app/Logging.java delete mode 100644 server/sonar-server-app/src/main/java/org/sonar/server/app/NullJarScanner.java delete mode 100644 server/sonar-server-app/src/main/java/org/sonar/server/app/ServerProcess.java delete mode 100644 server/sonar-server-app/src/main/java/org/sonar/server/app/Webapp.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/Logging.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/app/package-info.java delete mode 100644 server/sonar-web/src/main/webapp/META-INF/context.xml diff --git a/fork.sh b/fork.sh index 3b5f66ec516..0843cee4c5c 100755 --- a/fork.sh +++ b/fork.sh @@ -1,6 +1,6 @@ #!/bin/sh -mvn clean install -DskipTests -Denforcer.skip=true -pl server/sonar-search,server/sonar-process,server/sonar-server-app,server/sonar-server-app,sonar-application +mvn clean install -DskipTests -Denforcer.skip=true -pl server/sonar-search,server/sonar-process,sonar-application if [[ "$OSTYPE" == "darwin"* ]]; then OS='macosx-universal-64' diff --git a/server/pom.xml b/server/pom.xml index b26febac729..131d3efcbcd 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -14,7 +14,6 @@ sonar-process sonar-search sonar-server - sonar-server-app sonar-web sonar-ws-client diff --git a/server/sonar-server-app/src/main/java/org/sonar/server/app/Connectors.java b/server/sonar-server-app/src/main/java/org/sonar/server/app/Connectors.java deleted file mode 100644 index 13276f34dbf..00000000000 --- a/server/sonar-server-app/src/main/java/org/sonar/server/app/Connectors.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.app; - -import org.apache.catalina.connector.Connector; -import org.apache.catalina.startup.Tomcat; -import org.slf4j.LoggerFactory; -import org.sonar.process.Props; - -import javax.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -class Connectors { - - private static final int DISABLED_PORT = -1; - static final String HTTP_PROTOCOL = "HTTP/1.1"; - static final String AJP_PROTOCOL = "AJP/1.3"; - - static void configure(Tomcat tomcat, Props props) { - List connectors = new ArrayList(); - 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 connectors) { - if (connectors.isEmpty()) { - throw new IllegalStateException("HTTP connectors are disabled"); - } - Set ports = new HashSet(); - for (Connector connector : connectors) { - int port = connector.getPort(); - if (ports.contains(port)) { - throw new IllegalStateException(String.format("HTTP, AJP and HTTPS must not use the same port %d", port)); - } - ports.add(port); - } - } - - @Nullable - private static Connector newHttpConnector(Props props) { - Connector connector = null; - // Not named "sonar.web.http.port" to keep backward-compatibility - int port = props.intOf("sonar.web.port", 9000); - if (port > DISABLED_PORT) { - connector = newConnector(props, HTTP_PROTOCOL, "http"); - connector.setPort(port); - info("HTTP connector is enabled on port " + port); - } - return connector; - } - - @Nullable - private static Connector newAjpConnector(Props props) { - Connector connector = null; - int port = props.intOf("sonar.ajp.port", DISABLED_PORT); - if (port > DISABLED_PORT) { - connector = newConnector(props, AJP_PROTOCOL, "http"); - connector.setPort(port); - info("AJP connector is enabled on port " + port); - } - return connector; - } - - @Nullable - private static Connector newHttpsConnector(Props props) { - Connector connector = null; - int port = props.intOf("sonar.web.https.port", DISABLED_PORT); - if (port > DISABLED_PORT) { - connector = newConnector(props, HTTP_PROTOCOL, "https"); - connector.setPort(port); - connector.setSecure(true); - connector.setScheme("https"); - setConnectorAttribute(connector, "keyAlias", props.of("sonar.web.https.keyAlias")); - String keyPassword = props.of("sonar.web.https.keyPass", "changeit"); - setConnectorAttribute(connector, "keyPass", keyPassword); - setConnectorAttribute(connector, "keystorePass", props.of("sonar.web.https.keystorePass", keyPassword)); - setConnectorAttribute(connector, "keystoreFile", props.of("sonar.web.https.keystoreFile")); - setConnectorAttribute(connector, "keystoreType", props.of("sonar.web.https.keystoreType", "JKS")); - setConnectorAttribute(connector, "keystoreProvider", props.of("sonar.web.https.keystoreProvider")); - setConnectorAttribute(connector, "truststorePass", props.of("sonar.web.https.truststorePass", "changeit")); - setConnectorAttribute(connector, "truststoreFile", props.of("sonar.web.https.truststoreFile")); - setConnectorAttribute(connector, "truststoreType", props.of("sonar.web.https.truststoreType", "JKS")); - setConnectorAttribute(connector, "truststoreProvider", props.of("sonar.web.https.truststoreProvider")); - setConnectorAttribute(connector, "clientAuth", props.of("sonar.web.https.clientAuth", "false")); - setConnectorAttribute(connector, "sslProtocol", "TLS"); - setConnectorAttribute(connector, "SSLEnabled", true); - info("HTTPS connector is enabled on port " + port); - } - return connector; - } - - private static Connector newConnector(Props props, String protocol, String scheme) { - Connector connector = new Connector(protocol); - connector.setURIEncoding("UTF-8"); - connector.setProperty("address", props.of("sonar.web.host", "0.0.0.0")); - configurePool(props, connector, scheme); - configureCompression(connector); - return connector; - } - - private static void configurePool(Props props, Connector connector, String scheme) { - connector.setProperty("acceptorThreadCount", String.valueOf(2)); - connector.setProperty("minSpareThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".minThreads", 5))); - connector.setProperty("maxThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".maxThreads", 50))); - connector.setProperty("acceptCount", String.valueOf(props.intOf("sonar.web." + scheme + ".acceptCount", 25))); - } - - private static void configureCompression(Connector connector) { - connector.setProperty("compression", "on"); - connector.setProperty("compressionMinSize", "1024"); - connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,text/css,application/json,application/javascript"); - } - - private static void setConnectorAttribute(Connector c, String key, @Nullable Object value) { - if (value != null) { - c.setAttribute(key, value); - } - } - - private static void info(String message) { - LoggerFactory.getLogger(Connectors.class).info(message); - } -} diff --git a/server/sonar-server-app/src/main/java/org/sonar/server/app/EmbeddedTomcat.java b/server/sonar-server-app/src/main/java/org/sonar/server/app/EmbeddedTomcat.java deleted file mode 100644 index 50239fe282d..00000000000 --- a/server/sonar-server-app/src/main/java/org/sonar/server/app/EmbeddedTomcat.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.app; - -import org.apache.catalina.LifecycleException; -import org.apache.catalina.connector.Connector; -import org.apache.catalina.startup.Tomcat; -import org.apache.commons.io.FileUtils; -import org.sonar.process.Props; - -import java.io.File; - -class EmbeddedTomcat { - - private final Props props; - private Tomcat tomcat = null; - private Thread hook = null; - private boolean stopping = false, ready = false; - - EmbeddedTomcat(Props props) { - this.props = props; - } - - void start() { - if (tomcat != null || hook != null) { - throw new IllegalStateException("Server is already started"); - } - - try { - // '%2F' (slash /) and '%5C' (backslash \) are permitted as path delimiters in URLs - // See Ruby on Rails url_for - System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); - - System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); - - tomcat = new Tomcat(); - - // Initialize directories - File tomcatDir = tomcatBasedir(); - String basedir = tomcatDir.getAbsolutePath(); - tomcat.setBaseDir(basedir); - tomcat.getHost().setAppBase(basedir); - tomcat.getHost().setAutoDeploy(false); - tomcat.getHost().setCreateDirs(false); - tomcat.getHost().setDeployOnStartup(true); - - Logging.configure(tomcat, props); - Connectors.configure(tomcat, props); - Webapp.configure(tomcat, props); - tomcat.start(); - addShutdownHook(); - ready = true; - tomcat.getServer().await(); - } catch (Exception e) { - throw new IllegalStateException("Fail to start web server", e); - } - // Shutdown command received - stop(); - } - - private File tomcatBasedir() { - return new File(props.of("sonar.path.temp"), "tomcat"); - } - - private void addShutdownHook() { - hook = new Thread() { - @Override - public void run() { - EmbeddedTomcat.this.doStop(); - } - }; - Runtime.getRuntime().addShutdownHook(hook); - } - - - void stop() { - removeShutdownHook(); - doStop(); - } - - private synchronized void doStop() { - try { - if (tomcat != null && !stopping) { - stopping = true; - tomcat.stop(); - tomcat.destroy(); - } - tomcat = null; - stopping = false; - ready = false; - FileUtils.deleteQuietly(tomcatBasedir()); - - } catch (LifecycleException e) { - throw new IllegalStateException("Fail to stop web server", e); - } - } - - private void removeShutdownHook() { - if (hook != null && !hook.isAlive()) { - Runtime.getRuntime().removeShutdownHook(hook); - hook = null; - } - } - - boolean isReady() { - return ready; - } - - int port() { - Connector[] connectors = tomcat.getService().findConnectors(); - if (connectors.length > 0) { - return connectors[0].getLocalPort(); - } - return -1; - } -} diff --git a/server/sonar-server-app/src/main/java/org/sonar/server/app/Logging.java b/server/sonar-server-app/src/main/java/org/sonar/server/app/Logging.java deleted file mode 100644 index a4d1aaeec28..00000000000 --- a/server/sonar-server-app/src/main/java/org/sonar/server/app/Logging.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.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.io.File; -import java.util.logging.LogManager; - -class Logging { - - static final String ACCESS_RELATIVE_PATH = "WEB-INF/config/logback-access.xml"; - static final String PROPERTY_ENABLE_ACCESS_LOGS = "sonar.web.accessLogs.enable"; - - static void init() { - // Configure java.util.logging, used by Tomcat, in order to forward to slf4j - LogManager.getLogManager().reset(); - SLF4JBridgeHandler.install(); - } - - static void configure(Tomcat tomcat, Props props) { - tomcat.setSilent(false); - tomcat.getService().addLifecycleListener(new LifecycleLogger(console())); - configureLogbackAccess(tomcat, props); - } - - static Logger console() { - return LoggerFactory.getLogger("console"); - } - - private static void configureLogbackAccess(Tomcat tomcat, Props props) { - if (props.booleanOf(PROPERTY_ENABLE_ACCESS_LOGS, true)) { - LogbackValve valve = new LogbackValve(); - valve.setQuiet(true); - valve.setFilename(new File(props.of("sonar.path.web"), ACCESS_RELATIVE_PATH).getAbsolutePath()); - tomcat.getHost().getPipeline().addValve(valve); - } - } - - static class LifecycleLogger implements LifecycleListener { - private Logger logger; - - LifecycleLogger(Logger logger) { - this.logger = logger; - } - - @Override - public void lifecycleEvent(LifecycleEvent event) { - if ("after_start".equals(event.getType())) { - logger.info("Web server is started"); - - } else if ("after_destroy".equals(event.getType())) { - logger.info("Web server is stopped"); - } - } - } - -} diff --git a/server/sonar-server-app/src/main/java/org/sonar/server/app/NullJarScanner.java b/server/sonar-server-app/src/main/java/org/sonar/server/app/NullJarScanner.java deleted file mode 100644 index 4f8ac9e5312..00000000000 --- a/server/sonar-server-app/src/main/java/org/sonar/server/app/NullJarScanner.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.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 jarsToSkip) { - // doing nothing is fast! - } -} diff --git a/server/sonar-server-app/src/main/java/org/sonar/server/app/ServerProcess.java b/server/sonar-server-app/src/main/java/org/sonar/server/app/ServerProcess.java deleted file mode 100644 index 35c9aba1e8f..00000000000 --- a/server/sonar-server-app/src/main/java/org/sonar/server/app/ServerProcess.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.app; - -public class ServerProcess extends org.sonar.process.Process { - - private final EmbeddedTomcat tomcat; - - public ServerProcess(String[] args) { - super(args); - Logging.init(); - this.tomcat = new EmbeddedTomcat(props); - } - - @Override - public void onStart() { - tomcat.start(); - } - - @Override - public void onTerminate() { - tomcat.stop(); - } - - @Override - public boolean isReady() { - return tomcat.isReady(); - } - - public static void main(String[] args) { - new ServerProcess(args).start(); - LOGGER.info("ServerProcess is done"); - } -} diff --git a/server/sonar-server-app/src/main/java/org/sonar/server/app/Webapp.java b/server/sonar-server-app/src/main/java/org/sonar/server/app/Webapp.java deleted file mode 100644 index 5ad563df59c..00000000000 --- a/server/sonar-server-app/src/main/java/org/sonar/server/app/Webapp.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.app; - -import org.apache.catalina.Context; -import org.apache.catalina.startup.Tomcat; -import org.slf4j.LoggerFactory; -import org.sonar.process.Props; - -import java.io.File; -import java.util.Map; - -class Webapp { - - private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes"; - private static final String RAILS_ENV = "rails.env"; - private static final String PROPERTY_CONTEXT = "sonar.web.context"; - - static void configure(Tomcat tomcat, Props props) { - try { - String webDir = props.of("sonar.path.web"); - Context context = tomcat.addWebapp(getContextPath(props), webDir); - context.setConfigFile(new File(webDir, "META-INF/context.xml").toURI().toURL()); - for (Map.Entry entry : props.cryptedProperties().entrySet()) { - String key = entry.getKey().toString(); - if (key.startsWith("sonar.")) { - context.addParameter(key, entry.getValue().toString()); - } - } - configureRailsMode(props, context); - context.setJarScanner(new NullJarScanner()); - - } catch (Exception e) { - throw new IllegalStateException("Fail to configure webapp", e); - } - } - - static String getContextPath(Props props) { - String context = props.of(PROPERTY_CONTEXT, ""); - if ("/".equals(context)) { - context = ""; - } else if (!"".equals(context) && !context.startsWith("/")) { - throw new IllegalStateException(String.format("Value of '%s' must start with a forward slash: '%s'", PROPERTY_CONTEXT, context)); - } - return context; - } - - static void configureRailsMode(Props props, Context context) { - if (props.booleanOf("sonar.rails.dev")) { - context.addParameter(RAILS_ENV, "development"); - context.addParameter(JRUBY_MAX_RUNTIMES, "3"); - LoggerFactory.getLogger(Webapp.class).warn("\n\n\n------ RAILS DEVELOPMENT MODE IS ENABLED ------\n\n\n"); - } else { - context.addParameter(RAILS_ENV, "production"); - context.addParameter(JRUBY_MAX_RUNTIMES, "1"); - } - } -} diff --git a/server/sonar-server/pom.xml b/server/sonar-server/pom.xml index 1665e581eb8..92c5dbaf7d6 100644 --- a/server/sonar-server/pom.xml +++ b/server/sonar-server/pom.xml @@ -13,6 +13,39 @@ SonarQube :: Server + + org.slf4j + jul-to-slf4j + + + ch.qos.logback + logback-access + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + org.apache.tomcat.embed + tomcat-embed-core + + + org.apache.tomcat.embed + tomcat-embed-jasper + + + org.apache.tomcat.embed + tomcat-embed-logging-juli + + + dom4j + dom4j + 1.6.1 + com.google.code.gson gson @@ -148,12 +181,6 @@ org.elasticsearch elasticsearch - - org.apache.tomcat.embed - tomcat-embed-core - 7.0.42 - provided - com.google.code.findbugs jsr305 diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java b/server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java new file mode 100644 index 00000000000..13276f34dbf --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java @@ -0,0 +1,153 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.app; + +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.slf4j.LoggerFactory; +import org.sonar.process.Props; + +import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +class Connectors { + + private static final int DISABLED_PORT = -1; + static final String HTTP_PROTOCOL = "HTTP/1.1"; + static final String AJP_PROTOCOL = "AJP/1.3"; + + static void configure(Tomcat tomcat, Props props) { + List connectors = new ArrayList(); + 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 connectors) { + if (connectors.isEmpty()) { + throw new IllegalStateException("HTTP connectors are disabled"); + } + Set ports = new HashSet(); + for (Connector connector : connectors) { + int port = connector.getPort(); + if (ports.contains(port)) { + throw new IllegalStateException(String.format("HTTP, AJP and HTTPS must not use the same port %d", port)); + } + ports.add(port); + } + } + + @Nullable + private static Connector newHttpConnector(Props props) { + Connector connector = null; + // Not named "sonar.web.http.port" to keep backward-compatibility + int port = props.intOf("sonar.web.port", 9000); + if (port > DISABLED_PORT) { + connector = newConnector(props, HTTP_PROTOCOL, "http"); + connector.setPort(port); + info("HTTP connector is enabled on port " + port); + } + return connector; + } + + @Nullable + private static Connector newAjpConnector(Props props) { + Connector connector = null; + int port = props.intOf("sonar.ajp.port", DISABLED_PORT); + if (port > DISABLED_PORT) { + connector = newConnector(props, AJP_PROTOCOL, "http"); + connector.setPort(port); + info("AJP connector is enabled on port " + port); + } + return connector; + } + + @Nullable + private static Connector newHttpsConnector(Props props) { + Connector connector = null; + int port = props.intOf("sonar.web.https.port", DISABLED_PORT); + if (port > DISABLED_PORT) { + connector = newConnector(props, HTTP_PROTOCOL, "https"); + connector.setPort(port); + connector.setSecure(true); + connector.setScheme("https"); + setConnectorAttribute(connector, "keyAlias", props.of("sonar.web.https.keyAlias")); + String keyPassword = props.of("sonar.web.https.keyPass", "changeit"); + setConnectorAttribute(connector, "keyPass", keyPassword); + setConnectorAttribute(connector, "keystorePass", props.of("sonar.web.https.keystorePass", keyPassword)); + setConnectorAttribute(connector, "keystoreFile", props.of("sonar.web.https.keystoreFile")); + setConnectorAttribute(connector, "keystoreType", props.of("sonar.web.https.keystoreType", "JKS")); + setConnectorAttribute(connector, "keystoreProvider", props.of("sonar.web.https.keystoreProvider")); + setConnectorAttribute(connector, "truststorePass", props.of("sonar.web.https.truststorePass", "changeit")); + setConnectorAttribute(connector, "truststoreFile", props.of("sonar.web.https.truststoreFile")); + setConnectorAttribute(connector, "truststoreType", props.of("sonar.web.https.truststoreType", "JKS")); + setConnectorAttribute(connector, "truststoreProvider", props.of("sonar.web.https.truststoreProvider")); + setConnectorAttribute(connector, "clientAuth", props.of("sonar.web.https.clientAuth", "false")); + setConnectorAttribute(connector, "sslProtocol", "TLS"); + setConnectorAttribute(connector, "SSLEnabled", true); + info("HTTPS connector is enabled on port " + port); + } + return connector; + } + + private static Connector newConnector(Props props, String protocol, String scheme) { + Connector connector = new Connector(protocol); + connector.setURIEncoding("UTF-8"); + connector.setProperty("address", props.of("sonar.web.host", "0.0.0.0")); + configurePool(props, connector, scheme); + configureCompression(connector); + return connector; + } + + private static void configurePool(Props props, Connector connector, String scheme) { + connector.setProperty("acceptorThreadCount", String.valueOf(2)); + connector.setProperty("minSpareThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".minThreads", 5))); + connector.setProperty("maxThreads", String.valueOf(props.intOf("sonar.web." + scheme + ".maxThreads", 50))); + connector.setProperty("acceptCount", String.valueOf(props.intOf("sonar.web." + scheme + ".acceptCount", 25))); + } + + private static void configureCompression(Connector connector) { + connector.setProperty("compression", "on"); + connector.setProperty("compressionMinSize", "1024"); + connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,text/css,application/json,application/javascript"); + } + + private static void setConnectorAttribute(Connector c, String key, @Nullable Object value) { + if (value != null) { + c.setAttribute(key, value); + } + } + + private static void info(String message) { + LoggerFactory.getLogger(Connectors.class).info(message); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java b/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java new file mode 100644 index 00000000000..50239fe282d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java @@ -0,0 +1,133 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.app; + +import org.apache.catalina.LifecycleException; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.apache.commons.io.FileUtils; +import org.sonar.process.Props; + +import java.io.File; + +class EmbeddedTomcat { + + private final Props props; + private Tomcat tomcat = null; + private Thread hook = null; + private boolean stopping = false, ready = false; + + EmbeddedTomcat(Props props) { + this.props = props; + } + + void start() { + if (tomcat != null || hook != null) { + throw new IllegalStateException("Server is already started"); + } + + try { + // '%2F' (slash /) and '%5C' (backslash \) are permitted as path delimiters in URLs + // See Ruby on Rails url_for + System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); + + System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); + + tomcat = new Tomcat(); + + // Initialize directories + File tomcatDir = tomcatBasedir(); + String basedir = tomcatDir.getAbsolutePath(); + tomcat.setBaseDir(basedir); + tomcat.getHost().setAppBase(basedir); + tomcat.getHost().setAutoDeploy(false); + tomcat.getHost().setCreateDirs(false); + tomcat.getHost().setDeployOnStartup(true); + + Logging.configure(tomcat, props); + Connectors.configure(tomcat, props); + Webapp.configure(tomcat, props); + tomcat.start(); + addShutdownHook(); + ready = true; + tomcat.getServer().await(); + } catch (Exception e) { + throw new IllegalStateException("Fail to start web server", e); + } + // Shutdown command received + stop(); + } + + private File tomcatBasedir() { + return new File(props.of("sonar.path.temp"), "tomcat"); + } + + private void addShutdownHook() { + hook = new Thread() { + @Override + public void run() { + EmbeddedTomcat.this.doStop(); + } + }; + Runtime.getRuntime().addShutdownHook(hook); + } + + + void stop() { + removeShutdownHook(); + doStop(); + } + + private synchronized void doStop() { + try { + if (tomcat != null && !stopping) { + stopping = true; + tomcat.stop(); + tomcat.destroy(); + } + tomcat = null; + stopping = false; + ready = false; + FileUtils.deleteQuietly(tomcatBasedir()); + + } catch (LifecycleException e) { + throw new IllegalStateException("Fail to stop web server", e); + } + } + + private void removeShutdownHook() { + if (hook != null && !hook.isAlive()) { + Runtime.getRuntime().removeShutdownHook(hook); + hook = null; + } + } + + boolean isReady() { + return ready; + } + + int port() { + Connector[] connectors = tomcat.getService().findConnectors(); + if (connectors.length > 0) { + return connectors[0].getLocalPort(); + } + return -1; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/Logging.java b/server/sonar-server/src/main/java/org/sonar/server/app/Logging.java new file mode 100644 index 00000000000..a4d1aaeec28 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/Logging.java @@ -0,0 +1,82 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.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.io.File; +import java.util.logging.LogManager; + +class Logging { + + static final String ACCESS_RELATIVE_PATH = "WEB-INF/config/logback-access.xml"; + static final String PROPERTY_ENABLE_ACCESS_LOGS = "sonar.web.accessLogs.enable"; + + static void init() { + // Configure java.util.logging, used by Tomcat, in order to forward to slf4j + LogManager.getLogManager().reset(); + SLF4JBridgeHandler.install(); + } + + static void configure(Tomcat tomcat, Props props) { + tomcat.setSilent(false); + tomcat.getService().addLifecycleListener(new LifecycleLogger(console())); + configureLogbackAccess(tomcat, props); + } + + static Logger console() { + return LoggerFactory.getLogger("console"); + } + + private static void configureLogbackAccess(Tomcat tomcat, Props props) { + if (props.booleanOf(PROPERTY_ENABLE_ACCESS_LOGS, true)) { + LogbackValve valve = new LogbackValve(); + valve.setQuiet(true); + valve.setFilename(new File(props.of("sonar.path.web"), ACCESS_RELATIVE_PATH).getAbsolutePath()); + tomcat.getHost().getPipeline().addValve(valve); + } + } + + static class LifecycleLogger implements LifecycleListener { + private Logger logger; + + LifecycleLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void lifecycleEvent(LifecycleEvent event) { + if ("after_start".equals(event.getType())) { + logger.info("Web server is started"); + + } else if ("after_destroy".equals(event.getType())) { + logger.info("Web server is stopped"); + } + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java b/server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java new file mode 100644 index 00000000000..4f8ac9e5312 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/NullJarScanner.java @@ -0,0 +1,36 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.app; + +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.JarScannerCallback; + +import javax.servlet.ServletContext; +import java.util.Set; + +/** + * Disable taglib and web-fragment.xml scanning of Tomcat. Should speed up startup. + */ +class NullJarScanner implements JarScanner { + @Override + public void scan(ServletContext context, ClassLoader classloader, JarScannerCallback callback, Set jarsToSkip) { + // doing nothing is fast! + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java b/server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java new file mode 100644 index 00000000000..35c9aba1e8f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/ServerProcess.java @@ -0,0 +1,51 @@ +/* + * 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(); + this.tomcat = new EmbeddedTomcat(props); + } + + @Override + public void onStart() { + tomcat.start(); + } + + @Override + public void onTerminate() { + tomcat.stop(); + } + + @Override + public boolean isReady() { + return tomcat.isReady(); + } + + public static void main(String[] args) { + new ServerProcess(args).start(); + LOGGER.info("ServerProcess is done"); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java b/server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java new file mode 100644 index 00000000000..1120f10230a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java @@ -0,0 +1,83 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.app; + +import org.apache.catalina.Context; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; +import org.slf4j.LoggerFactory; +import org.sonar.process.Props; + +import java.util.Map; + +class Webapp { + + private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes"; + private static final String RAILS_ENV = "rails.env"; + private static final String PROPERTY_CONTEXT = "sonar.web.context"; + + static void configure(Tomcat tomcat, Props props) { + try { + String webDir = props.of("sonar.path.web"); + StandardContext context = (StandardContext) tomcat.addWebapp(getContextPath(props), webDir); + context.setReloadable(false); + context.setUseHttpOnly(true); + context.setProcessTlds(false); + context.setTldValidation(false); + context.setTldNamespaceAware(false); + context.setXmlValidation(false); + context.setXmlNamespaceAware(false); + context.setUseNaming(false); + context.setDelegate(true); + for (Map.Entry entry : props.cryptedProperties().entrySet()) { + String key = entry.getKey().toString(); + if (key.startsWith("sonar.")) { + context.addParameter(key, entry.getValue().toString()); + } + } + configureRailsMode(props, context); + context.setJarScanner(new NullJarScanner()); + + } catch (Exception e) { + throw new IllegalStateException("Fail to configure webapp", e); + } + } + + static String getContextPath(Props props) { + String context = props.of(PROPERTY_CONTEXT, ""); + if ("/".equals(context)) { + context = ""; + } else if (!"".equals(context) && !context.startsWith("/")) { + throw new IllegalStateException(String.format("Value of '%s' must start with a forward slash: '%s'", PROPERTY_CONTEXT, context)); + } + return context; + } + + static void configureRailsMode(Props props, Context context) { + if (props.booleanOf("sonar.rails.dev")) { + context.addParameter(RAILS_ENV, "development"); + context.addParameter(JRUBY_MAX_RUNTIMES, "3"); + LoggerFactory.getLogger(Webapp.class).warn("\n\n\n------ RAILS DEVELOPMENT MODE IS ENABLED ------\n\n\n"); + } else { + context.addParameter(RAILS_ENV, "production"); + context.addParameter(JRUBY_MAX_RUNTIMES, "1"); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/app/package-info.java new file mode 100644 index 00000000000..5cccec43c1b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/app/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +@ParametersAreNonnullByDefault +package org.sonar.server.app; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-web/pom.xml b/server/sonar-web/pom.xml index 6e3ed52fcf7..881f84e83f1 100644 --- a/server/sonar-web/pom.xml +++ b/server/sonar-web/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.codehaus.sonar @@ -19,13 +20,6 @@ src/main/js/third-party/**/*,src/main/js/require.js,src/main/js/tests/**/* - - - org.codehaus.sonar - sonar-server - ${project.version} - - diff --git a/server/sonar-web/src/main/webapp/META-INF/context.xml b/server/sonar-web/src/main/webapp/META-INF/context.xml deleted file mode 100644 index db181e0854e..00000000000 --- a/server/sonar-web/src/main/webapp/META-INF/context.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/sonar-application/assembly.xml b/sonar-application/assembly.xml index 2304815d061..50553043ab1 100644 --- a/sonar-application/assembly.xml +++ b/sonar-application/assembly.xml @@ -38,8 +38,7 @@ org.postgresql:postgresql net.sourceforge.jtds:jtds tanukisoft:wrapper - tanukisoft:wrapper - org.codehaus.sonar:sonar-server-app + org.codehaus.sonar:sonar-server org.codehaus.sonar:sonar-web org.codehaus.sonar:sonar-search org.codehaus.sonar.plugins:* @@ -49,12 +48,24 @@ runtime + + lib/common + true + false + + org.elasticsearch:elasticsearch + + provided + + + + lib/search false true - true + false org.codehaus.sonar:sonar-search @@ -63,16 +74,16 @@ lib/server + false true true - org.codehaus.sonar:sonar-server-app + org.codehaus.sonar:sonar-server provided - lib/batch false diff --git a/sonar-application/pom.xml b/sonar-application/pom.xml index 52ec763c1e4..b9a5d8afa09 100644 --- a/sonar-application/pom.xml +++ b/sonar-application/pom.xml @@ -31,10 +31,15 @@ jsr305 provided + + org.elasticsearch + elasticsearch + provided + org.codehaus.sonar - sonar-server-app + sonar-server ${project.version} provided @@ -195,7 +200,6 @@ org.apache.maven.plugins maven-jar-plugin - 2.5 true diff --git a/sonar-batch-protocol/pom.xml b/sonar-batch-protocol/pom.xml index e596ef0ab90..35cc8af069a 100644 --- a/sonar-batch-protocol/pom.xml +++ b/sonar-batch-protocol/pom.xml @@ -13,11 +13,6 @@ Classes used for communication between batch and server - - com.esotericsoftware.kryo - kryo - 2.24.0 - com.google.code.gson gson -- cgit v1.2.3