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 }