From d7c81d0b83d4e6d852bedace63e8a9db1e40ab46 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Sun, 16 Mar 2014 23:31:18 +0100 Subject: [PATCH] SONAR-4843 restart most of components --- .../api/platform/ComponentContainer.java | 17 +- .../org/sonar/server/platform/Platform.java | 183 +++++------------- ...entsStarter.java => ServerComponents.java} | 133 ++++++++++++- .../server/platform/ws/RestartHandler.java | 2 +- .../java/org/sonar/server/ui/JRubyFacade.java | 6 +- .../platform/ws/RestartHandlerTest.java | 2 +- 6 files changed, 195 insertions(+), 148 deletions(-) rename sonar-server/src/main/java/org/sonar/server/platform/{ServerComponentsStarter.java => ServerComponents.java} (81%) diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java index ec001e31d78..ac26bc00521 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java @@ -32,7 +32,7 @@ import org.sonar.api.ServerComponent; import org.sonar.api.config.PropertyDefinitions; import javax.annotation.Nullable; - +import java.util.Collection; import java.util.List; /** @@ -152,6 +152,13 @@ public class ComponentContainer implements BatchComponent, ServerComponent { return this; } + public ComponentContainer addSingletons(Collection components) { + for (Object component : components) { + addSingleton(component); + } + return this; + } + public ComponentContainer addSingleton(Object component) { return addComponent(component, true); } @@ -162,8 +169,12 @@ public class ComponentContainer implements BatchComponent, ServerComponent { */ public ComponentContainer addComponent(Object component, boolean singleton) { Object key = componentKeys.of(component); - pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component); - declareExtension(null, component); + if (component instanceof ComponentAdapter) { + pico.addAdapter((ComponentAdapter) component); + } else { + pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component); + declareExtension(null, component); + } return this; } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index ea7ad0bc995..353893e7d0b 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -19,46 +19,10 @@ */ package org.sonar.server.platform; -import org.apache.commons.configuration.BaseConfiguration; import org.slf4j.LoggerFactory; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.platform.Server; -import org.sonar.api.utils.Durations; -import org.sonar.api.utils.HttpDownloader; -import org.sonar.api.utils.TimeProfiler; -import org.sonar.api.utils.UriReader; -import org.sonar.api.utils.internal.TempFolderCleaner; -import org.sonar.core.config.Logback; -import org.sonar.core.i18n.DefaultI18n; -import org.sonar.core.i18n.GwtI18n; -import org.sonar.core.i18n.RuleI18nManager; -import org.sonar.core.persistence.DaoUtils; import org.sonar.core.persistence.DatabaseVersion; -import org.sonar.core.persistence.DefaultDatabase; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.persistence.PreviewDatabaseFactory; -import org.sonar.core.persistence.SemaphoreUpdater; -import org.sonar.core.persistence.SemaphoresImpl; -import org.sonar.core.profiling.Profiling; -import org.sonar.core.purge.PurgeProfiler; -import org.sonar.jpa.session.DatabaseSessionProvider; -import org.sonar.jpa.session.DefaultDatabaseConnector; -import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; -import org.sonar.server.db.EmbeddedDatabaseFactory; -import org.sonar.server.db.migrations.DatabaseMigration; -import org.sonar.server.db.migrations.DatabaseMigrations; -import org.sonar.server.db.migrations.DatabaseMigrator; -import org.sonar.server.es.ESNode; -import org.sonar.server.platform.ws.PlatformWs; -import org.sonar.server.platform.ws.RestartHandler; -import org.sonar.server.plugins.ServerPluginJarInstaller; -import org.sonar.server.plugins.ServerPluginJarsInstaller; -import org.sonar.server.plugins.ServerPluginRepository; -import org.sonar.server.plugins.InstalledPluginReferentialFactory; -import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.startup.ServerMetadataPersister; -import org.sonar.server.ui.JRubyI18n; -import org.sonar.server.ui.JRubyProfiling; import javax.annotation.CheckForNull; import javax.servlet.ServletContext; @@ -70,12 +34,10 @@ public class Platform { private static final Platform INSTANCE = new Platform(); - // level 1 : database stuff, settings - // level 2 : level 1 + components that are never restarted - // level 3 : level 2 + all other components, including plugin extensions - private ComponentContainer level1Container, level2Container, level3Container; - - private boolean connected = false; + private ServerComponents serverComponents; + private ComponentContainer level1Container, level2Container, level3Container, level4Container; + private ComponentContainer currentContainer; + private boolean dbConnected = false; private boolean started = false; private Platform() { @@ -104,98 +66,70 @@ public class Platform { } public void init(ServletContext servletContext) { - if (!connected) { - startLevel1Container(servletContext); - connected = true; + serverComponents = new ServerComponents(this, servletContext); + if (!dbConnected) { + startLevel1Container(); + startLevel2Container(); + dbConnected = true; } } - // Platform is injected in Pico, so do not rename "start" + // Platform is injected in Pico, so do not rename this method "start" public void doStart() { if (!started && getDatabaseStatus() == DatabaseVersion.Status.UP_TO_DATE) { - TimeProfiler profiler = new TimeProfiler().start("Start components"); - startLevel2Container(); + startLevel34Containers(); started = true; - profiler.stop(); } } - private void startLevel1Container(ServletContext servletContext) { + /** + * Start level 1 only + */ + private void startLevel1Container() { level1Container = new ComponentContainer(); - level1Container.addSingleton(this); - level1Container.addSingleton(servletContext); - level1Container.addSingleton(new BaseConfiguration()); - level1Container.addSingleton(ServerSettings.class); - level1Container.addSingleton(ServerImpl.class); - level1Container.addSingleton(Logback.class); - level1Container.addSingleton(Profiling.class); - level1Container.addSingleton(JRubyProfiling.class); - level1Container.addSingleton(EmbeddedDatabaseFactory.class); - level1Container.addSingleton(DefaultDatabase.class); - level1Container.addSingleton(MyBatis.class); - level1Container.addSingleton(DefaultServerUpgradeStatus.class); - level1Container.addSingleton(DatabaseServerCompatibility.class); - for (Class migrationClass : DatabaseMigrations.CLASSES) { - level1Container.addSingleton(migrationClass); - } - level1Container.addSingleton(DatabaseMigrator.class); - level1Container.addSingleton(DatabaseVersion.class); - for (Class daoClass : DaoUtils.getDaoClasses()) { - level1Container.addSingleton(daoClass); - } - level1Container.addSingleton(PurgeProfiler.class); - level1Container.addSingleton(DefaultServerFileSystem.class); - level1Container.addSingleton(PreviewDatabaseFactory.class); - level1Container.addSingleton(SemaphoreUpdater.class); - level1Container.addSingleton(SemaphoresImpl.class); - level1Container.addPicoAdapter(new TempFolderProvider()); - level1Container.addSingleton(TempFolderCleaner.class); - - - // plugins - level1Container.addSingleton(ServerPluginJarsInstaller.class); - level1Container.addSingleton(ServerPluginJarInstaller.class); - level1Container.addSingleton(InstalledPluginReferentialFactory.class); - level1Container.addSingleton(ServerPluginRepository.class); - level1Container.addSingleton(ServerExtensionInstaller.class); - - // depends on plugins - level1Container.addSingleton(RailsAppsDeployer.class); - level1Container.addSingleton(JRubyI18n.class); - level1Container.addSingleton(DefaultI18n.class); - level1Container.addSingleton(RuleI18nManager.class); - level1Container.addSingleton(GwtI18n.class); - level1Container.addSingleton(Durations.class); + level1Container.addSingletons(serverComponents.level1Components()); level1Container.startComponents(); - } - - private DatabaseVersion.Status getDatabaseStatus() { - DatabaseVersion version = getContainer().getComponentByType(DatabaseVersion.class); - return version.getStatus(); + currentContainer = level1Container; } /** - * Components listed here are never restarted + * Start level 2 only */ private void startLevel2Container() { level2Container = level1Container.createChild(); - level2Container.addSingleton(PersistentSettings.class); - level2Container.addSingleton(DefaultDatabaseConnector.class); - level2Container.addSingleton(ThreadLocalDatabaseSessionFactory.class); - level2Container.addPicoAdapter(new DatabaseSessionProvider()); - level2Container.addSingleton(ServerMetadataPersister.class); - level2Container.addSingleton(ESNode.class); - level2Container.addSingleton(HttpDownloader.class); - level2Container.addSingleton(UriReader.class); - level2Container.addSingleton(ServerIdGenerator.class); - - // ws - level2Container.addSingleton(RestartHandler.class); - level2Container.addSingleton(PlatformWs.class); - + level2Container.addSingletons(serverComponents.level2Components()); level2Container.startComponents(); + currentContainer = level2Container; + } + + /** + * Start level 3 and greater + */ + private void startLevel34Containers() { + level3Container = level2Container.createChild(); + level3Container.addSingletons(serverComponents.level3Components()); + level3Container.startComponents(); + currentContainer = level3Container; - restartLevel3Container(); + level4Container = level3Container.createChild(); + serverComponents.startLevel4Components(level4Container); + currentContainer = level4Container; + + } + + public void restart() { + // Do not need to initialize database connection, so level 1 is skipped + if (level2Container != null) { + level2Container.stopComponents(); + currentContainer = level1Container; + } + startLevel2Container(); + startLevel34Containers(); + } + + private DatabaseVersion.Status getDatabaseStatus() { + DatabaseVersion version = getContainer().getComponentByType(DatabaseVersion.class); + return version.getStatus(); } // Do not rename "stop" @@ -204,7 +138,8 @@ public class Platform { try { level1Container.stopComponents(); level1Container = null; - connected = false; + currentContainer = null; + dbConnected = false; started = false; } catch (Exception e) { LoggerFactory.getLogger(getClass()).debug("Fail to stop server - ignored", e); @@ -212,22 +147,8 @@ public class Platform { } } - public void restartLevel3Container() { - if (level3Container != null) { - level3Container.stopComponents(); - } - level3Container = level2Container.createChild(); - new ServerComponentsStarter().start(level3Container); - } - public ComponentContainer getContainer() { - if (level3Container != null) { - return level3Container; - } - if (level2Container != null) { - return level2Container; - } - return level1Container; + return currentContainer; } public Object getComponent(Object key) { diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponentsStarter.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java similarity index 81% rename from sonar-server/src/main/java/org/sonar/server/platform/ServerComponentsStarter.java rename to sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index c360d947c6c..55754132be7 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponentsStarter.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -19,6 +19,8 @@ */ package org.sonar.server.platform; +import com.google.common.collect.Lists; +import org.apache.commons.configuration.BaseConfiguration; import org.sonar.api.config.EmailSettings; import org.sonar.api.issue.action.Actions; import org.sonar.api.platform.ComponentContainer; @@ -29,8 +31,16 @@ import org.sonar.api.resources.Languages; import org.sonar.api.resources.ResourceTypes; import org.sonar.api.rules.AnnotationRuleParser; import org.sonar.api.rules.XMLRuleParser; +import org.sonar.api.utils.Durations; +import org.sonar.api.utils.HttpDownloader; +import org.sonar.api.utils.UriReader; +import org.sonar.api.utils.internal.TempFolderCleaner; import org.sonar.core.component.SnapshotPerspectives; import org.sonar.core.component.db.ComponentDao; +import org.sonar.core.config.Logback; +import org.sonar.core.i18n.DefaultI18n; +import org.sonar.core.i18n.GwtI18n; +import org.sonar.core.i18n.RuleI18nManager; import org.sonar.core.issue.IssueFilterSerializer; import org.sonar.core.issue.IssueNotifications; import org.sonar.core.issue.IssueUpdater; @@ -42,7 +52,16 @@ import org.sonar.core.measure.MeasureFilterFactory; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; +import org.sonar.core.persistence.DaoUtils; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.DefaultDatabase; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.persistence.PreviewDatabaseFactory; +import org.sonar.core.persistence.SemaphoreUpdater; +import org.sonar.core.persistence.SemaphoresImpl; import org.sonar.core.preview.PreviewCache; +import org.sonar.core.profiling.Profiling; +import org.sonar.core.purge.PurgeProfiler; import org.sonar.core.qualitygate.db.ProjectQgateAssociationDao; import org.sonar.core.qualitygate.db.QualityGateConditionDao; import org.sonar.core.qualitygate.db.QualityGateDao; @@ -61,14 +80,21 @@ import org.sonar.jpa.dao.MeasuresDao; import org.sonar.jpa.dao.ProfilesDao; import org.sonar.jpa.dao.RulesDao; import org.sonar.jpa.session.DatabaseSessionFactory; +import org.sonar.jpa.session.DatabaseSessionProvider; +import org.sonar.jpa.session.DefaultDatabaseConnector; +import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; import org.sonar.server.charts.ChartFactory; import org.sonar.server.component.DefaultComponentFinder; import org.sonar.server.component.DefaultRubyComponentService; +import org.sonar.server.db.EmbeddedDatabaseFactory; +import org.sonar.server.db.migrations.DatabaseMigrations; +import org.sonar.server.db.migrations.DatabaseMigrator; import org.sonar.server.debt.DebtCharacteristicsXMLImporter; import org.sonar.server.debt.DebtModelSynchronizer; import org.sonar.server.debt.DebtRulesXMLImporter; import org.sonar.server.debt.DebtService; import org.sonar.server.es.ESIndex; +import org.sonar.server.es.ESNode; import org.sonar.server.issue.ActionPlanService; import org.sonar.server.issue.ActionService; import org.sonar.server.issue.AssignAction; @@ -95,8 +121,14 @@ import org.sonar.server.notifications.NotificationService; import org.sonar.server.permission.InternalPermissionService; import org.sonar.server.permission.InternalPermissionTemplateService; import org.sonar.server.permission.PermissionFinder; +import org.sonar.server.platform.ws.PlatformWs; +import org.sonar.server.platform.ws.RestartHandler; +import org.sonar.server.plugins.InstalledPluginReferentialFactory; import org.sonar.server.plugins.PluginDownloader; import org.sonar.server.plugins.ServerExtensionInstaller; +import org.sonar.server.plugins.ServerPluginJarInstaller; +import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.plugins.UpdateCenterClient; import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.qualitygate.QgateProjectFinder; @@ -153,8 +185,11 @@ import org.sonar.server.startup.RegisterNewProfiles; import org.sonar.server.startup.RegisterPermissionTemplates; import org.sonar.server.startup.RegisterServletFilters; import org.sonar.server.startup.RenameDeprecatedPropertyKeys; +import org.sonar.server.startup.ServerMetadataPersister; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; +import org.sonar.server.ui.JRubyI18n; +import org.sonar.server.ui.JRubyProfiling; import org.sonar.server.ui.PageDecorations; import org.sonar.server.ui.Views; import org.sonar.server.user.DefaultUserService; @@ -172,15 +207,97 @@ import org.sonar.server.util.TypeValidations; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; -public class ServerComponentsStarter { +import java.util.Arrays; +import java.util.Collection; +import java.util.List; - public void start(ComponentContainer pico) { - registerComponents(pico); - pico.startComponents(); - executeStartupTaks(pico); +class ServerComponents { + + private final Object[] rootComponents; + + ServerComponents(Object... rootComponents) { + this.rootComponents = rootComponents; + } + + /** + * All the stuff required to connect to database + */ + Collection level1Components() { + List components = Lists.newArrayList(rootComponents); + components.addAll(Arrays.asList( + new BaseConfiguration(), + ServerSettings.class, + ServerImpl.class, + Logback.class, + Profiling.class, + JRubyProfiling.class, + EmbeddedDatabaseFactory.class, + DefaultDatabase.class, + MyBatis.class, + DefaultServerUpgradeStatus.class, + DatabaseServerCompatibility.class, + DatabaseMigrator.class, + DatabaseVersion.class, + PurgeProfiler.class, + DefaultServerFileSystem.class, + PreviewDatabaseFactory.class, + SemaphoreUpdater.class, + SemaphoresImpl.class, + TempFolderCleaner.class, + new TempFolderProvider() + )); + components.addAll(DatabaseMigrations.CLASSES); + components.addAll(DaoUtils.getDaoClasses()); + return components; + } + + /** + * The stuff required to display the db upgrade form in webapp. + * Needs to be connected to db. + */ + Collection level2Components() { + return Lists.newArrayList( + // plugins + ServerPluginJarsInstaller.class, + ServerPluginJarInstaller.class, + InstalledPluginReferentialFactory.class, + ServerPluginRepository.class, + ServerExtensionInstaller.class, + + // depends on plugins + RailsAppsDeployer.class, + JRubyI18n.class, + DefaultI18n.class, + RuleI18nManager.class, + GwtI18n.class, + Durations.class, + + // ws + RestartHandler.class, + PlatformWs.class + ); + } + + /** + * The core components that complete the initialization of database + * when its schema is up-to-date. + */ + Collection level3Components() { + return Lists.newArrayList( + PersistentSettings.class, + DefaultDatabaseConnector.class, + ThreadLocalDatabaseSessionFactory.class, + new DatabaseSessionProvider(), + ServerMetadataPersister.class, + ESNode.class, + HttpDownloader.class, + UriReader.class, + ServerIdGenerator.class + ); } - private void registerComponents(ComponentContainer pico) { + + void startLevel4Components(ComponentContainer pico) { pico.addSingleton(ESIndex.class); pico.addSingleton(UpdateCenterClient.class); pico.addSingleton(UpdateCenterMatrixFactory.class); @@ -353,6 +470,9 @@ public class ServerComponentsStarter { ServerExtensionInstaller extensionRegistrar = pico.getComponentByType(ServerExtensionInstaller.class); extensionRegistrar.installExtensions(pico); + + pico.startComponents(); + executeStartupTaks(pico); } private void executeStartupTaks(ComponentContainer pico) { @@ -383,7 +503,6 @@ public class ServerComponentsStarter { // It would hide the possible exception raised during startup // See SONAR-3107 startupContainer.stopComponents(); - pico.removeChild(); pico.getComponentByType(DatabaseSessionFactory.class).clear(); } } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartHandler.java b/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartHandler.java index 94312f13377..75b080a21f3 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartHandler.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartHandler.java @@ -51,7 +51,7 @@ public class RestartHandler implements RequestHandler { if (settings.getBoolean("sonar.dev")) { Logger logger = LoggerFactory.getLogger(getClass()); logger.info("Restart server"); - platform.restartLevel3Container(); + platform.restart(); logger.info("Server restarted"); response.noContent(); diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index bd8bf72af01..98d8741014d 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -70,7 +70,6 @@ import static com.google.common.collect.Lists.newArrayList; public final class JRubyFacade { private static final JRubyFacade SINGLETON = new JRubyFacade(); - private JRubyI18n i18n; public static JRubyFacade getInstance() { return SINGLETON; @@ -311,10 +310,7 @@ public final class JRubyFacade { } private JRubyI18n getJRubyI18n() { - if (i18n == null) { - i18n = get(JRubyI18n.class); - } - return i18n; + return get(JRubyI18n.class); } public String getMessage(String rubyLocale, String key, String defaultValue, Object... parameters) { diff --git a/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartHandlerTest.java b/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartHandlerTest.java index 8038d87644e..03b2b3d6428 100644 --- a/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartHandlerTest.java +++ b/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartHandlerTest.java @@ -44,7 +44,7 @@ public class RestartHandlerTest { WsTester tester = new WsTester(ws); tester.newRequest("restart").execute(); - verify(platform).restartLevel3Container(); + verify(platform).restart(); } @Test -- 2.39.5