/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.executor; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.geoserver.wps.ProcessDismissedException; import org.geoserver.wps.ProcessEvent; import org.geoserver.wps.ProcessListener; import org.geotools.util.logging.Logging; import org.opengis.util.InternationalString; import org.opengis.util.ProgressListener; /** * Handles notification to all the {@link ProcessListener} implementations for a given process * * @author Andrea Aime - GeoSolutions */ public class ProcessListenerNotifier { static final Logger LOGGER = Logging.getLogger(ProcessListenerNotifier.class); ExecutionStatus status; List<ProcessListener> listeners; WPSProgressListener progressListener; LazyInputMap inputs; Map<String, Object> outputs; ExecuteRequest request; public ProcessListenerNotifier(ExecutionStatus status, ExecuteRequest request, LazyInputMap inputs, List<ProcessListener> listeners) { this.status = status; this.request = request; this.progressListener = new WPSProgressListener(); this.inputs = inputs; this.listeners = listeners; fireProcessSubmitted(); } public void fireProcessSubmitted() { ProcessEvent event = new ProcessEvent(status, inputs); for (ProcessListener listener : listeners) { listener.submitted(event); } } public void fireProgress(float progress, String task) { if (progress > status.progress || StringUtils.equals(task, status.task)) { if (status.getPhase() == ProcessState.QUEUED) { status.setPhase(ProcessState.RUNNING); } status.setProgress(progress); status.setTask(task); ProcessEvent event = new ProcessEvent(status, inputs, outputs); for (ProcessListener listener : listeners) { listener.progress(event); } } } public void fireFailed(Throwable e) { status.setPhase(ProcessState.FAILED); if (e != null) { status.setException(e); } ProcessEvent event = new ProcessEvent(status, inputs, outputs); for (ProcessListener listener : listeners) { listener.failed(event); } } public void fireSucceded() { status.setPhase(ProcessState.SUCCEEDED); status.setProgress(100); status.setTask(null); ProcessEvent event = new ProcessEvent(status, inputs, outputs); for (ProcessListener listener : listeners) { listener.succeeded(event); } } public void fireCompleted() { if (status.getPhase() == ProcessState.RUNNING) { fireSucceded(); } else if (status.getPhase() == ProcessState.DISMISSING) { fireDismissed(); } else { fireFailed(null); } } /** * Notifies all listeners that the process is being dismissed */ public void dismiss() { this.status.phase = ProcessState.DISMISSING; ProcessEvent event = new ProcessEvent(status, inputs, outputs); for (ProcessListener listener : listeners) { listener.dismissing(event); } } /** * Notifies all listeners that the process is being dismissed */ public void fireDismissed() { this.status.phase = ProcessState.FAILED; ProcessEvent event = new ProcessEvent(status, inputs, outputs); for (ProcessListener listener : listeners) { listener.dismissed(event); } } /** * Listens to the process progress and allows to cancel it * * @author Andrea Aime - GeoSolutions */ class WPSProgressListener implements ProgressListener { InternationalString task; String description; Throwable exception; @Override public InternationalString getTask() { return task; } @Override public void setTask(InternationalString task) { this.task = task; checkDismissed(); fireProgress(status.progress, task.toString()); } @Override public String getDescription() { return this.description; } @Override public void setDescription(String description) { checkDismissed(); this.description = description; } @Override public void started() { progress(0f); } @Override public void progress(float percent) { // force process to just exit immediately checkDismissed(); fireProgress(percent, task != null ? task.toString() : null); } @Override public float getProgress() { return status.progress; } @Override public void complete() { progress(100); } @Override public void dispose() { // nothing to do } @Override public boolean isCanceled() { return status.phase == ProcessState.DISMISSING; } @Override public void setCanceled(boolean cancel) { dismiss(); } @Override public void warningOccurred(String source, String location, String warning) { LOGGER.log(Level.WARNING, "Got a warning during process execution " + status.getExecutionId() + ": " + warning); // force process to just exit immediately checkDismissed(); } @Override public void exceptionOccurred(Throwable exception) { // do not record the exception if we just forced the process to bail out if (status.phase != ProcessState.DISMISSING) { this.exception = exception; fireFailed(exception); } } public Throwable getException() { return exception; } } public WPSProgressListener getProgressListener() { return progressListener; } /** * Throws a process cancelled exception if the process has been cancelled */ public void checkDismissed() { if (status.getPhase() == ProcessState.DISMISSING) { throw new ProcessDismissedException(); } } }