blob: 9ebfcfd6be33825f0dc8887e3ef0ff0dba68d564 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2018, 2021 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.concurrent.ScheduledFuture;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.runtime.CommonsRuntime;
import org.eclipse.statet.jcommons.status.ErrorStatus;
import org.eclipse.statet.jcommons.status.InfoStatus;
import org.eclipse.statet.jcommons.status.NullProgressMonitor;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.rhelp.core.REnvHelpConfiguration;
import org.eclipse.statet.rhelp.core.update.REnvIndexUpdater;
import org.eclipse.statet.rj.renv.core.REnv;
import org.eclipse.statet.rj.renv.runtime.RPkgManager;
import org.eclipse.statet.rj.renv.runtime.RPkgManagerDataset;
import org.eclipse.statet.rj.servi.RServi;
import org.eclipse.statet.rj.services.RService;
@NonNullByDefault
public class REnvIndexer implements Runnable {
public static final byte PERIODIC= 1;
public static final byte LIB_PATHS_MONITOR= 2;
public static final byte EXPLICITE= 3;
private static final byte STOPPED= -1;
private static String getTriggerString(final byte type) {
switch (type) {
case PERIODIC:
return "PERIODIC";
case LIB_PATHS_MONITOR:
return "LIB_PATHS_MONITOR";
case EXPLICITE:
return "EXPLICITE";
default:
return Integer.toString(type);
}
}
private final REnvIndexController controller;
private final REnv rEnv;
private final String logPrefix;
private byte isScheduled= STOPPED;
private int isRequested;
private @Nullable ScheduledFuture<?> scheduled;
private boolean isRunning;
private final ProgressMonitor progress;
private final RLibPathsMonitor rLibPathsMonitor;
private final REnvServiController rServi;
public REnvIndexer(final REnvIndexController controller, final REnv rEnv) {
this.controller= nonNullAssert(controller);
this.rEnv= nonNullAssert(rEnv);
this.logPrefix= String.format("REnv '%1$s': ", this.rEnv.getId());
this.progress= new NullProgressMonitor();
this.rLibPathsMonitor= new RLibPathsMonitor(this);
this.rServi= new REnvServiController(this.rEnv);
}
public REnv getREnv() {
return this.rEnv;
}
REnvIndexController getController() {
return this.controller;
}
String getLogPrefix() {
return this.logPrefix;
}
public synchronized void start() {
if (this.isScheduled != STOPPED) {
return;
}
this.isScheduled= 0;
schedule(EXPLICITE);
}
public synchronized void stop() {
if (this.isScheduled == STOPPED) {
return;
}
this.isScheduled= 0;
this.rLibPathsMonitor.stop();
final ScheduledFuture<?> scheduled= this.scheduled;
if (scheduled != null) {
this.scheduled= null;
scheduled.cancel(false);
}
}
public synchronized void schedule(byte type) {
if (this.isScheduled < 0) {
return;
}
if (this.isScheduled > type) {
if (this.scheduled != null || this.isRunning) {
return;
}
if (this.scheduled == null) {
type= this.isScheduled;
this.isScheduled= 0;
}
}
final int delay= getScheduleDelay(type);
if (delay < 0) {
return;
}
final ScheduledFuture<?> scheduled= this.scheduled;
if (scheduled != null) {
this.scheduled= null;
scheduled.cancel(false);
}
if (!this.isRunning) {
this.scheduled= this.controller.schedule(this, delay);
}
this.isScheduled= type;
}
private int getScheduleDelay(final byte type) {
switch (type) {
case EXPLICITE:
return 100;
case LIB_PATHS_MONITOR:
return this.controller.getMonitorDelay();
case PERIODIC:
return this.controller.getPeriodicDelay();
default:
return -1;
}
}
public synchronized void cancel() {
this.isScheduled= 0;
final ScheduledFuture<?> scheduled= this.scheduled;
if (scheduled != null) {
this.scheduled= null;
scheduled.cancel(false);
}
if (this.isRunning) {
this.progress.setCanceled(true);
}
}
@Override
public void run() {
final byte trigger;
synchronized (this) {
trigger= this.isScheduled;
if (trigger <= 0) {
return;
}
this.isScheduled= 0;
this.isRunning= true;
this.progress.setCanceled(false);
}
try {
CommonsRuntime.log(new InfoStatus(BUNDLE_ID,
String.format("%1$sR environment index update started (%2$s).",
this.logPrefix, getTriggerString(trigger) )));
this.rServi.configure();
this.rServi.startR(this.progress);
try (RServi r= this.rServi.getRServi("R environment index")) {
run(r, this.progress);
}
}
catch (final Throwable e) {
CommonsRuntime.log(Status.newStatus((e instanceof StatusException) ?
((StatusException)e).getStatus().getSeverity() : Status.ERROR,
BUNDLE_ID,
String.format("%1$sR environment index did not complete normally.",
this.logPrefix ),
e ));
}
finally {
this.rServi.stopR();
synchronized (this) {
this.isRunning= false;
schedule(PERIODIC);
}
}
}
private void run(final RService r, final ProgressMonitor m) throws StatusException {
final RPkgManagerImpl rPkgManager= new RPkgManagerImpl(this.rEnv);
rPkgManager.check(RPkgManager.NONE, r, m);
if (this.controller.getMonitorDelay() >= 0) {
final RPkgManagerDataset dataset= nonNullAssert(rPkgManager.getDataset());
this.rLibPathsMonitor.check(dataset.getRLibPaths());
}
final REnvHelpConfiguration rEnvHelpConfig= this.rEnv.get(REnvHelpConfiguration.class);
if (rEnvHelpConfig == null) {
throw new StatusException(new ErrorStatus(BUNDLE_ID,
"R help configuration is missing." ));
}
final REnvIndexUpdater updater= new ServerREnvIndexUpdater(rEnvHelpConfig,
this.controller.getRHelpManager(), rPkgManager,
this.controller.getHelperExecutorService() );
final int request;
synchronized (this) {
request= this.isRequested;
this.isRequested= 0;
}
final Status status= updater.update(r, (request != 0), null, m);
CommonsRuntime.log(Status.newStatus(status.getSeverity(), BUNDLE_ID, status.getCode(),
this.logPrefix + status.getMessage(),
status.getException() ));
}
}