contains the Web Server "executable", the Pico Container definition (to allow building it up from multiple modules) and Tomcat/JEE specific codetags/8.0
@@ -19,406 +19,16 @@ | |||
*/ | |||
package org.sonar.server.platform; | |||
import com.google.common.collect.Lists; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import java.util.function.Supplier; | |||
import javax.annotation.Nullable; | |||
import javax.servlet.ServletContext; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.utils.log.Profiler; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.server.app.ProcessCommandWrapper; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel1; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel2; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel3; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel4; | |||
import org.sonar.server.platform.platformlevel.PlatformLevelSafeMode; | |||
import org.sonar.server.platform.platformlevel.PlatformLevelStartup; | |||
/** | |||
* @since 2.2 | |||
*/ | |||
public class Platform { | |||
private static final Logger LOGGER = Loggers.get(Platform.class); | |||
private static final Platform INSTANCE = new Platform(); | |||
private final Supplier<AutoStarter> autoStarterSupplier; | |||
private AutoStarter autoStarter = null; | |||
private Properties properties; | |||
private ServletContext servletContext; | |||
private PlatformLevel level1; | |||
private PlatformLevel level2; | |||
private PlatformLevel levelSafeMode; | |||
private PlatformLevel level3; | |||
private PlatformLevel level4; | |||
private PlatformLevel currentLevel; | |||
private boolean dbConnected = false; | |||
private boolean started = false; | |||
private final List<Object> level4AddedComponents = Lists.newArrayList(); | |||
private final Profiler profiler = Profiler.createIfTrace(Loggers.get(Platform.class)); | |||
private Platform() { | |||
this.autoStarterSupplier = () -> { | |||
ProcessCommandWrapper processCommandWrapper = getContainer().getComponentByType(ProcessCommandWrapper.class); | |||
return new AsynchronousAutoStarter(processCommandWrapper); | |||
}; | |||
} | |||
protected Platform(Supplier<AutoStarter> autoStarterSupplier) { | |||
this.autoStarterSupplier = autoStarterSupplier; | |||
} | |||
public static Platform getInstance() { | |||
return INSTANCE; | |||
} | |||
public void init(Properties properties, ServletContext servletContext) { | |||
this.properties = properties; | |||
this.servletContext = servletContext; | |||
if (!dbConnected) { | |||
startLevel1Container(); | |||
startLevel2Container(); | |||
currentLevel = level2; | |||
dbConnected = true; | |||
} | |||
} | |||
// Platform is injected in Pico, so do not rename this method "start" | |||
public void doStart() { | |||
doStart(Startup.ALL); | |||
} | |||
protected void doStart(Startup startup) { | |||
if (started && !isInSafeMode()) { | |||
return; | |||
} | |||
boolean dbRequiredMigration = dbRequiresMigration(); | |||
startSafeModeContainer(); | |||
currentLevel = levelSafeMode; | |||
started = true; | |||
// if AutoDbMigration kicked in or no DB migration was required, startup can be resumed in another thread | |||
if (dbRequiresMigration()) { | |||
LOGGER.info("Database needs to be migrated. Please refer to https://docs.sonarqube.org/latest/setup/upgrading"); | |||
} else { | |||
this.autoStarter = autoStarterSupplier.get(); | |||
this.autoStarter.execute(new AutoStarterRunnable(autoStarter) { | |||
@Override | |||
public void doRun() { | |||
if (dbRequiredMigration) { | |||
LOGGER.info("Database has been automatically updated"); | |||
} | |||
runIfNotAborted(Platform.this::startLevel34Containers); | |||
runIfNotAborted(() -> executeStartupTasks(startup)); | |||
// switch current container last to avoid giving access to a partially initialized container | |||
runIfNotAborted(() -> { | |||
currentLevel = level4; | |||
LOGGER.info("WebServer is operational"); | |||
}); | |||
// stop safemode container if it existed | |||
runIfNotAborted(Platform.this::stopSafeModeContainer); | |||
} | |||
}); | |||
} | |||
} | |||
private boolean dbRequiresMigration() { | |||
return getDatabaseStatus() != DatabaseVersion.Status.UP_TO_DATE; | |||
} | |||
public interface Platform { | |||
void doStart(); | |||
public boolean isStarted() { | |||
return status() == Status.UP; | |||
} | |||
public boolean isInSafeMode() { | |||
return status() == Status.SAFEMODE; | |||
} | |||
public Status status() { | |||
if (!started) { | |||
return Status.BOOTING; | |||
} | |||
PlatformLevel current = this.currentLevel; | |||
PlatformLevel levelSafe = this.levelSafeMode; | |||
if (levelSafe != null && current == levelSafe) { | |||
return isRunning(this.autoStarter) ? Status.STARTING : Status.SAFEMODE; | |||
} | |||
if (current == level4) { | |||
return Status.UP; | |||
} | |||
return Status.BOOTING; | |||
} | |||
private static boolean isRunning(@Nullable AutoStarter autoStarter) { | |||
return autoStarter != null && autoStarter.isRunning(); | |||
} | |||
/** | |||
* Starts level 1 | |||
*/ | |||
private void startLevel1Container() { | |||
level1 = start(new PlatformLevel1(this, properties, servletContext)); | |||
} | |||
/** | |||
* Starts level 2 | |||
*/ | |||
private void startLevel2Container() { | |||
level2 = start(new PlatformLevel2(level1)); | |||
} | |||
Status status(); | |||
/** | |||
* Starts level 3 and 4 | |||
*/ | |||
private void startLevel34Containers() { | |||
level3 = start(new PlatformLevel3(level2)); | |||
level4 = start(new PlatformLevel4(level3, level4AddedComponents)); | |||
} | |||
public void executeStartupTasks() { | |||
executeStartupTasks(Startup.ALL); | |||
} | |||
ComponentContainer getContainer(); | |||
private void executeStartupTasks(Startup startup) { | |||
if (startup.ordinal() >= Startup.ALL.ordinal()) { | |||
new PlatformLevelStartup(level4) | |||
.configure() | |||
.start() | |||
.stop() | |||
.destroy(); | |||
} | |||
} | |||
private void startSafeModeContainer() { | |||
levelSafeMode = start(new PlatformLevelSafeMode(level2)); | |||
} | |||
private PlatformLevel start(PlatformLevel platformLevel) { | |||
profiler.start(); | |||
platformLevel.configure(); | |||
profiler.stopTrace(String.format("%s configured", platformLevel.getName())); | |||
profiler.start(); | |||
platformLevel.start(); | |||
profiler.stopTrace(String.format("%s started", platformLevel.getName())); | |||
return platformLevel; | |||
} | |||
/** | |||
* Stops level 1 | |||
*/ | |||
private void stopLevel1Container() { | |||
if (level1 != null) { | |||
level1.stop(); | |||
level1 = null; | |||
} | |||
} | |||
/** | |||
* Stops level 2, 3 and 4 containers cleanly if they exists. | |||
* Call this method before {@link #startLevel1Container()} to avoid duplicate attempt to stop safemode container | |||
* components (since calling stop on a container calls stop on its children too, see | |||
* {@link ComponentContainer#stopComponents()}). | |||
*/ | |||
private void stopLevel234Containers() { | |||
if (level2 != null) { | |||
level2.stop(); | |||
level2 = null; | |||
level3 = null; | |||
level4 = null; | |||
} | |||
} | |||
/** | |||
* Stops safemode container cleanly if it exists. | |||
* Call this method before {@link #stopLevel234Containers()} and {@link #stopLevel1Container()} to avoid duplicate | |||
* attempt to stop safemode container components (since calling stop on a container calls stops on its children too, | |||
* see {@link ComponentContainer#stopComponents()}). | |||
*/ | |||
private void stopSafeModeContainer() { | |||
if (levelSafeMode != null) { | |||
levelSafeMode.stop(); | |||
levelSafeMode = null; | |||
} | |||
} | |||
private DatabaseVersion.Status getDatabaseStatus() { | |||
DatabaseVersion version = getContainer().getComponentByType(DatabaseVersion.class); | |||
return version.getStatus(); | |||
} | |||
// Do not rename "stop" | |||
public void doStop() { | |||
try { | |||
stopAutoStarter(); | |||
stopSafeModeContainer(); | |||
stopLevel234Containers(); | |||
stopLevel1Container(); | |||
currentLevel = null; | |||
dbConnected = false; | |||
started = false; | |||
} catch (Exception e) { | |||
LOGGER.error("Fail to stop server - ignored", e); | |||
} | |||
} | |||
private void stopAutoStarter() { | |||
if (autoStarter != null) { | |||
autoStarter.abort(); | |||
autoStarter = null; | |||
} | |||
} | |||
public void addComponents(Collection<?> components) { | |||
level4AddedComponents.addAll(components); | |||
} | |||
public ComponentContainer getContainer() { | |||
return currentLevel.getContainer(); | |||
} | |||
public Object getComponent(Object key) { | |||
return getContainer().getComponentByKey(key); | |||
} | |||
public enum Status { | |||
enum Status { | |||
BOOTING, SAFEMODE, STARTING, UP | |||
} | |||
public enum Startup { | |||
NO_STARTUP_TASKS, ALL | |||
} | |||
public interface AutoStarter { | |||
/** | |||
* Let the autostarted execute the provided code. | |||
*/ | |||
void execute(Runnable startCode); | |||
/** | |||
* This method is called by executed start code (see {@link #execute(Runnable)} has finished with a failure. | |||
*/ | |||
void failure(Throwable t); | |||
/** | |||
* This method is called by executed start code (see {@link #execute(Runnable)} has finished successfully. | |||
*/ | |||
void success(); | |||
/** | |||
* Indicates whether the AutoStarter is running. | |||
*/ | |||
boolean isRunning(); | |||
/** | |||
* Requests the startcode (ie. the argument of {@link #execute(Runnable)}) aborts its processing (if it supports it). | |||
*/ | |||
void abort(); | |||
/** | |||
* Indicates whether {@link #abort()} was invoked. | |||
* <p> | |||
* This method can be used by the start code to check whether it should proceed running or stop. | |||
* </p> | |||
*/ | |||
boolean isAborting(); | |||
/** | |||
* Called when abortion is complete. | |||
* <p> | |||
* Start code support abortion should call this method once is done processing and if it stopped on abortion. | |||
* </p> | |||
*/ | |||
void aborted(); | |||
} | |||
private abstract static class AutoStarterRunnable implements Runnable { | |||
private final AutoStarter autoStarter; | |||
AutoStarterRunnable(AutoStarter autoStarter) { | |||
this.autoStarter = autoStarter; | |||
} | |||
@Override | |||
public void run() { | |||
try { | |||
doRun(); | |||
} catch (Throwable t) { | |||
autoStarter.failure(t); | |||
} finally { | |||
if (autoStarter.isAborting()) { | |||
autoStarter.aborted(); | |||
} else { | |||
autoStarter.success(); | |||
} | |||
} | |||
} | |||
abstract void doRun(); | |||
void runIfNotAborted(Runnable r) { | |||
if (!autoStarter.isAborting()) { | |||
r.run(); | |||
} | |||
} | |||
} | |||
private static final class AsynchronousAutoStarter implements AutoStarter { | |||
private final ProcessCommandWrapper processCommandWrapper; | |||
private boolean running = true; | |||
private boolean abort = false; | |||
private AsynchronousAutoStarter(ProcessCommandWrapper processCommandWrapper) { | |||
this.processCommandWrapper = processCommandWrapper; | |||
} | |||
@Override | |||
public void execute(Runnable startCode) { | |||
new Thread(startCode, "SQ starter").start(); | |||
} | |||
@Override | |||
public void failure(Throwable t) { | |||
LOGGER.error("Background initialization failed. Stopping SonarQube", t); | |||
processCommandWrapper.requestHardStop(); | |||
this.running = false; | |||
} | |||
@Override | |||
public void success() { | |||
LOGGER.debug("Background initialization of SonarQube done"); | |||
this.running = false; | |||
} | |||
@Override | |||
public void aborted() { | |||
LOGGER.debug("Background initialization of SonarQube aborted"); | |||
this.running = false; | |||
} | |||
@Override | |||
public boolean isRunning() { | |||
return running; | |||
} | |||
@Override | |||
public void abort() { | |||
this.abort = true; | |||
} | |||
@Override | |||
public boolean isAborting() { | |||
return this.abort; | |||
} | |||
} | |||
} |
@@ -29,7 +29,7 @@ import static java.util.Objects.requireNonNull; | |||
public class RequestIdMDCStorage implements AutoCloseable { | |||
public static final String HTTP_REQUEST_ID_MDC_KEY = "HTTP_REQUEST_ID"; | |||
RequestIdMDCStorage(String requestId) { | |||
public RequestIdMDCStorage(String requestId) { | |||
MDC.put(HTTP_REQUEST_ID_MDC_KEY, requireNonNull(requestId, "Request ID can't be null")); | |||
} | |||
@@ -14,7 +14,7 @@ | |||
</filter> | |||
<filter> | |||
<filter-name>UserSessionFilter</filter-name> | |||
<filter-class>org.sonar.server.user.UserSessionFilter</filter-class> | |||
<filter-class>org.sonar.server.platform.web.UserSessionFilter</filter-class> | |||
</filter> | |||
<filter> | |||
<filter-name>SetCharacterEncodingFilter</filter-name> | |||
@@ -38,7 +38,7 @@ | |||
</filter> | |||
<filter> | |||
<filter-name>RequestUidFilter</filter-name> | |||
<filter-class>org.sonar.server.platform.web.requestid.RequestIdFilter</filter-class> | |||
<filter-class>org.sonar.server.platform.web.RequestIdFilter</filter-class> | |||
</filter> | |||
<filter> | |||
<filter-name>WebPagesFilter</filter-name> | |||
@@ -89,7 +89,7 @@ | |||
<servlet> | |||
<servlet-name>static</servlet-name> | |||
<servlet-class>org.sonar.server.plugins.StaticResourcesServlet</servlet-class> | |||
<servlet-class>org.sonar.server.platform.web.StaticResourcesServlet</servlet-class> | |||
</servlet> | |||
<servlet-mapping> |
@@ -0,0 +1,26 @@ | |||
description = 'SonarQube WebServer' | |||
sonarqube { | |||
properties { | |||
property 'sonar.projectName', "${projectTitle} :: WebServer" | |||
} | |||
} | |||
dependencies { | |||
// please keep the list grouped by configuration and ordered by name | |||
compile 'com.google.guava:guava' | |||
compile 'org.apache.tomcat.embed:tomcat-embed-core' | |||
compile project(':sonar-core') | |||
compile project(':server:sonar-process') | |||
compile project(':server:sonar-server') | |||
compile project(':server:sonar-server-common') | |||
compileOnly 'com.google.code.findbugs:jsr305' | |||
testCompile 'com.google.code.findbugs:jsr305' | |||
testCompile 'org.eclipse.jetty:jetty-server' | |||
testCompile 'org.eclipse.jetty:jetty-servlet' | |||
testCompile 'org.mockito:mockito-core' | |||
testCompile project(':sonar-testing-harness') | |||
} |
@@ -0,0 +1,423 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.platform; | |||
import com.google.common.collect.Lists; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Properties; | |||
import java.util.function.Supplier; | |||
import javax.annotation.Nullable; | |||
import javax.servlet.ServletContext; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.utils.log.Profiler; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.server.app.ProcessCommandWrapper; | |||
import org.sonar.server.platform.db.migration.version.DatabaseVersion; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel1; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel2; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel3; | |||
import org.sonar.server.platform.platformlevel.PlatformLevel4; | |||
import org.sonar.server.platform.platformlevel.PlatformLevelSafeMode; | |||
import org.sonar.server.platform.platformlevel.PlatformLevelStartup; | |||
/** | |||
* @since 2.2 | |||
*/ | |||
public class PlatformImpl implements Platform { | |||
private static final Logger LOGGER = Loggers.get(Platform.class); | |||
private static final PlatformImpl INSTANCE = new PlatformImpl(); | |||
private final Supplier<AutoStarter> autoStarterSupplier; | |||
private AutoStarter autoStarter = null; | |||
private Properties properties; | |||
private ServletContext servletContext; | |||
private PlatformLevel level1; | |||
private PlatformLevel level2; | |||
private PlatformLevel levelSafeMode; | |||
private PlatformLevel level3; | |||
private PlatformLevel level4; | |||
private PlatformLevel currentLevel; | |||
private boolean dbConnected = false; | |||
private boolean started = false; | |||
private final List<Object> level4AddedComponents = Lists.newArrayList(); | |||
private final Profiler profiler = Profiler.createIfTrace(Loggers.get(PlatformImpl.class)); | |||
private PlatformImpl() { | |||
this.autoStarterSupplier = () -> { | |||
ProcessCommandWrapper processCommandWrapper = getContainer().getComponentByType(ProcessCommandWrapper.class); | |||
return new AsynchronousAutoStarter(processCommandWrapper); | |||
}; | |||
} | |||
protected PlatformImpl(Supplier<AutoStarter> autoStarterSupplier) { | |||
this.autoStarterSupplier = autoStarterSupplier; | |||
} | |||
public static PlatformImpl getInstance() { | |||
return INSTANCE; | |||
} | |||
public void init(Properties properties, ServletContext servletContext) { | |||
this.properties = properties; | |||
this.servletContext = servletContext; | |||
if (!dbConnected) { | |||
startLevel1Container(); | |||
startLevel2Container(); | |||
currentLevel = level2; | |||
dbConnected = true; | |||
} | |||
} | |||
// Platform is injected in Pico, so do not rename this method "start" | |||
@Override | |||
public void doStart() { | |||
doStart(Startup.ALL); | |||
} | |||
protected void doStart(Startup startup) { | |||
if (started && !isInSafeMode()) { | |||
return; | |||
} | |||
boolean dbRequiredMigration = dbRequiresMigration(); | |||
startSafeModeContainer(); | |||
currentLevel = levelSafeMode; | |||
started = true; | |||
// if AutoDbMigration kicked in or no DB migration was required, startup can be resumed in another thread | |||
if (dbRequiresMigration()) { | |||
LOGGER.info("Database needs to be migrated. Please refer to https://docs.sonarqube.org/latest/setup/upgrading"); | |||
} else { | |||
this.autoStarter = autoStarterSupplier.get(); | |||
this.autoStarter.execute(new AutoStarterRunnable(autoStarter) { | |||
@Override | |||
public void doRun() { | |||
if (dbRequiredMigration) { | |||
LOGGER.info("Database has been automatically updated"); | |||
} | |||
runIfNotAborted(PlatformImpl.this::startLevel34Containers); | |||
runIfNotAborted(() -> executeStartupTasks(startup)); | |||
// switch current container last to avoid giving access to a partially initialized container | |||
runIfNotAborted(() -> { | |||
currentLevel = level4; | |||
LOGGER.info("WebServer is operational"); | |||
}); | |||
// stop safemode container if it existed | |||
runIfNotAborted(PlatformImpl.this::stopSafeModeContainer); | |||
} | |||
}); | |||
} | |||
} | |||
private boolean dbRequiresMigration() { | |||
return getDatabaseStatus() != DatabaseVersion.Status.UP_TO_DATE; | |||
} | |||
public boolean isStarted() { | |||
return status() == Status.UP; | |||
} | |||
public boolean isInSafeMode() { | |||
return status() == Status.SAFEMODE; | |||
} | |||
@Override | |||
public Status status() { | |||
if (!started) { | |||
return Status.BOOTING; | |||
} | |||
PlatformLevel current = this.currentLevel; | |||
PlatformLevel levelSafe = this.levelSafeMode; | |||
if (levelSafe != null && current == levelSafe) { | |||
return isRunning(this.autoStarter) ? Status.STARTING : Status.SAFEMODE; | |||
} | |||
if (current == level4) { | |||
return Status.UP; | |||
} | |||
return Status.BOOTING; | |||
} | |||
private static boolean isRunning(@Nullable AutoStarter autoStarter) { | |||
return autoStarter != null && autoStarter.isRunning(); | |||
} | |||
/** | |||
* Starts level 1 | |||
*/ | |||
private void startLevel1Container() { | |||
level1 = start(new PlatformLevel1(this, properties, servletContext)); | |||
} | |||
/** | |||
* Starts level 2 | |||
*/ | |||
private void startLevel2Container() { | |||
level2 = start(new PlatformLevel2(level1)); | |||
} | |||
/** | |||
* Starts level 3 and 4 | |||
*/ | |||
private void startLevel34Containers() { | |||
level3 = start(new PlatformLevel3(level2)); | |||
level4 = start(new PlatformLevel4(level3, level4AddedComponents)); | |||
} | |||
public void executeStartupTasks() { | |||
executeStartupTasks(Startup.ALL); | |||
} | |||
private void executeStartupTasks(Startup startup) { | |||
if (startup.ordinal() >= Startup.ALL.ordinal()) { | |||
new PlatformLevelStartup(level4) | |||
.configure() | |||
.start() | |||
.stop() | |||
.destroy(); | |||
} | |||
} | |||
private void startSafeModeContainer() { | |||
levelSafeMode = start(new PlatformLevelSafeMode(level2)); | |||
} | |||
private PlatformLevel start(PlatformLevel platformLevel) { | |||
profiler.start(); | |||
platformLevel.configure(); | |||
profiler.stopTrace(String.format("%s configured", platformLevel.getName())); | |||
profiler.start(); | |||
platformLevel.start(); | |||
profiler.stopTrace(String.format("%s started", platformLevel.getName())); | |||
return platformLevel; | |||
} | |||
/** | |||
* Stops level 1 | |||
*/ | |||
private void stopLevel1Container() { | |||
if (level1 != null) { | |||
level1.stop(); | |||
level1 = null; | |||
} | |||
} | |||
/** | |||
* Stops level 2, 3 and 4 containers cleanly if they exists. | |||
* Call this method before {@link #startLevel1Container()} to avoid duplicate attempt to stop safemode container | |||
* components (since calling stop on a container calls stop on its children too, see | |||
* {@link ComponentContainer#stopComponents()}). | |||
*/ | |||
private void stopLevel234Containers() { | |||
if (level2 != null) { | |||
level2.stop(); | |||
level2 = null; | |||
level3 = null; | |||
level4 = null; | |||
} | |||
} | |||
/** | |||
* Stops safemode container cleanly if it exists. | |||
* Call this method before {@link #stopLevel234Containers()} and {@link #stopLevel1Container()} to avoid duplicate | |||
* attempt to stop safemode container components (since calling stop on a container calls stops on its children too, | |||
* see {@link ComponentContainer#stopComponents()}). | |||
*/ | |||
private void stopSafeModeContainer() { | |||
if (levelSafeMode != null) { | |||
levelSafeMode.stop(); | |||
levelSafeMode = null; | |||
} | |||
} | |||
private DatabaseVersion.Status getDatabaseStatus() { | |||
DatabaseVersion version = getContainer().getComponentByType(DatabaseVersion.class); | |||
return version.getStatus(); | |||
} | |||
// Do not rename "stop" | |||
public void doStop() { | |||
try { | |||
stopAutoStarter(); | |||
stopSafeModeContainer(); | |||
stopLevel234Containers(); | |||
stopLevel1Container(); | |||
currentLevel = null; | |||
dbConnected = false; | |||
started = false; | |||
} catch (Exception e) { | |||
LOGGER.error("Fail to stop server - ignored", e); | |||
} | |||
} | |||
private void stopAutoStarter() { | |||
if (autoStarter != null) { | |||
autoStarter.abort(); | |||
autoStarter = null; | |||
} | |||
} | |||
public void addComponents(Collection<?> components) { | |||
level4AddedComponents.addAll(components); | |||
} | |||
@Override | |||
public ComponentContainer getContainer() { | |||
return currentLevel.getContainer(); | |||
} | |||
public Object getComponent(Object key) { | |||
return getContainer().getComponentByKey(key); | |||
} | |||
public enum Startup { | |||
NO_STARTUP_TASKS, ALL | |||
} | |||
public interface AutoStarter { | |||
/** | |||
* Let the autostarted execute the provided code. | |||
*/ | |||
void execute(Runnable startCode); | |||
/** | |||
* This method is called by executed start code (see {@link #execute(Runnable)} has finished with a failure. | |||
*/ | |||
void failure(Throwable t); | |||
/** | |||
* This method is called by executed start code (see {@link #execute(Runnable)} has finished successfully. | |||
*/ | |||
void success(); | |||
/** | |||
* Indicates whether the AutoStarter is running. | |||
*/ | |||
boolean isRunning(); | |||
/** | |||
* Requests the startcode (ie. the argument of {@link #execute(Runnable)}) aborts its processing (if it supports it). | |||
*/ | |||
void abort(); | |||
/** | |||
* Indicates whether {@link #abort()} was invoked. | |||
* <p> | |||
* This method can be used by the start code to check whether it should proceed running or stop. | |||
* </p> | |||
*/ | |||
boolean isAborting(); | |||
/** | |||
* Called when abortion is complete. | |||
* <p> | |||
* Start code support abortion should call this method once is done processing and if it stopped on abortion. | |||
* </p> | |||
*/ | |||
void aborted(); | |||
} | |||
private abstract static class AutoStarterRunnable implements Runnable { | |||
private final AutoStarter autoStarter; | |||
AutoStarterRunnable(AutoStarter autoStarter) { | |||
this.autoStarter = autoStarter; | |||
} | |||
@Override | |||
public void run() { | |||
try { | |||
doRun(); | |||
} catch (Throwable t) { | |||
autoStarter.failure(t); | |||
} finally { | |||
if (autoStarter.isAborting()) { | |||
autoStarter.aborted(); | |||
} else { | |||
autoStarter.success(); | |||
} | |||
} | |||
} | |||
abstract void doRun(); | |||
void runIfNotAborted(Runnable r) { | |||
if (!autoStarter.isAborting()) { | |||
r.run(); | |||
} | |||
} | |||
} | |||
private static final class AsynchronousAutoStarter implements AutoStarter { | |||
private final ProcessCommandWrapper processCommandWrapper; | |||
private boolean running = true; | |||
private boolean abort = false; | |||
private AsynchronousAutoStarter(ProcessCommandWrapper processCommandWrapper) { | |||
this.processCommandWrapper = processCommandWrapper; | |||
} | |||
@Override | |||
public void execute(Runnable startCode) { | |||
new Thread(startCode, "SQ starter").start(); | |||
} | |||
@Override | |||
public void failure(Throwable t) { | |||
LOGGER.error("Background initialization failed. Stopping SonarQube", t); | |||
processCommandWrapper.requestHardStop(); | |||
this.running = false; | |||
} | |||
@Override | |||
public void success() { | |||
LOGGER.debug("Background initialization of SonarQube done"); | |||
this.running = false; | |||
} | |||
@Override | |||
public void aborted() { | |||
LOGGER.debug("Background initialization of SonarQube aborted"); | |||
this.running = false; | |||
} | |||
@Override | |||
public boolean isRunning() { | |||
return running; | |||
} | |||
@Override | |||
public void abort() { | |||
this.abort = true; | |||
} | |||
@Override | |||
public boolean isAborting() { | |||
return this.abort; | |||
} | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.platform; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -115,7 +115,6 @@ import org.sonar.server.platform.PersistentSettings; | |||
import org.sonar.server.platform.SettingsChangeNotifier; | |||
import org.sonar.server.platform.WebCoreExtensionsInstaller; | |||
import org.sonar.server.platform.monitoring.WebSystemInfoModule; | |||
import org.sonar.server.platform.web.WebPagesFilter; | |||
import org.sonar.server.platform.web.requestid.HttpRequestIdModule; | |||
import org.sonar.server.platform.ws.ChangeLogLevelActionModule; | |||
import org.sonar.server.platform.ws.DbMigrationStatusAction; | |||
@@ -255,7 +254,6 @@ public class PlatformLevel4 extends PlatformLevel { | |||
ServerWs.class, | |||
BackendCleanup.class, | |||
IndexDefinitions.class, | |||
WebPagesFilter.class, | |||
WebAnalyticsLoaderImpl.class, | |||
// batch |
@@ -19,7 +19,6 @@ | |||
*/ | |||
package org.sonar.server.platform.platformlevel; | |||
import org.sonar.server.platform.ws.SafeModeHealthActionModule; | |||
import org.sonar.server.authentication.SafeModeUserSession; | |||
import org.sonar.server.organization.NoopDefaultOrganizationCache; | |||
import org.sonar.server.platform.ServerImpl; | |||
@@ -27,11 +26,11 @@ import org.sonar.server.platform.db.migration.AutoDbMigration; | |||
import org.sonar.server.platform.db.migration.DatabaseMigrationImpl; | |||
import org.sonar.server.platform.db.migration.MigrationEngineModule; | |||
import org.sonar.server.platform.db.migration.NoopDatabaseMigrationImpl; | |||
import org.sonar.server.platform.web.WebPagesFilter; | |||
import org.sonar.server.platform.ws.DbMigrationStatusAction; | |||
import org.sonar.server.platform.ws.IndexAction; | |||
import org.sonar.server.platform.ws.L10nWs; | |||
import org.sonar.server.platform.ws.MigrateDbAction; | |||
import org.sonar.server.platform.ws.SafeModeHealthActionModule; | |||
import org.sonar.server.platform.ws.StatusAction; | |||
import org.sonar.server.platform.ws.SystemWs; | |||
import org.sonar.server.ws.WebServiceEngine; | |||
@@ -47,7 +46,6 @@ public class PlatformLevelSafeMode extends PlatformLevel { | |||
protected void configureLevel() { | |||
add( | |||
ServerImpl.class, | |||
WebPagesFilter.class, | |||
// l10n WS | |||
L10nWs.class, |
@@ -34,7 +34,7 @@ import javax.servlet.ServletResponse; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.web.ServletFilter; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.PlatformImpl; | |||
/** | |||
* Inspired by http://stackoverflow.com/a/7592883/229031 | |||
@@ -56,7 +56,7 @@ public class MasterServletFilter implements Filter { | |||
public void init(FilterConfig config) { | |||
// Filters are already available in picocontainer unless a database migration is required. See | |||
// org.sonar.server.startup.RegisterServletFilters. | |||
init(config, Platform.getInstance().getContainer().getComponentsByType(ServletFilter.class)); | |||
init(config, PlatformImpl.getInstance().getContainer().getComponentsByType(ServletFilter.class)); | |||
} | |||
void init(FilterConfig config, List<ServletFilter> filters) { |
@@ -25,7 +25,7 @@ import javax.servlet.ServletContext; | |||
import javax.servlet.ServletContextEvent; | |||
import javax.servlet.ServletContextListener; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.PlatformImpl; | |||
public final class PlatformServletContextListener implements ServletContextListener { | |||
static final String STARTED_ATTRIBUTE = "sonarqube.started"; | |||
@@ -40,14 +40,14 @@ public final class PlatformServletContextListener implements ServletContextListe | |||
String key = paramKeys.nextElement(); | |||
props.put(key, servletContext.getInitParameter(key)); | |||
} | |||
Platform.getInstance().init(props, servletContext); | |||
Platform.getInstance().doStart(); | |||
PlatformImpl.getInstance().init(props, servletContext); | |||
PlatformImpl.getInstance().doStart(); | |||
event.getServletContext().setAttribute(STARTED_ATTRIBUTE, Boolean.TRUE); | |||
} catch (org.sonar.api.utils.MessageException | org.sonar.process.MessageException e) { | |||
Loggers.get(Platform.class).error("Web server startup failed: " + e.getMessage()); | |||
Loggers.get(PlatformImpl.class).error("Web server startup failed: " + e.getMessage()); | |||
stopQuietly(); | |||
} catch (Throwable t) { | |||
Loggers.get(Platform.class).error("Web server startup failed", t); | |||
Loggers.get(PlatformImpl.class).error("Web server startup failed", t); | |||
stopQuietly(); | |||
throw new AbortTomcatStartException(); | |||
} | |||
@@ -55,7 +55,7 @@ public final class PlatformServletContextListener implements ServletContextListe | |||
private void stopQuietly() { | |||
try { | |||
Platform.getInstance().doStop(); | |||
PlatformImpl.getInstance().doStop(); | |||
} catch (Exception e) { | |||
// ignored, but an error during startup generally prevents pico to be correctly stopped | |||
} | |||
@@ -63,7 +63,7 @@ public final class PlatformServletContextListener implements ServletContextListe | |||
@Override | |||
public void contextDestroyed(ServletContextEvent event) { | |||
Platform.getInstance().doStop(); | |||
PlatformImpl.getInstance().doStop(); | |||
} | |||
} |
@@ -17,7 +17,7 @@ | |||
* 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.platform.web.requestid; | |||
package org.sonar.server.platform.web; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import java.io.IOException; | |||
@@ -28,6 +28,9 @@ import javax.servlet.ServletException; | |||
import javax.servlet.ServletRequest; | |||
import javax.servlet.ServletResponse; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.PlatformImpl; | |||
import org.sonar.server.platform.web.requestid.RequestIdGenerator; | |||
import org.sonar.server.platform.web.requestid.RequestIdMDCStorage; | |||
/** | |||
* A {@link Filter} that puts and removes the HTTP request ID from the {@link org.slf4j.MDC}. | |||
@@ -37,7 +40,7 @@ public class RequestIdFilter implements Filter { | |||
private final Platform platform; | |||
public RequestIdFilter() { | |||
this(Platform.getInstance()); | |||
this(PlatformImpl.getInstance()); | |||
} | |||
@VisibleForTesting |
@@ -17,7 +17,7 @@ | |||
* 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.plugins; | |||
package org.sonar.server.platform.web; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import java.io.IOException; | |||
@@ -34,7 +34,7 @@ import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.core.platform.PluginRepository; | |||
import org.sonar.core.extension.CoreExtensionRepository; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.PlatformImpl; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static java.lang.String.format; | |||
@@ -130,11 +130,11 @@ public class StaticResourcesServlet extends HttpServlet { | |||
static class System { | |||
PluginRepository getPluginRepository() { | |||
return Platform.getInstance().getContainer().getComponentByType(PluginRepository.class); | |||
return PlatformImpl.getInstance().getContainer().getComponentByType(PluginRepository.class); | |||
} | |||
CoreExtensionRepository getCoreExtensionRepository() { | |||
return Platform.getInstance().getContainer().getComponentByType(CoreExtensionRepository.class); | |||
return PlatformImpl.getInstance().getContainer().getComponentByType(CoreExtensionRepository.class); | |||
} | |||
@CheckForNull |
@@ -17,7 +17,7 @@ | |||
* 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.user; | |||
package org.sonar.server.platform.web; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import java.io.IOException; | |||
@@ -36,6 +36,7 @@ import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.server.authentication.UserSessionInitializer; | |||
import org.sonar.server.organization.DefaultOrganizationCache; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.PlatformImpl; | |||
import org.sonar.server.setting.ThreadLocalSettings; | |||
public class UserSessionFilter implements Filter { | |||
@@ -43,7 +44,7 @@ public class UserSessionFilter implements Filter { | |||
private final Platform platform; | |||
public UserSessionFilter() { | |||
this.platform = Platform.getInstance(); | |||
this.platform = PlatformImpl.getInstance(); | |||
} | |||
@VisibleForTesting |
@@ -30,7 +30,7 @@ import javax.servlet.ServletResponse; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import org.sonar.api.web.ServletFilter; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.PlatformImpl; | |||
import static java.nio.charset.StandardCharsets.UTF_8; | |||
import static java.util.Locale.ENGLISH; | |||
@@ -56,7 +56,7 @@ public class WebPagesFilter implements Filter { | |||
private WebPagesCache webPagesCache; | |||
public WebPagesFilter() { | |||
this(Platform.getInstance().getContainer().getComponentByType(WebPagesCache.class)); | |||
this(PlatformImpl.getInstance().getContainer().getComponentByType(WebPagesCache.class)); | |||
} | |||
@VisibleForTesting |
@@ -0,0 +1,23 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.platform.web; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -17,13 +17,11 @@ | |||
* 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.startup; | |||
package org.sonar.server.platform.web; | |||
import javax.servlet.ServletException; | |||
import org.junit.Test; | |||
import org.sonar.api.web.ServletFilter; | |||
import org.sonar.server.platform.web.MasterServletFilter; | |||
import org.sonar.server.platform.web.RegisterServletFilters; | |||
import static org.mockito.ArgumentMatchers.anyList; | |||
import static org.mockito.Mockito.mock; |
@@ -17,7 +17,7 @@ | |||
* 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.platform.web.requestid; | |||
package org.sonar.server.platform.web; | |||
import java.io.IOException; | |||
import javax.servlet.FilterChain; | |||
@@ -29,6 +29,7 @@ import org.junit.Test; | |||
import org.slf4j.MDC; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.platform.web.requestid.RequestIdGenerator; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.fail; | |||
@@ -48,7 +49,7 @@ public class RequestIdFilterTest { | |||
private RequestIdFilter underTest = new RequestIdFilter(platform); | |||
@Before | |||
public void setUp() throws Exception { | |||
public void setUp() { | |||
ComponentContainer container = new ComponentContainer(); | |||
container.add(requestIdGenerator); | |||
when(platform.getContainer()).thenReturn(container); |
@@ -17,7 +17,7 @@ | |||
* 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.plugins; | |||
package org.sonar.server.platform.web; | |||
import java.io.IOException; | |||
import java.io.InputStream; |
@@ -17,7 +17,7 @@ | |||
* 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.user; | |||
package org.sonar.server.platform.web; | |||
import java.io.IOException; | |||
import javax.servlet.FilterChain; |
@@ -55,7 +55,7 @@ public class WebPagesCacheTest { | |||
private WebPagesCache underTest = new WebPagesCache(platform, mapSettings.asConfig(), officialDistribution); | |||
@Before | |||
public void setUp() throws Exception { | |||
public void setUp() { | |||
when(servletContext.getContextPath()).thenReturn(TEST_CONTEXT); | |||
when(servletContext.getResourceAsStream("/index.html")).thenAnswer( | |||
(Answer<InputStream>) invocationOnMock -> toInputStream("Content of default index.html with context [%WEB_CONTEXT%], status [%SERVER_STATUS%], instance [%INSTANCE%]", |
@@ -0,0 +1,26 @@ | |||
<?xml version="1.0" encoding="UTF-8" ?> | |||
<configuration debug="false"> | |||
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/> | |||
<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"/> | |||
<appender-ref ref="CONSOLE"/> | |||
</root> | |||
<logger name="ch.qos.logback"> | |||
<level value="WARN"/> | |||
</logger> | |||
<logger name="okhttp3.mockwebserver"> | |||
<level value="WARN"/> | |||
</logger> | |||
</configuration> |
@@ -17,6 +17,7 @@ include 'server:sonar-server' | |||
include 'server:sonar-server-common' | |||
include 'server:sonar-vsts' | |||
include 'server:sonar-web' | |||
include 'server:sonar-webserver' | |||
include 'server:sonar-webserver-ws' | |||
include 'sonar-application' |
@@ -34,7 +34,7 @@ dependencies { | |||
compile project(':server:sonar-ce') | |||
compile project(':server:sonar-main') | |||
compile project(':server:sonar-process') | |||
compile project(':server:sonar-server') | |||
compile project(':server:sonar-webserver') | |||
compile project(':sonar-core') | |||
compile project(path: ':sonar-plugin-api', configuration: 'shadow') | |||
compile project(':sonar-plugin-api-impl') |