/* (c) 2014 - 2015 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.resource;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.geoserver.wcs.CoverageCleanerCallback;
import org.geoserver.wps.ProcessEvent;
import org.geoserver.wps.ProcessListenerAdapter;
import org.geoserver.wps.WPSException;
import org.opengis.coverage.grid.GridCoverage;
/**
* Listens to progress events and makes sure the input/output coverages are marked as resources to
* clean up (to release readers and thus file system locks and associated tiles in the tile cache)
*
* @author Andrea Aime - GeoSolutions
*/
public class CoverageResourceListener extends ProcessListenerAdapter {
/**
* A simple check to assess whether the
*
* @author Andrea Aime - GeoSolutions
*/
private static class ResourceStatus {
final Set<String> inputsChecked = new HashSet<>();
final Set<String> outputsChecked = new HashSet<>();
}
WPSResourceManager resourceManager;
CoverageCleanerCallback cleaner;
Map<String, ResourceStatus> resourceStates = new ConcurrentHashMap<>();
public CoverageResourceListener(WPSResourceManager resourceManager, CoverageCleanerCallback cleaner) {
this.resourceManager = resourceManager;
this.cleaner = cleaner;
}
@Override
public void progress(ProcessEvent event) throws WPSException {
checkInputOutput(event);
}
private void checkInputOutput(ProcessEvent event) {
Map<String, Object> inputs = event.getInputs();
Map<String, Object> outputs = event.getOutputs();
if (((inputs == null) || inputs.isEmpty()) &&
((outputs == null) || outputs.isEmpty())) {
return;
}
// check if we have the status
String executionId = event.getStatus().getExecutionId();
ResourceStatus status = resourceStates.get(executionId);
if (status == null) {
status = new ResourceStatus();
resourceStates.put(executionId, status);
}
// check if the available inputs have already been checked
Set<String> inputsChecked = status.inputsChecked;
if ((inputs != null) && (inputsChecked.size() < inputs.size())) {
for (Entry<String, Object> entry : inputs.entrySet()) {
Object input = entry.getValue();
if ((input != null) && inputsChecked.add(entry.getKey()) &&
(input instanceof GridCoverage)) {
resourceManager.addResource(new GridCoverageResource((GridCoverage) input));
}
}
}
// check if the available outputs have already been checked
Set<String> outputsChecked = status.outputsChecked;
if ((outputs != null) && (outputsChecked.size() < outputs.size())) {
for (Entry<String, Object> entry : outputs.entrySet()) {
Object output = entry.getValue();
if ((output != null) && outputsChecked.add(entry.getKey()) &&
(output instanceof GridCoverage)) {
resourceManager.addResource(new GridCoverageResource((GridCoverage) output));
}
}
}
}
@Override
public void succeeded(ProcessEvent event) throws WPSException {
cleanResourceStatus(event);
}
@Override
public void dismissed(ProcessEvent event) throws WPSException {
cleanResourceStatus(event);
}
@Override
public void failed(ProcessEvent event) {
cleanResourceStatus(event);
cleaner.clean();
}
private void cleanResourceStatus(ProcessEvent event) {
// just in case we have partially processed inputs and the process ended before
// even starting, check the inputs and outputs
checkInputOutput(event);
// and now remove the ResourceStatus from the map, we're done
resourceStates.remove(event.getStatus().getExecutionId());
}
}