package org.easyrec.plugin.support; import org.easyrec.plugin.Executable; import org.easyrec.plugin.Progress; import org.easyrec.plugin.exception.PluginException; import org.easyrec.plugin.model.Version; import org.easyrec.plugin.stats.ExecutableStatistics; import org.easyrec.plugin.util.ObserverRegistry; import org.easyrec.plugin.util.ObserverRegistryImpl; import java.net.URI; import java.util.Date; public abstract class ExecutablePluginSupport<S extends ExecutableStatistics> extends PluginSupport implements Executable<S> { private ObserverRegistryImpl<Executable> executableObserverRegistry = new ObserverRegistryImpl<Executable>(this); private ExecutionState executionState = ExecutionState.STOPPED; private Progress progress; private ExecutionControl executionControl = new ExecutionControl(); private Class<S> statsClass; /** * Constructor that sets the displayName, id, and version. As Java makes it * almost impossible to create an instance of a class that is passed as a * generic type argument, and as we need to do it anyway, the class object of * the stats class is necessary here, too. * * @param displayName * @param id * @param version * @param statsClass */ public ExecutablePluginSupport(String displayName, URI id, Version version, Class<S> statsClass) { super(displayName, id, version); this.statsClass = statsClass; this.progress = Progress.NOT_RUNNING; } public final void abort() { if (this.executionState.isRunning()) { changeExecutionState(ExecutionState.ABORT_REQUESTED); } } public final S execute() throws Exception { if (!getLifecyclePhase().isInitialized()) { throw new IllegalStateException("Executable has not been initialized"); } S stats = null; try { stats = createExecutableStats(); stats.setStartDate(new Date()); changeExecutionState(ExecutionState.RUNNING); doExecute(executionControl, stats); } catch (Throwable t) { throw new PluginException(this, "Caught error during doExecute", t); } finally { changeExecutionState(ExecutionState.STOPPED); if (stats != null) stats.setEndDate(new Date()); } return stats; } public ObserverRegistry<Executable> getExecutableObserverRegistry() { return this.executableObserverRegistry; } public ExecutionState getExecutionState() { return this.executionState; } public Progress getProgress() { return this.progress; } /** * Implementations place their main code here. Any Exception is wrapped in a * PluginException and thrown on. Implementations should check the * executionState frequently so that the application is responsive when * <code>abort()</code> is called. Likewise, the implementation should call * <code>updateProgress(Progress)</code> frequently so that the application * state can be monitored easily. * <p/> * The ExecutableStats * * @throws Exception */ protected abstract void doExecute(ExecutionControl control, S stats) throws Exception; protected void updateProgress(Progress progress) { this.progress = progress; this.executableObserverRegistry.notifyObservers(); } /** * Factory method for creating in instance of ExecutableStats. */ private S createExecutableStats() { try { return this.statsClass.newInstance(); } catch (Exception ex) { logger.warn("could not create instance of stats class " + this.statsClass.getName()); throw new IllegalStateException("could not create instance of stats class " + this.statsClass.getName(), ex); } } public Class<S> getStatisticsClass() { return statsClass; } private void changeExecutionState(ExecutionState newState) { this.executionState = newState; this.executableObserverRegistry.notifyObservers(); } public class ExecutionControl { public boolean isAbortRequested() { return executionState.isAbortRequested(); } public void updateProgress(Progress progress) { ExecutablePluginSupport.this.updateProgress(progress); } public void updateProgress(int currentStep, int totalSteps, String message) { updateProgress(new Progress(currentStep, totalSteps, message)); } /** * Updates the {@code message} but leaves {@code currentStep} and {@code totalSteps} at the old values. * * @param message */ public void updateProgress(String message) { updateProgress(getProgress().getCurrentSteps(), message); } /** * Updates the {@code @message} and {@code currentStep} but leaves {@code totalSteps} at the old value. * * @param currentStep * @param message */ public void updateProgress(int currentStep, String message) { updateProgress(currentStep, getProgress().getTotalSteps(), message); } } }