sonar ce 的启动过程
/* * SonarQube * Copyright (C) 2009-2016 SonarSource SA * mailto:contact AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.ce.app; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.CheckForNull; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.ce.ComputeEngine; import org.sonar.ce.ComputeEngineImpl; import org.sonar.ce.container.ComputeEngineContainerImpl; import org.sonar.process.MinimumViableSystem; import org.sonar.process.Monitored; import org.sonar.process.ProcessEntryPoint; import org.sonar.process.Props; import org.sonar.ce.log.CeProcessLogging; import static com.google.common.base.Preconditions.checkState; import static org.sonar.process.ProcessUtils.awaitTermination; /** * The Compute Engine server which starts a daemon thread to run the {@link ComputeEngineImpl} when it's {@link #start()} * method is called. * <p> * This is the class to call to run a standalone {@link ComputeEngineImpl} (see {@link #main(String[])}). * </p> */ public class CeServer implements Monitored { private static final Logger LOG = Loggers.get(CeServer.class); private static final String CE_MAIN_THREAD_NAME = "ce-main"; /** * Thread that currently is inside our await() method. */ private AtomicReference<Thread> awaitThread = new AtomicReference<>(); private volatile boolean stopAwait = false; private final StartupBarrier startupBarrier; private final ComputeEngine computeEngine; @CheckForNull private CeMainThread ceMainThread = null; @VisibleForTesting protected CeServer(StartupBarrier startupBarrier, ComputeEngine computeEngine, MinimumViableSystem mvs) { this.startupBarrier = startupBarrier; this.computeEngine = computeEngine; mvs .checkWritableTempDir() .checkRequiredJavaOptions(ImmutableMap.of("file.encoding", "UTF-8")); } @Override public void start() { checkState(ceMainThread == null, "start() can not be called twice"); // start main thread ceMainThread = new CeMainThread(); ceMainThread.start(); } @Override public boolean isUp() { checkState(ceMainThread != null, "isUp() can not be called before start()"); return ceMainThread.isStarted(); } @Override public void awaitStop() { checkState(awaitThread.compareAndSet(null, Thread.currentThread()), "There can't be more than one thread waiting for the Compute Engine to stop"); checkState(ceMainThread != null, "awaitStop() must not be called before start()"); try { while (!stopAwait) { try { // wait for a quite long time but we will be interrupted if flag changes anyway Thread.sleep(10_000); } catch (InterruptedException e) { // continue and check the flag } } } finally { awaitThread = null; } } @Override public void stop() { if (ceMainThread != null) { // signal main Thread to stop ceMainThread.stopIt(); awaitTermination(ceMainThread); } } /** * Can't be started as is. Needs to be bootstrapped by sonar-application */ public static void main(String[] args) { ProcessEntryPoint entryPoint = ProcessEntryPoint.createForArguments(args); Props props = entryPoint.getProps(); new CeProcessLogging().configure(props); CeServer server = new CeServer( new StartupBarrierFactory().create(entryPoint), new ComputeEngineImpl(props, new ComputeEngineContainerImpl()), new MinimumViableSystem()); entryPoint.launch(server); } private class CeMainThread extends Thread { private static final int CHECK_FOR_STOP_DELAY = 50; private volatile boolean stop = false; private volatile boolean started = false; public CeMainThread() { super(CE_MAIN_THREAD_NAME); } @Override public void run() { boolean webServerOperational = startupBarrier.waitForOperational(); if (!webServerOperational) { LOG.debug("Interrupted while waiting for WebServer to be operational. Assuming it will never be. Stopping."); // signal CE is done booting (obviously, since we are about to stop) this.started = true; // release thread (if any) in CeServer#awaitStop() stopAwait(); return; } boolean startupSuccessful = attemptStartup(); this.started = true; if (startupSuccessful) { // call below is blocking waitForStopSignal(); } else { stopAwait(); } } private boolean attemptStartup() { try { startup(); return true; } catch (Throwable e) { LOG.error("Compute Engine startup failed", e); return false; } } private void startup() { LOG.info("Compute Engine starting up..."); computeEngine.startup(); LOG.info("Compute Engine is up"); } private void waitForStopSignal() { while (!stop) { try { Thread.sleep(CHECK_FOR_STOP_DELAY); } catch (InterruptedException e) { // ignore the interruption itself, check the flag } } attemptShutdown(); } private void attemptShutdown() { try { shutdown(); } catch (Throwable e) { LOG.error("Compute Engine shutdown failed", e); } finally { // release thread waiting for CeServer stopAwait(); } } private void shutdown() { LOG.info("Compute Engine shutting down..."); computeEngine.shutdown(); } public boolean isStarted() { return started; } public void stopIt() { // stop looping indefinitely this.stop = true; // interrupt current thread in case its waiting for WebServer interrupt(); } private void stopAwait() { stopAwait = true; Thread t = awaitThread.get(); if (t != null) { t.interrupt(); try { t.join(1000); } catch (InterruptedException e) { // Ignored } } } } }
调用的类ComputeEngineImpl->ComputeEngine:
/* * SonarQube * Copyright (C) 2009-2016 SonarSource SA * mailto:contact AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.ce; import org.sonar.ce.container.ComputeEngineContainer; import org.sonar.process.Props; import static com.google.common.base.Preconditions.checkState; public class ComputeEngineImpl implements ComputeEngine { private final Props props; private final ComputeEngineContainer computeEngineContainer; private Status status = Status.INIT; public ComputeEngineImpl(Props props, ComputeEngineContainer computeEngineContainer) { this.props = props; this.computeEngineContainer = computeEngineContainer; } @Override public void startup() { checkState(this.status == Status.INIT, "startup() can not be called multiple times"); try { this.status = Status.STARTING; this.computeEngineContainer.start(props); } finally { this.status = Status.STARTED; } } @Override public void shutdown() { checkStateAsShutdown(this.status); try { this.status = Status.STOPPING; this.computeEngineContainer.stop(); } finally { this.status = Status.STOPPED; } } private static void checkStateAsShutdown(Status currentStatus) { checkState(currentStatus.ordinal() >= Status.STARTED.ordinal(), "shutdown() must not be called before startup()"); checkState(currentStatus.ordinal() <= Status.STOPPING.ordinal(), "shutdown() can not be called multiple times"); } private enum Status { INIT, STARTING, STARTED, STOPPING, STOPPED } }
调用ComputeEngineContainerImpl->ComputeEngineContainer
public class ComputeEngineContainerImpl implements ComputeEngineContainer { @CheckForNull private ComponentContainer level1; @CheckForNull private ComponentContainer level4; @Override public ComputeEngineContainer start(Props props) { this.level1 = new ComponentContainer(); this.level1 .add(props.rawProperties()) .add(level1Components()) .add(toArray(CorePropertyDefinitions.all())) .add(toArray(ClusterProperties.definitions())); configureFromModules(this.level1); this.level1.startComponents(); ComponentContainer level2 = this.level1.createChild(); level2.add(level2Components()); configureFromModules(level2); level2.startComponents(); ComponentContainer level3 = level2.createChild(); level3.add(level3Components()); configureFromModules(level3); level3.startComponents(); this.level4 = level3.createChild(); this.level4.add(level4Components()); configureFromModules(this.level4); ServerExtensionInstaller extensionInstaller = this.level4.getComponentByType(ServerExtensionInstaller.class); extensionInstaller.installExtensions(this.level4); this.level4.startComponents(); startupTasks(); return this; } private void startupTasks() { ComponentContainer startupLevel = this.level4.createChild(); startupLevel.add(startupComponents()); startupLevel.startComponents(); // done in PlatformLevelStartup ServerLifecycleNotifier serverLifecycleNotifier = startupLevel.getComponentByType(ServerLifecycleNotifier.class); if (serverLifecycleNotifier != null) { serverLifecycleNotifier.notifyStart(); } startupLevel.stopComponents(); } @Override public ComputeEngineContainer stop() { this.level1.stopComponents(); return this; } @VisibleForTesting protected ComponentContainer getComponentContainer() { return level4; } private static Object[] level1Components() { Version apiVersion = ApiVersion.load(System2.INSTANCE); return new Object[] { ThreadLocalSettings.class, new SonarQubeVersion(apiVersion), SonarRuntimeImpl.forSonarQube(ApiVersion.load(System2.INSTANCE), SonarQubeSide.COMPUTE_ENGINE), CeProcessLogging.class, UuidFactoryImpl.INSTANCE, ClusterImpl.class, LogbackHelper.class, DefaultDatabase.class, DatabaseChecker.class, // must instantiate deprecated class in 5.2 and only this one (and not its replacement) // to avoid having two SqlSessionFactory instances org.sonar.core.persistence.MyBatis.class, DatabaseServerCompatibility.class, DatabaseVersion.class, PurgeProfiler.class, ServerFileSystemImpl.class, new TempFolderProvider(), System2.INSTANCE, // user session CeUserSession.class, // DB DaoModule.class, ReadOnlyPropertiesDao.class, DbClient.class, // Elasticsearch EsSearchModule.class, // rules/qprofiles RuleIndex.class, ActiveRuleIndex.class, // issues IssueIndex.class, new OkHttpClientProvider() }; } private static Object[] level2Components() { return new Object[] { DatabaseSettingLoader.class, DatabaseSettingsEnabler.class, UrlSettings.class, // add ReadOnlyPropertiesDao at level2 again so that it shadows PropertiesDao ReadOnlyPropertiesDao.class, DefaultServerUpgradeStatus.class, // plugins PluginClassloaderFactory.class, CePluginJarExploder.class, PluginLoader.class, CePluginRepository.class, InstalledPluginReferentialFactory.class, ComputeEngineExtensionInstaller.class, // depends on plugins DefaultI18n.class, // used by RuleI18nManager RuleI18nManager.class, // used by DebtRulesXMLImporter Durations.class, // used in Web Services and DebtCalculator }; } private static Object[] level3Components() { return new Object[] { new StartupMetadataProvider(), ServerIdManager.class, UriReader.class, ServerImpl.class, DefaultOrganizationProviderImpl.class }; } private static Object[] level4Components() { return new Object[] { ResourceTypes.class, DefaultResourceTypes.get(), Periods.class, // used by JRuby and EvaluationResultTextConverterImpl // quality profile ActiveRuleIndexer.class, XMLProfileParser.class, XMLProfileSerializer.class, AnnotationProfileParser.class, Rules.QProfiles.class, QProfileLookup.class, QProfileProjectOperations.class, // rule RuleIndexer.class, AnnotationRuleParser.class, XMLRuleParser.class, DefaultRuleFinder.class, DeprecatedRulesDefinitionLoader.class, CommonRuleDefinitionsImpl.class, RuleDefinitionsLoader.class, RulesDefinitionXmlLoader.class, // languages Languages.class, // used by CommonRuleDefinitionsImpl // measure CoreCustomMetrics.class, DefaultMetricFinder.class, // users DeprecatedUserFinder.class, DefaultUserFinder.class, UserIndexer.class, UserIndex.class, // permissions PermissionTemplateService.class, PermissionUpdater.class, UserPermissionChanger.class, GroupPermissionChanger.class, // components ComponentFinder.class, // used in ComponentService ComponentService.class, // used in ReportSubmitter NewAlerts.class, NewAlerts.newMetadata(), ComponentCleanerService.class, ProjectMeasuresIndexer.class, // views ViewIndexer.class, ViewIndex.class, // issues IssueIndexer.class, PermissionIndexer.class, IssueUpdater.class, // used in Web Services and CE's DebtCalculator FunctionExecutor.class, // used by IssueWorkflow IssueWorkflow.class, // used in Web Services and CE's DebtCalculator NewIssuesEmailTemplate.class, MyNewIssuesEmailTemplate.class, IssueChangesEmailTemplate.class, AlertsEmailTemplate.class, ChangesOnMyIssueNotificationDispatcher.class, ChangesOnMyIssueNotificationDispatcher.newMetadata(), NewIssuesNotificationDispatcher.class, NewIssuesNotificationDispatcher.newMetadata(), MyNewIssuesNotificationDispatcher.class, MyNewIssuesNotificationDispatcher.newMetadata(), DoNotFixNotificationDispatcher.class, DoNotFixNotificationDispatcher.newMetadata(), NewIssuesNotificationFactory.class, // used by SendIssueNotificationsStep EmailNotificationChannel.class, // technical debt DebtModelPluginRepository.class, DebtRulesXMLImporter.class, // Notifications EmailSettings.class, NotificationService.class, NotificationCenter.class, DefaultNotificationManager.class, // Tests TestIndexer.class, // System ServerLogging.class, // privileged plugins PrivilegedPluginsBootstraper.class, PrivilegedPluginsStopper.class, // Compute engine (must be after Views and Developer Cockpit) CeConfigurationModule.class, CeQueueModule.class, CeHttpModule.class, CeTaskCommonsModule.class, ProjectAnalysisTaskModule.class, CeTaskProcessorModule.class, InternalPropertiesImpl.class, ProjectSettingsFactory.class, }; } private static Object[] startupComponents() { return new Object[] { LogServerId.class, ServerLifecycleNotifier.class, PurgeCeActivities.class, }; } private static Object[] toArray(List<?> list) { return list.toArray(new Object[list.size()]); } private static void configureFromModules(ComponentContainer container) { List<Module> modules = container.getComponentsByType(Module.class); for (Module module : modules) { module.configure(container); } } }start方法调用startTask方法