001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2012 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.server.platform;
021    
022    import org.apache.commons.configuration.BaseConfiguration;
023    import org.slf4j.LoggerFactory;
024    import org.sonar.api.Plugins;
025    import org.sonar.api.platform.ComponentContainer;
026    import org.sonar.api.platform.Server;
027    import org.sonar.api.profiles.AnnotationProfileParser;
028    import org.sonar.api.profiles.XMLProfileParser;
029    import org.sonar.api.profiles.XMLProfileSerializer;
030    import org.sonar.api.resources.Languages;
031    import org.sonar.api.resources.ResourceTypes;
032    import org.sonar.api.rules.AnnotationRuleParser;
033    import org.sonar.api.rules.DefaultRulesManager;
034    import org.sonar.api.rules.XMLRuleParser;
035    import org.sonar.api.utils.HttpDownloader;
036    import org.sonar.api.utils.IocContainer;
037    import org.sonar.api.utils.TimeProfiler;
038    import org.sonar.core.PicoUtils;
039    import org.sonar.core.i18n.GwtI18n;
040    import org.sonar.core.i18n.I18nManager;
041    import org.sonar.core.i18n.RuleI18nManager;
042    import org.sonar.core.metric.DefaultMetricFinder;
043    import org.sonar.core.notification.DefaultNotificationManager;
044    import org.sonar.core.persistence.*;
045    import org.sonar.core.qualitymodel.DefaultModelFinder;
046    import org.sonar.core.rule.DefaultRuleFinder;
047    import org.sonar.core.user.DefaultUserFinder;
048    import org.sonar.jpa.dao.DaoFacade;
049    import org.sonar.jpa.dao.MeasuresDao;
050    import org.sonar.jpa.dao.ProfilesDao;
051    import org.sonar.jpa.dao.RulesDao;
052    import org.sonar.jpa.session.DatabaseSessionFactory;
053    import org.sonar.jpa.session.DatabaseSessionProvider;
054    import org.sonar.jpa.session.DefaultDatabaseConnector;
055    import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory;
056    import org.sonar.server.charts.ChartFactory;
057    import org.sonar.server.configuration.Backup;
058    import org.sonar.server.configuration.ProfilesManager;
059    import org.sonar.server.database.EmbeddedDatabaseFactory;
060    import org.sonar.server.filters.FilterExecutor;
061    import org.sonar.server.notifications.NotificationService;
062    import org.sonar.server.notifications.reviews.ReviewsNotificationManager;
063    import org.sonar.server.plugins.*;
064    import org.sonar.server.qualitymodel.DefaultModelManager;
065    import org.sonar.server.rules.ProfilesConsole;
066    import org.sonar.server.rules.RulesConsole;
067    import org.sonar.server.startup.*;
068    import org.sonar.server.ui.CodeColorizers;
069    import org.sonar.server.ui.JRubyI18n;
070    import org.sonar.server.ui.SecurityRealmFactory;
071    import org.sonar.server.ui.Views;
072    
073    import javax.servlet.ServletContext;
074    
075    /**
076     * @since 2.2
077     */
078    public final class Platform {
079    
080      private static final Platform INSTANCE = new Platform();
081    
082      private ComponentContainer rootContainer;// level 1 : only database connectors
083      private ComponentContainer coreContainer;// level 2 : level 1 + core components
084      private ComponentContainer servicesContainer;// level 3 : level 2 + plugin extensions + core components that depend on plugin extensions
085    
086      private boolean connected = false;
087      private boolean started = false;
088    
089      public static Platform getInstance() {
090        return INSTANCE;
091      }
092    
093      private Platform() {
094      }
095    
096      public void init(ServletContext servletContext) {
097        if (!connected) {
098          try {
099            startDatabaseConnectors(servletContext);
100            connected = true;
101    
102          } catch (RuntimeException e) {
103            // full stacktrace is lost by jruby. It must be logged now.
104            Throwable initialException = PicoUtils.sanitize(e);
105            LoggerFactory.getLogger(getClass()).error(initialException.getMessage(), initialException);
106            PicoUtils.propagateStartupException(initialException);
107          }
108        }
109      }
110    
111      public void start() {
112        if (!started && getDatabaseStatus() == DatabaseVersion.Status.UP_TO_DATE) {
113          try {
114            TimeProfiler profiler = new TimeProfiler().start("Start components");
115            startCoreComponents();
116            startServiceComponents();
117            executeStartupTasks();
118            started = true;
119            profiler.stop();
120          } catch (RuntimeException e) {
121            // full stacktrace is lost by jruby. It must be logged now.
122            Throwable initialException = PicoUtils.sanitize(e);
123            LoggerFactory.getLogger(getClass()).error(initialException.getMessage(), initialException);
124            PicoUtils.propagateStartupException(initialException);
125          }
126        }
127      }
128    
129      private void startDatabaseConnectors(ServletContext servletContext) {
130        rootContainer = new ComponentContainer();
131        rootContainer.addSingleton(servletContext);
132        rootContainer.addSingleton(IocContainer.class); // for backward compatibility
133        rootContainer.addSingleton(new BaseConfiguration());
134        rootContainer.addSingleton(ServerSettings.class);
135        rootContainer.addSingleton(ServerImpl.class);
136        rootContainer.addSingleton(EmbeddedDatabaseFactory.class);
137        rootContainer.addSingleton(DefaultDatabase.class);
138        rootContainer.addSingleton(MyBatis.class);
139        rootContainer.addSingleton(DefaultServerUpgradeStatus.class);
140        rootContainer.addSingleton(DatabaseServerCompatibility.class);
141        rootContainer.addSingleton(DatabaseMigrator.class);
142        rootContainer.addSingleton(DatabaseVersion.class);
143        for (Class daoClass : DaoUtils.getDaoClasses()) {
144          rootContainer.addSingleton(daoClass);
145        }
146        rootContainer.addSingleton(PluginDeployer.class);
147        rootContainer.addSingleton(DefaultServerPluginRepository.class);
148        rootContainer.addSingleton(DefaultServerFileSystem.class);
149        rootContainer.addSingleton(ApplicationDeployer.class);
150        rootContainer.startComponents();
151      }
152    
153      private DatabaseVersion.Status getDatabaseStatus() {
154        DatabaseVersion version = getContainer().getComponentByType(DatabaseVersion.class);
155        return version.getStatus();
156      }
157    
158      private void startCoreComponents() {
159        coreContainer = rootContainer.createChild();
160        coreContainer.addSingleton(ServerDatabaseSettingsLoader.class);
161        coreContainer.addSingleton(DefaultDatabaseConnector.class);
162        coreContainer.addSingleton(ServerExtensionInstaller.class);
163        coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class);
164        coreContainer.addPicoAdapter(new DatabaseSessionProvider());
165        coreContainer.startComponents();
166      }
167    
168      /**
169       * plugin extensions + all the components that depend on plugin extensions
170       */
171      private void startServiceComponents() {
172        servicesContainer = coreContainer.createChild();
173        ServerExtensionInstaller extensionRegistrar = servicesContainer.getComponentByType(ServerExtensionInstaller.class);
174        extensionRegistrar.registerExtensions(servicesContainer);
175    
176        servicesContainer.addSingleton(GlobalSettingsUpdater.class);
177        servicesContainer.addSingleton(HttpDownloader.class);
178        servicesContainer.addSingleton(UpdateCenterClient.class);
179        servicesContainer.addSingleton(UpdateCenterMatrixFactory.class);
180        servicesContainer.addSingleton(PluginDownloader.class);
181        servicesContainer.addSingleton(ServerIdGenerator.class);
182        servicesContainer.addComponent(FilterExecutor.class, false);
183        servicesContainer.addSingleton(DefaultModelFinder.class); // depends on plugins
184        servicesContainer.addSingleton(DefaultModelManager.class);
185        servicesContainer.addSingleton(Plugins.class);
186        servicesContainer.addSingleton(ChartFactory.class);
187        servicesContainer.addSingleton(Languages.class);
188        servicesContainer.addSingleton(Views.class);
189        servicesContainer.addSingleton(CodeColorizers.class);
190        servicesContainer.addComponent(RulesDao.class, false);
191        servicesContainer.addComponent(MeasuresDao.class, false);
192        servicesContainer.addComponent(org.sonar.api.database.daos.MeasuresDao.class, false);
193        servicesContainer.addComponent(ProfilesDao.class, false);
194        servicesContainer.addComponent(DaoFacade.class, false);
195        servicesContainer.addComponent(DefaultRulesManager.class, false);
196        servicesContainer.addComponent(ProfilesManager.class, false);
197        servicesContainer.addComponent(Backup.class, false);
198        servicesContainer.addSingleton(SecurityRealmFactory.class);
199        servicesContainer.addSingleton(ServerLifecycleNotifier.class);
200        servicesContainer.addSingleton(AnnotationProfileParser.class);
201        servicesContainer.addSingleton(XMLProfileParser.class);
202        servicesContainer.addSingleton(XMLProfileSerializer.class);
203        servicesContainer.addSingleton(AnnotationRuleParser.class);
204        servicesContainer.addSingleton(XMLRuleParser.class);
205        servicesContainer.addSingleton(DefaultRuleFinder.class);
206        servicesContainer.addSingleton(DefaultMetricFinder.class);
207        servicesContainer.addSingleton(ProfilesConsole.class);
208        servicesContainer.addSingleton(RulesConsole.class);
209        servicesContainer.addSingleton(JRubyI18n.class);
210        servicesContainer.addSingleton(DefaultUserFinder.class);
211        servicesContainer.addSingleton(I18nManager.class);
212        servicesContainer.addSingleton(RuleI18nManager.class);
213        servicesContainer.addSingleton(GwtI18n.class);
214        servicesContainer.addSingleton(ResourceTypes.class);
215    
216        // Notifications
217        servicesContainer.addSingleton(NotificationService.class);
218        servicesContainer.addSingleton(DefaultNotificationManager.class);
219        servicesContainer.addSingleton(ReviewsNotificationManager.class);
220    
221        servicesContainer.startComponents();
222      }
223    
224      private void executeStartupTasks() {
225        ComponentContainer startupContainer = servicesContainer.createChild();
226        startupContainer.addSingleton(GwtPublisher.class);
227        startupContainer.addSingleton(RegisterMetrics.class);
228        startupContainer.addSingleton(RegisterRules.class);
229        startupContainer.addSingleton(RegisterProvidedProfiles.class);
230        startupContainer.addSingleton(EnableProfiles.class);
231        startupContainer.addSingleton(ActivateDefaultProfiles.class);
232        startupContainer.addSingleton(JdbcDriverDeployer.class);
233        startupContainer.addSingleton(ServerMetadataPersister.class);
234        startupContainer.addSingleton(RegisterQualityModels.class);
235        startupContainer.addSingleton(DeleteDeprecatedMeasures.class);
236        startupContainer.addSingleton(GeneratePluginIndex.class);
237        startupContainer.addSingleton(RegisterNewDashboards.class);
238        startupContainer.startComponents();
239    
240        startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart();
241    
242        // Do not put the following statements in a finally block.
243        // It would hide the possible exception raised during startup
244        // See SONAR-3107
245        startupContainer.stopComponents();
246        servicesContainer.removeChild();
247        servicesContainer.getComponentByType(DatabaseSessionFactory.class).clear();
248      }
249    
250      public void stop() {
251        if (rootContainer != null) {
252          try {
253            TimeProfiler profiler = new TimeProfiler().start("Stop sonar");
254            rootContainer.stopComponents();
255            rootContainer = null;
256            connected = false;
257            started = false;
258            profiler.stop();
259          } catch (Exception e) {
260            LoggerFactory.getLogger(getClass()).debug("Fail to stop Sonar - ignored", e);
261          }
262        }
263      }
264    
265      public ComponentContainer getContainer() {
266        if (servicesContainer != null) {
267          return servicesContainer;
268        }
269        if (coreContainer != null) {
270          return coreContainer;
271        }
272        return rootContainer;
273      }
274    
275      public Object getComponent(Object key) {
276        return getContainer().getComponentByKey(key);
277      }
278    
279      /**
280       * shortcut for ruby code
281       */
282      public static Server getServer() {
283        return (Server) getInstance().getComponent(Server.class);
284      }
285    }