*/
package org.sonar.server.platform;
+import java.util.Collection;
+import java.util.Properties;
+import javax.annotation.CheckForNull;
+import javax.servlet.ServletContext;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.platform.Server;
+import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.persistence.DatabaseVersion;
-import javax.annotation.CheckForNull;
-import javax.servlet.ServletContext;
-import java.util.Collection;
-import java.util.Properties;
-
/**
* @since 2.2
*/
public class Platform {
+ private static final Logger LOGGER = Loggers.get(Platform.class);
+
private static final Platform INSTANCE = new Platform();
private ServerComponents serverComponents;
- private ComponentContainer level1Container, level2Container, level3Container, level4Container;
+ private ComponentContainer level1Container, level2Container, safeModeContainer, level3Container, level4Container;
private ComponentContainer currentContainer;
private boolean dbConnected = false;
private boolean started = false;
if (!dbConnected) {
startLevel1Container();
startLevel2Container();
+ currentContainer = level2Container;
dbConnected = true;
}
}
// Platform is injected in Pico, so do not rename this method "start"
public void doStart() {
- if (!started && getDatabaseStatus() == DatabaseVersion.Status.UP_TO_DATE) {
+ if (started && !isInSafeMode()) {
+ return;
+ }
+
+ if (requireSafeMode()) {
+ LOGGER.info("DB needs migration, entering safe mode");
+ startSafeModeContainer();
+ currentContainer = safeModeContainer;
+ started = true;
+ } else {
startLevel34Containers();
+ executeStartupTasks();
+ // switch current container last to avoid giving access to a partially initialized container
+ currentContainer = level4Container;
started = true;
+
+ // stop safemode container if it existed
+ stopSafeModeContainer();
}
}
+ public void restart() {
+ // switch currentContainer on level1 now to avoid exposing a container in the process of stopping
+ currentContainer = level1Container;
+
+ // stop containers
+ stopSafeModeContainer();
+ stopLevel234Containers();
+
+ // no need to initialize database connection, so level 1 is skipped
+ startLevel2Container();
+ startLevel34Containers();
+ executeStartupTasks();
+ currentContainer = level4Container;
+ }
+
+ private boolean requireSafeMode() {
+ return getDatabaseStatus() != DatabaseVersion.Status.UP_TO_DATE;
+ }
+
public boolean isStarted() {
- return started;
+ return started && !isInSafeMode();
+ }
+
+ public boolean isInSafeMode() {
+ return started && safeModeContainer != null && currentContainer == safeModeContainer;
}
/**
- * Start level 1 only
+ * Starts level 1
*/
private void startLevel1Container() {
level1Container = new ComponentContainer();
level1Container.addSingletons(serverComponents.level1Components());
level1Container.startComponents();
- currentContainer = level1Container;
}
/**
- * Start level 2 only
+ * Starts level 2
*/
private void startLevel2Container() {
level2Container = level1Container.createChild();
level2Container.addSingletons(serverComponents.level2Components());
level2Container.startComponents();
- currentContainer = level2Container;
}
/**
- * Start level 3 and greater
+ * Starts level 3 and 4
*/
private void startLevel34Containers() {
level3Container = level2Container.createChild();
level3Container.addSingletons(serverComponents.level3Components());
level3Container.startComponents();
- currentContainer = level3Container;
level4Container = level3Container.createChild();
serverComponents.startLevel4Components(level4Container);
- currentContainer = level4Container;
- executeStartupTasks();
}
public void executeStartupTasks() {
serverComponents.executeStartupTasks(level4Container);
}
- public void restart() {
- // Do not need to initialize database connection, so level 1 is skipped
+ private void startSafeModeContainer() {
+ safeModeContainer = level2Container.createChild();
+ safeModeContainer.addSingletons(serverComponents.safeModeComponents());
+ safeModeContainer.startComponents();
+ }
+
+ /**
+ * Stops level 1
+ */
+ private void stopLevel1Container() {
+ if (level1Container != null) {
+ level1Container.stopComponents();
+ level1Container = null;
+ }
+ }
+
+ /**
+ * Stops level 2, 3 and 3 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 (level2Container != null) {
level2Container.stopComponents();
- currentContainer = level1Container;
+ level2Container = null;
+ level3Container = null;
+ level4Container = 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 (safeModeContainer != null) {
+ safeModeContainer.stopComponents();
+ safeModeContainer = null;
}
- startLevel2Container();
- startLevel34Containers();
}
private DatabaseVersion.Status getDatabaseStatus() {
// Do not rename "stop"
public void doStop() {
- if (level1Container != null) {
- try {
- level1Container.stopComponents();
- level1Container = null;
- currentContainer = null;
- dbConnected = false;
- started = false;
- } catch (Exception e) {
- Loggers.get(getClass()).debug("Fail to stop server - ignored", e);
- }
+ try {
+ stopSafeModeContainer();
+ stopLevel234Containers();
+ stopLevel1Container();
+ currentContainer = null;
+ dbConnected = false;
+ started = false;
+ } catch (Exception e) {
+ LOGGER.debug("Fail to stop server - ignored", e);
}
}