/* (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.Date; import java.util.logging.Logger; import org.geoserver.platform.ExtensionPriority; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.wps.MemoryProcessStatusStore; import org.geoserver.wps.ProcessEvent; import org.geoserver.wps.ProcessListener; import org.geoserver.wps.ProcessStatusStore; import org.geoserver.wps.WPSException; import org.geotools.factory.CommonFactoryFinder; import org.geotools.util.logging.Logging; import org.opengis.filter.And; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.Not; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * A listener that tracks the evolution of process execution and stores it in a * {@link ProcessStatusStore} * * @author Andrea Aime - GeoSolutions */ public class ProcessStatusTracker implements ApplicationContextAware, ProcessListener, ExtensionPriority { static final FilterFactory FF = CommonFactoryFinder.getFilterFactory(); static final Logger LOGGER = Logging.getLogger(ProcessStatusTracker.class); ProcessStatusStore store; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ProcessStatusStore store = GeoServerExtensions.bean(ProcessStatusStore.class, applicationContext); if (store == null) { store = new MemoryProcessStatusStore(); } this.store = store; } @Override public void submitted(ProcessEvent event) throws WPSException { if(store == null) { return; } store.save(event.getStatus()); } /** * Custom method that updates the status last updated field without touching anything else, to * make sure we let the cluster know the process is still running * * @param executionId * @throws WPSException */ public void touch(String executionId) throws WPSException { ExecutionStatus status = store.get(executionId); if (status != null) { status.setLastUpdated(new Date()); store.save(status); } } @Override public void succeeded(ProcessEvent event) throws WPSException { ExecutionStatus newStatus = event.getStatus(); ExecutionStatus original = store.get(newStatus.getExecutionId()); newStatus.setLastUpdated(new Date()); store.save(newStatus); // update the status in the event to let the process know it has been cancelled if (original.getPhase() == ProcessState.DISMISSING) { event.getStatus().setPhase(ProcessState.FAILED); } } @Override public void dismissing(ProcessEvent event) throws WPSException { ExecutionStatus status = event.getStatus(); status.setLastUpdated(new Date()); store.save(status); } @Override public void dismissed(ProcessEvent event) throws WPSException { ExecutionStatus status = event.getStatus(); store.remove(status.getExecutionId()); } @Override public void failed(ProcessEvent event) { ExecutionStatus status = event.getStatus(); status.setLastUpdated(new Date()); store.save(status); } @Override public void progress(ProcessEvent event) throws WPSException { ExecutionStatus original = store.get(event.getStatus().getExecutionId()); if (original.getPhase() == ProcessState.DISMISSING) { event.getStatus().setPhase(ProcessState.DISMISSING); } else { ExecutionStatus newStatus = event.getStatus(); newStatus.setLastUpdated(new Date()); store.save(newStatus); } } public ExecutionStatus getStatus(String executionId) { return store.get(executionId); } public void cleanExpiredStatuses(long expirationThreshold) { Date date = new Date(expirationThreshold); Not completionTimenotNull = FF.not(FF.isNull(FF.property("completionTime"))); Filter completionTimeExpired = FF.before(FF.property("completionTime"), FF.literal(date)); Filter completionTimeFilter = FF.and(completionTimenotNull, completionTimeExpired); Not lastUpdatedNotNull = FF.not(FF.isNull(FF.property("lastUpdated"))); Filter lastUpdatedExpired = FF.before(FF.property("lastUpdated"), FF.literal(date)); Filter lastUpdatedFilter = FF.and(lastUpdatedNotNull, lastUpdatedExpired); And filter = FF.and(completionTimeFilter, lastUpdatedFilter); store.remove(filter); } public ProcessStatusStore getStore() { return store; } /** * Removes the execution status for the given id, and returns its value, if found, or null, if * not found * * @param executionId * */ public ExecutionStatus remove(String executionId) { return store.remove(executionId); } @Override public int getPriority() { // we want status tracking to be the last bit in the status tracking chain, // to make sure that when a status changes, all other listener has done its job return ExtensionPriority.LOWEST; } }