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 }