blob: 05813e868c8ff7ab7d8558da58daf6692fb1fc56 [file] [log] [blame]
/*=============================================================================#
# 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();
}
}
}