001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 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.rules.AnnotationRuleParser;
032    import org.sonar.api.rules.DefaultRulesManager;
033    import org.sonar.api.rules.XMLRuleParser;
034    import org.sonar.api.utils.HttpDownloader;
035    import org.sonar.api.utils.IocContainer;
036    import org.sonar.api.utils.TimeProfiler;
037    import org.sonar.core.i18n.GwtI18n;
038    import org.sonar.core.i18n.I18nManager;
039    import org.sonar.core.i18n.RuleI18nManager;
040    import org.sonar.core.metric.DefaultMetricFinder;
041    import org.sonar.core.notification.DefaultNotificationManager;
042    import org.sonar.core.persistence.DaoUtils;
043    import org.sonar.core.persistence.DatabaseMigrator;
044    import org.sonar.core.persistence.DefaultDatabase;
045    import org.sonar.core.persistence.MyBatis;
046    import org.sonar.core.qualitymodel.DefaultModelFinder;
047    import org.sonar.core.rule.DefaultRuleFinder;
048    import org.sonar.core.user.DefaultUserFinder;
049    import org.sonar.jpa.dao.DaoFacade;
050    import org.sonar.jpa.dao.MeasuresDao;
051    import org.sonar.jpa.dao.ProfilesDao;
052    import org.sonar.jpa.dao.RulesDao;
053    import org.sonar.jpa.session.DatabaseSessionFactory;
054    import org.sonar.jpa.session.DatabaseSessionProvider;
055    import org.sonar.jpa.session.DefaultDatabaseConnector;
056    import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory;
057    import org.sonar.server.charts.ChartFactory;
058    import org.sonar.server.configuration.Backup;
059    import org.sonar.server.configuration.ProfilesManager;
060    import org.sonar.server.database.EmbeddedDatabaseFactory;
061    import org.sonar.server.filters.FilterExecutor;
062    import org.sonar.server.mavendeployer.MavenRepository;
063    import org.sonar.server.notifications.NotificationService;
064    import org.sonar.server.notifications.reviews.ReviewsNotificationManager;
065    import org.sonar.server.plugins.*;
066    import org.sonar.server.qualitymodel.DefaultModelManager;
067    import org.sonar.server.rules.ProfilesConsole;
068    import org.sonar.server.rules.RulesConsole;
069    import org.sonar.server.startup.*;
070    import org.sonar.server.ui.AuthenticatorFactory;
071    import org.sonar.server.ui.CodeColorizers;
072    import org.sonar.server.ui.JRubyI18n;
073    import org.sonar.server.ui.Views;
074    
075    import javax.servlet.ServletContext;
076    
077    /**
078     * @since 2.2
079     */
080    public final class Platform {
081    
082      private static final Platform INSTANCE = new Platform();
083    
084      private ComponentContainer rootContainer;// level 1 : only database connectors
085      private ComponentContainer coreContainer;// level 2 : level 1 + core components
086      private ComponentContainer servicesContainer;// level 3 : level 2 + plugin extensions + core components that depend on plugin extensions
087    
088      private boolean connected = false;
089      private boolean started = false;
090    
091      public static Platform getInstance() {
092        return INSTANCE;
093      }
094    
095      private Platform() {
096      }
097    
098      public void init(ServletContext servletContext) {
099        if (!connected) {
100          try {
101            startDatabaseConnectors(servletContext);
102            connected = true;
103    
104          } catch (Exception e) {
105            LoggerFactory.getLogger(getClass()).error("Can not start Sonar", e);
106          }
107        }
108      }
109    
110      public void start() {
111        if (!started && isUpToDateDatabase()) {
112          try {
113            TimeProfiler profiler = new TimeProfiler().start("Start services");
114            startCoreComponents();
115            startServiceComponents();
116            executeStartupTasks();
117            started = true;
118            profiler.stop();
119          } catch (Exception e) {
120            // full stacktrace is lost by jruby. It must be logged now.
121            LoggerFactory.getLogger(getClass()).error("Fail to start server", e);
122            throw new IllegalStateException("Fail to start server", e);
123          }
124        }
125      }
126    
127      private void startDatabaseConnectors(ServletContext servletContext) {
128        rootContainer = new ComponentContainer();
129        rootContainer.addSingleton(servletContext);
130        rootContainer.addSingleton(IocContainer.class); // for backward compatibility
131        rootContainer.addSingleton(new BaseConfiguration());
132        rootContainer.addSingleton(ServerSettings.class);
133        rootContainer.addSingleton(EmbeddedDatabaseFactory.class);
134        rootContainer.addSingleton(DefaultDatabase.class);
135        rootContainer.addSingleton(MyBatis.class);
136        rootContainer.addSingleton(DefaultDatabaseConnector.class);
137        rootContainer.addSingleton(DefaultServerUpgradeStatus.class);
138        rootContainer.addSingleton(DatabaseMigrator.class);
139        for (Class daoClass : DaoUtils.getDaoClasses()) {
140          rootContainer.addSingleton(daoClass);
141        }
142        rootContainer.startComponents();
143      }
144    
145      private boolean isUpToDateDatabase() {
146        DefaultDatabaseConnector databaseConnector = getContainer().getComponentByType(DefaultDatabaseConnector.class);
147        return databaseConnector.isOperational();
148      }
149    
150      private void startCoreComponents() {
151        coreContainer = rootContainer.createChild();
152        coreContainer.addSingleton(PluginDeployer.class);
153        coreContainer.addSingleton(DefaultServerPluginRepository.class);
154        coreContainer.addSingleton(ServerExtensionInstaller.class);
155        coreContainer.addSingleton(DefaultServerFileSystem.class);
156        coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class);
157        coreContainer.addPicoAdapter(new DatabaseSessionProvider());
158        coreContainer.startComponents();
159    
160        DatabaseSessionFactory sessionFactory = coreContainer.getComponentByType(DatabaseSessionFactory.class);
161        ServerSettings serverSettings = coreContainer.getComponentByType(ServerSettings.class);
162        serverSettings.setSessionFactory(sessionFactory);
163        serverSettings.load();
164      }
165    
166      /**
167       * plugin extensions + all the components that depend on plugin extensions
168       */
169      private void startServiceComponents() {
170        servicesContainer = coreContainer.createChild();
171        ServerExtensionInstaller extensionRegistrar = servicesContainer.getComponentByType(ServerExtensionInstaller.class);
172        extensionRegistrar.registerExtensions(servicesContainer);
173    
174        servicesContainer.addSingleton(HttpDownloader.class);
175        servicesContainer.addSingleton(UpdateCenterClient.class);
176        servicesContainer.addSingleton(UpdateCenterMatrixFactory.class);
177        servicesContainer.addSingleton(PluginDownloader.class);
178        servicesContainer.addSingleton(ServerIdGenerator.class);
179        servicesContainer.addSingleton(ServerImpl.class);
180        servicesContainer.addComponent(FilterExecutor.class, false);
181        servicesContainer.addSingleton(DefaultModelFinder.class); // depends on plugins
182        servicesContainer.addSingleton(DefaultModelManager.class);
183        servicesContainer.addSingleton(Plugins.class);
184        servicesContainer.addSingleton(ChartFactory.class);
185        servicesContainer.addSingleton(Languages.class);
186        servicesContainer.addSingleton(Views.class);
187        servicesContainer.addSingleton(CodeColorizers.class);
188        servicesContainer.addComponent(RulesDao.class, false);
189        servicesContainer.addComponent(MeasuresDao.class, false);
190        servicesContainer.addComponent(org.sonar.api.database.daos.MeasuresDao.class, false);
191        servicesContainer.addComponent(ProfilesDao.class, false);
192        servicesContainer.addComponent(DaoFacade.class, false);
193        servicesContainer.addComponent(DefaultRulesManager.class, false);
194        servicesContainer.addComponent(ProfilesManager.class, false);
195        servicesContainer.addComponent(Backup.class, false);
196        servicesContainer.addSingleton(AuthenticatorFactory.class);
197        servicesContainer.addSingleton(ServerLifecycleNotifier.class);
198        servicesContainer.addSingleton(AnnotationProfileParser.class);
199        servicesContainer.addSingleton(XMLProfileParser.class);
200        servicesContainer.addSingleton(XMLProfileSerializer.class);
201        servicesContainer.addSingleton(AnnotationRuleParser.class);
202        servicesContainer.addSingleton(XMLRuleParser.class);
203        servicesContainer.addSingleton(DefaultRuleFinder.class);
204        servicesContainer.addSingleton(DefaultMetricFinder.class);
205        servicesContainer.addSingleton(ProfilesConsole.class);
206        servicesContainer.addSingleton(RulesConsole.class);
207        servicesContainer.addSingleton(JRubyI18n.class);
208        servicesContainer.addSingleton(DefaultUserFinder.class);
209        servicesContainer.addSingleton(I18nManager.class);
210        servicesContainer.addSingleton(RuleI18nManager.class);
211        servicesContainer.addSingleton(GwtI18n.class);
212    
213        // Notifications
214        servicesContainer.addSingleton(NotificationService.class);
215        servicesContainer.addSingleton(DefaultNotificationManager.class);
216        servicesContainer.addSingleton(ReviewsNotificationManager.class);
217    
218        servicesContainer.startComponents();
219      }
220    
221      private void executeStartupTasks() {
222        ComponentContainer startupContainer = servicesContainer.createChild();
223        startupContainer.addSingleton(MavenRepository.class);
224        startupContainer.addSingleton(GwtPublisher.class);
225        startupContainer.addSingleton(RegisterMetrics.class);
226        startupContainer.addSingleton(RegisterRules.class);
227        startupContainer.addSingleton(RegisterProvidedProfiles.class);
228        startupContainer.addSingleton(EnableProfiles.class);
229        startupContainer.addSingleton(ActivateDefaultProfiles.class);
230        startupContainer.addSingleton(JdbcDriverDeployer.class);
231        startupContainer.addSingleton(ServerMetadataPersister.class);
232        startupContainer.addSingleton(RegisterQualityModels.class);
233        startupContainer.addSingleton(DeleteDeprecatedMeasures.class);
234        startupContainer.addSingleton(GeneratePluginIndex.class);
235        startupContainer.addSingleton(RegisterNewDashboards.class);
236        startupContainer.startComponents();
237    
238        startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart();
239    
240        // Do not put the following statements in a finally block.
241        // It would hide the possible exception raised during startup
242        // See SONAR-3107
243        startupContainer.stopComponents();
244        servicesContainer.removeChild();
245        servicesContainer.getComponentByType(DatabaseSessionFactory.class).clear();
246      }
247    
248      public void stop() {
249        if (rootContainer != null) {
250          try {
251            TimeProfiler profiler = new TimeProfiler().start("Stop sonar");
252            rootContainer.stopComponents();
253            rootContainer = null;
254            connected = false;
255            started = false;
256            profiler.stop();
257          } catch (Exception e) {
258            LoggerFactory.getLogger(getClass()).debug("Fail to stop Sonar - ignored", e);
259          }
260        }
261      }
262    
263      public ComponentContainer getContainer() {
264        if (servicesContainer != null) {
265          return servicesContainer;
266        }
267        if (coreContainer != null) {
268          return coreContainer;
269        }
270        return rootContainer;
271      }
272    
273      public Object getComponent(Object key) {
274        return getContainer().getComponentByKey(key);
275      }
276    
277      /**
278       * shortcut for ruby code
279       */
280      public static Server getServer() {
281        return (Server) getInstance().getComponent(Server.class);
282      }
283    }