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