/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.wps.ws;
import org.apache.sis.util.logging.Logging;
import org.constellation.ServiceDef;
import org.constellation.wps.utils.WPSUtils;
import org.constellation.ws.CstlServiceException;
import org.geotoolkit.ows.xml.v110.ExceptionReport;
import org.geotoolkit.process.ProcessEvent;
import org.geotoolkit.process.ProcessListener;
import org.geotoolkit.util.Exceptions;
import org.geotoolkit.util.StringUtilities;
import org.geotoolkit.wps.converters.WPSConvertersUtils;
import org.geotoolkit.wps.xml.v100.Execute;
import org.geotoolkit.wps.xml.v100.ExecuteResponse;
import org.geotoolkit.wps.xml.v100.ProcessFailedType;
import org.geotoolkit.wps.xml.v100.ProcessStartedType;
import org.geotoolkit.wps.xml.v100.StatusType;
import org.opengis.parameter.GeneralParameterDescriptor;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* ProcessListener implementation for WPS asynchronous process execution.
*
* @author Quentin Boileau (Geomatys).
*/
public class WPSProcessListener implements ProcessListener{
private static final Logger LOGGER = Logging.getLogger("org.constellation.wps.ws");
private static final int TIMEOUT = 200; //processing time step
private final Execute request;
private final ExecuteResponse responseDoc;
private final String fileName;
private final ServiceDef def;
final Map<String, Object> parameters;
private final String folderPath;
private long nextTimestamp;
private final boolean useStatus;
/**
*
* @param request execute request
* @param responseDoc ExecuteResponse base
* @param fileName name of the file to update
* @param def service def (use when exception occurs)
*/
public WPSProcessListener(final Execute request, final ExecuteResponse responseDoc, final String fileName, final ServiceDef def,
final Map<String, Object> parameters) {
this.request = request;
this.responseDoc = responseDoc;
this.fileName = fileName;
this.def = def;
this.parameters = parameters;
this.folderPath = (String) parameters.get(WPSConvertersUtils.OUT_STORAGE_DIR);
this.nextTimestamp = System.currentTimeMillis() + TIMEOUT;
this.useStatus = this.request.getResponseForm().getResponseDocument().isStatus();
}
@Override
public void started(final ProcessEvent event) {
LOGGER.log(Level.INFO, "Process {0} is started.", WPSUtils.buildProcessIdentifier(event.getSource().getDescriptor()));
final StatusType status = new StatusType();
final ProcessStartedType started = new ProcessStartedType();
status.setCreationTime(WPSUtils.getCurrentXMLGregorianCalendar());
started.setValue("Process " + request.getIdentifier().getValue() + " is started");
started.setPercentCompleted(0);
status.setProcessStarted(started);
if (useStatus) {
responseDoc.setStatus(status);
}
WPSUtils.storeResponse(responseDoc, folderPath, fileName);
}
@Override
public void progressing(final ProcessEvent event) {
if (useStatus) {
final long currentTimestamp = System.currentTimeMillis();
if (currentTimestamp >= (nextTimestamp)){
//LOGGER.log(Level.INFO, "Process {0} is progressing : {1}.", new Object[]{WPSUtils.buildProcessIdentifier(event.getSource().getDescriptor()), event.getProgress()});
nextTimestamp += TIMEOUT;
try {
final List<GeneralParameterDescriptor> processOutputDesc = event.getSource().getDescriptor().getOutputDescriptor().descriptors();
final ExecuteResponse.ProcessOutputs outputs = new ExecuteResponse.ProcessOutputs();
WPSWorker.fillOutputsFromProcessResult(outputs, request.getResponseForm().getResponseDocument().getOutput(), processOutputDesc,
event.getOutput(), parameters, true);
final StatusType status = new StatusType();
status.setCreationTime(WPSUtils.getCurrentXMLGregorianCalendar());
final ProcessStartedType started = new ProcessStartedType();
started.setValue("Process " + request.getIdentifier().getValue() + " is pending");
started.setPercentCompleted((int) event.getProgress());
status.setProcessStarted(started);
responseDoc.setStatus(status);
responseDoc.setProcessOutputs(outputs);
WPSUtils.storeResponse(responseDoc, folderPath, fileName);
} catch (CstlServiceException ex) {
writeException(ex);
}
}
}
}
@Override
public void completed(final ProcessEvent event) {
LOGGER.log(Level.INFO, "Process {0} is finished.", WPSUtils.buildProcessIdentifier(event.getSource().getDescriptor()));
try {
final List<GeneralParameterDescriptor> processOutputDesc = event.getSource().getDescriptor().getOutputDescriptor().descriptors();
final ExecuteResponse.ProcessOutputs outputs = new ExecuteResponse.ProcessOutputs();
WPSWorker.fillOutputsFromProcessResult(outputs, request.getResponseForm().getResponseDocument().getOutput(), processOutputDesc,
event.getOutput(), parameters, false);
final StatusType status = new StatusType();
status.setCreationTime(WPSUtils.getCurrentXMLGregorianCalendar());
status.setProcessSucceeded("Process completed.");
responseDoc.setStatus(status);
responseDoc.setProcessOutputs(outputs);
WPSUtils.storeResponse(responseDoc, folderPath, fileName);
} catch (CstlServiceException ex) {
writeException(ex);
}
}
@Override
public void failed(final ProcessEvent event) {
LOGGER.log(Level.WARNING, "Process "+WPSUtils.buildProcessIdentifier(event.getSource().getDescriptor())+" has failed.", event.getException());
final StatusType status = new StatusType();
status.setCreationTime(WPSUtils.getCurrentXMLGregorianCalendar());
final ProcessFailedType processFT = new ProcessFailedType();
if (event.getException() != null) {
processFT.setExceptionReport(new ExceptionReport(Exceptions.formatStackTrace(event.getException()), null, null, this.def.exceptionVersion.toString()));
} else {
processFT.setExceptionReport(new ExceptionReport("Process failed for some unknown reason.", null, null, this.def.exceptionVersion.toString()));
}
status.setProcessFailed(processFT);
responseDoc.setStatus(status);
WPSUtils.storeResponse(responseDoc, folderPath, fileName);
}
@Override
public void paused(final ProcessEvent event) {
if (useStatus) {
LOGGER.log(Level.INFO, "Process {0} is paused.", WPSUtils.buildProcessIdentifier(event.getSource().getDescriptor()));
final StatusType status = new StatusType();
final ProcessStartedType paused = new ProcessStartedType();
status.setCreationTime(WPSUtils.getCurrentXMLGregorianCalendar());
paused.setValue("Process " + request.getIdentifier().getValue() + " is paused");
paused.setPercentCompleted((int) event.getProgress());
status.setProcessPaused(paused);
responseDoc.setStatus(status);
WPSUtils.storeResponse(responseDoc, folderPath, fileName);
}
}
@Override
public void resumed(final ProcessEvent event) {
if (useStatus) {
LOGGER.log(Level.INFO, "Process {0} is resumed.", WPSUtils.buildProcessIdentifier(event.getSource().getDescriptor()));
final StatusType status = new StatusType();
final ProcessStartedType resumed = new ProcessStartedType();
status.setCreationTime(WPSUtils.getCurrentXMLGregorianCalendar());
resumed.setValue("Process " + request.getIdentifier().getValue() + " is resumed");
resumed.setPercentCompleted((int) event.getProgress());
status.setProcessPaused(resumed);
responseDoc.setStatus(status);
WPSUtils.storeResponse(responseDoc, folderPath, fileName);
}
}
/**
* Write the occurred exception in the response file.
*
* @param ex exception
*/
private void writeException(final CstlServiceException ex){
final String codeRepresentation;
if (ex.getExceptionCode() instanceof org.constellation.ws.ExceptionCode) {
codeRepresentation = StringUtilities.transformCodeName(ex.getExceptionCode().name());
} else {
codeRepresentation = ex.getExceptionCode().name();
}
final ExceptionReport report = new ExceptionReport(ex.getMessage(), codeRepresentation, ex.getLocator(),
def.exceptionVersion.toString());
WPSUtils.storeResponse(report, folderPath, fileName);
}
}