| /*=============================================================================# |
| # 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() )); |
| } |
| |
| } |