| /*=============================================================================# |
| # Copyright (c) 2018, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.rhelp.server.update; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| |
| import static org.eclipse.statet.rhelp.server.Application.BUNDLE_ID; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.ScheduledFuture; |
| import java.util.concurrent.ScheduledThreadPoolExecutor; |
| import java.util.concurrent.SynchronousQueue; |
| import java.util.concurrent.ThreadPoolExecutor; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.annotation.PreDestroy; |
| |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.boot.SpringApplication; |
| import org.springframework.context.ApplicationContext; |
| import org.springframework.stereotype.Component; |
| |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.concurrent.CommonThreadFactory; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.rmi.RMIRegistryManager; |
| import org.eclipse.statet.jcommons.runtime.CommonsRuntime; |
| import org.eclipse.statet.jcommons.status.ErrorStatus; |
| |
| import org.eclipse.statet.rhelp.core.RHelpManager; |
| import org.eclipse.statet.rj.renv.core.REnv; |
| import org.eclipse.statet.rj.renv.core.REnvManager; |
| |
| |
| @Component |
| @NonNullByDefault |
| public class REnvIndexController { |
| |
| |
| public static final TimeUnit TIME_UNIT= TimeUnit.MILLISECONDS; |
| |
| public static final String INDEX_AND_EXIT_PROFILE= "index-and-exit"; //$NON-NLS-1$ |
| private static final byte INDEX_AND_EXIT= 1 << 1; |
| |
| |
| private final REnvManager rEnvManager; |
| |
| private final RHelpManager rHelpManager; |
| |
| private final int mode; |
| private final REnvIndexConfig config; |
| |
| private final ThreadGroup threadGroup; |
| private final ScheduledThreadPoolExecutor mainExecutorService; |
| private final ExecutorService helperExecutorService; |
| private final CommonThreadFactory monitorExecutorService; |
| |
| private final List<REnvIndexer> indexers; |
| |
| |
| @Autowired |
| public REnvIndexController(final REnvManager rEnvManager, final RHelpManager rHelpManager, |
| final REnvIndexConfig config, final ApplicationContext appContext) { |
| this.rEnvManager= nonNullAssert(rEnvManager); |
| this.rHelpManager= nonNullAssert(rHelpManager); |
| if (appContext.getEnvironment().acceptsProfiles(INDEX_AND_EXIT_PROFILE)) { |
| this.mode= INDEX_AND_EXIT; |
| } |
| else { |
| this.mode= 0; |
| } |
| this.config= nonNullAssert(config); |
| |
| this.threadGroup= new ThreadGroup("REnvHelpIndex"); |
| this.mainExecutorService= new ScheduledThreadPoolExecutor(1, |
| new CommonThreadFactory(this.threadGroup, "MainPool")); |
| this.helperExecutorService= new ThreadPoolExecutor(1, |
| Math.max(2, Math.min(Runtime.getRuntime().availableProcessors(), 16)), |
| 30L, TimeUnit.MINUTES, new SynchronousQueue<Runnable>(), |
| new CommonThreadFactory(this.threadGroup, "HelperPool")); |
| this.monitorExecutorService= |
| new CommonThreadFactory(this.threadGroup, "RLibPathsMonitor") { |
| @Override |
| protected boolean getDaemon() { |
| return true; |
| } |
| }; |
| |
| final ImList<? extends REnv> rEnvs= this.rEnvManager.list(); |
| final List<REnvIndexer> list= new ArrayList<>(); |
| for (final REnv rEnv : rEnvs) { |
| list.add(new REnvIndexer(this, rEnv)); |
| } |
| this.indexers= list; |
| |
| start(); |
| |
| if (this.mode == INDEX_AND_EXIT) { |
| this.helperExecutorService.execute(new Runnable() { |
| private int indexShutdownResult; |
| @Override |
| public void run() { |
| this.indexShutdownResult= 20; |
| try { |
| REnvIndexController.this.mainExecutorService.shutdown(); |
| if (REnvIndexController.this.mainExecutorService |
| .awaitTermination(1, TimeUnit.DAYS) ) { |
| this.indexShutdownResult= 0; |
| } |
| else { |
| this.indexShutdownResult= 21; |
| CommonsRuntime.log(new ErrorStatus(BUNDLE_ID, |
| "Shutdown because of timeout." )); |
| } |
| } |
| catch (final InterruptedException e) { |
| this.indexShutdownResult= 22; |
| CommonsRuntime.log(new ErrorStatus(BUNDLE_ID, |
| "Shutdown because of interrupt.", e )); |
| } |
| finally { |
| final int exitCode= SpringApplication.exit(appContext, |
| () -> this.indexShutdownResult ); |
| |
| System.exit(exitCode); |
| } |
| } |
| }); |
| } |
| } |
| |
| |
| public REnvManager getREnvManager() { |
| return this.rEnvManager; |
| } |
| |
| public RHelpManager getRHelpManager() { |
| return this.rHelpManager; |
| } |
| |
| |
| ScheduledFuture<?> schedule(final REnvIndexer rEnvIndexer, final int delay) { |
| return this.mainExecutorService.schedule(rEnvIndexer, delay, TIME_UNIT); |
| } |
| |
| void startMonitor(final Runnable runnable) { |
| this.monitorExecutorService.newThread(runnable).start(); |
| } |
| |
| ExecutorService getHelperExecutorService() { |
| return this.helperExecutorService; |
| } |
| |
| |
| public int getMonitorDelay() { |
| switch (this.mode) { |
| case INDEX_AND_EXIT: |
| return -1; |
| default: |
| return this.config.getMonitorDelay(); |
| } |
| } |
| |
| public int getPeriodicDelay() { |
| switch (this.mode) { |
| case INDEX_AND_EXIT: |
| return -1; |
| default: |
| return this.config.getPeriodicDelay(); |
| } |
| } |
| |
| |
| private void start() { |
| this.mainExecutorService.setCorePoolSize(Math.max(1, |
| Math.min(Runtime.getRuntime().availableProcessors() * 2 / 3, this.indexers.size()) ) ); |
| RMIRegistryManager.INSTANCE.setEmbeddedPrivateMode(false); |
| |
| for (final REnvIndexer indexer : this.indexers) { |
| indexer.start(); |
| } |
| } |
| |
| @PreDestroy |
| private void stop() { |
| this.mainExecutorService.shutdown(); |
| |
| for (final REnvIndexer indexer : this.indexers) { |
| indexer.stop(); |
| } |
| } |
| |
| } |