Package org.geoserver.wps.executor

Source Code of org.geoserver.wps.executor.ExecuteResponseBuilder

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.executor;

import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.datatype.XMLGregorianCalendar;

import net.opengis.ows11.BoundingBoxType;
import net.opengis.ows11.CodeType;
import net.opengis.ows11.ExceptionReportType;
import net.opengis.ows11.Ows11Factory;
import net.opengis.wps10.ComplexDataType;
import net.opengis.wps10.DataType;
import net.opengis.wps10.DocumentOutputDefinitionType;
import net.opengis.wps10.ExecuteResponseType;
import net.opengis.wps10.ExecuteType;
import net.opengis.wps10.InputType;
import net.opengis.wps10.LiteralDataType;
import net.opengis.wps10.OutputDataType;
import net.opengis.wps10.OutputDefinitionType;
import net.opengis.wps10.OutputDefinitionsType;
import net.opengis.wps10.OutputReferenceType;
import net.opengis.wps10.ProcessBriefType;
import net.opengis.wps10.ProcessFailedType;
import net.opengis.wps10.ProcessOutputsType1;
import net.opengis.wps10.ProcessStartedType;
import net.opengis.wps10.ResponseDocumentType;
import net.opengis.wps10.Wps10Factory;

import org.apache.commons.io.IOUtils;
import org.eclipse.emf.common.util.EList;
import org.geoserver.ows.Ows11Util;
import org.geoserver.ows.URLMangler.URLType;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.ServiceException;
import org.geoserver.wps.BinaryEncoderDelegate;
import org.geoserver.wps.CDataEncoderDelegate;
import org.geoserver.wps.RawDataEncoderDelegate;
import org.geoserver.wps.WPSException;
import org.geoserver.wps.XMLEncoderDelegate;
import org.geoserver.wps.executor.ExecutionStatus.ProcessState;
import org.geoserver.wps.ppio.BinaryPPIO;
import org.geoserver.wps.ppio.BoundingBoxPPIO;
import org.geoserver.wps.ppio.CDataPPIO;
import org.geoserver.wps.ppio.ComplexPPIO;
import org.geoserver.wps.ppio.LiteralPPIO;
import org.geoserver.wps.ppio.ProcessParameterIO;
import org.geoserver.wps.ppio.RawDataPPIO;
import org.geoserver.wps.ppio.XMLPPIO;
import org.geoserver.wps.process.GeoServerProcessors;
import org.geoserver.wps.process.RawData;
import org.geoserver.wps.resource.GridCoverageResource;
import org.geoserver.wps.resource.WPSResourceManager;
import org.geotools.data.Parameter;
import org.geotools.process.ProcessFactory;
import org.geotools.util.Converters;
import org.geotools.util.logging.Logging;
import org.geotools.xml.EMFUtils;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.feature.type.Name;
import org.springframework.context.ApplicationContext;

public class ExecuteResponseBuilder {
   
    static final Logger LOGGER = Logging.getLogger(ExecuteResponseBuilder.class);

    ExecuteType request;

    ExecutionStatus status;

    Map<String, Object> outputs;

    Date created;

    boolean verboseExceptions;

    Throwable exception;

    ApplicationContext context;

    String executionId;

    WPSResourceManager resourceManager;

    public ExecuteResponseBuilder(ExecuteType request, ApplicationContext context, Date created) {
        this.request = request;
        this.created = created;
        this.context = context;
        this.resourceManager = context.getBean(WPSResourceManager.class);
    }

    public void setStatus(ExecutionStatus status) {
        this.status = status;
    }

    public void setOutputs(Map<String, Object> outputs) {
        this.outputs = outputs;
        // mark the output coverages as resources to be cleaned after
        // ... hmmm.. wondering if we could make this more pluggable...
        for (Object result : outputs.values()) {
            if(result instanceof GridCoverage) {
                resourceManager.addResource(new GridCoverageResource(((GridCoverage) result)));
            }
        }
    }

    public ExecuteResponseType build() {
        ExecuteRequest helper = new ExecuteRequest(request);

        // build the response
        Wps10Factory f = Wps10Factory.eINSTANCE;
        ExecuteResponseType response = f.createExecuteResponseType();
        response.setLang("en");
        if (request.getBaseUrl() != null) {
            response.setServiceInstance(ResponseUtils.appendQueryString(
                    ResponseUtils.buildURL(request.getBaseUrl(), "ows", null, URLType.SERVICE), ""));
        }

        // process
        Name processName = helper.getProcessName();
        ProcessFactory pf = GeoServerProcessors.createProcessFactory(processName);
        final ProcessBriefType process = f.createProcessBriefType();
        response.setProcess(process);
        // damn blasted EMF changes the state of request if we set its identifier on
        // another object! (I guess, following some strict ownership rule...)
        process.setIdentifier((CodeType) EMFUtils.clone(request.getIdentifier(),
                Ows11Factory.eINSTANCE, true));
        process.setProcessVersion(pf.getVersion(processName));
        process.setTitle(Ows11Util.languageString(pf.getTitle(processName)));
        process.setAbstract(Ows11Util.languageString(pf.getDescription(processName)));

        // status
        response.setStatus(f.createStatusType());
        XMLGregorianCalendar gc = Converters.convert(created, XMLGregorianCalendar.class);
        response.getStatus().setCreationTime(gc);
        if (status == null) {
            if (exception != null) {
                setResponseFailed(response, getException(ProcessState.COMPLETED));
            } else if (outputs == null) {
                response.getStatus().setProcessAccepted("Process accepted.");
            } else {
                response.getStatus().setProcessSucceeded("Process succeeded.");
            }
        } else {
            if (status.getPhase() == ProcessState.QUEUED) {
                response.getStatus().setProcessAccepted("Process accepted.");
            } else if (status.getPhase() == ProcessState.RUNNING) {
                ProcessStartedType startedType = f.createProcessStartedType();
                int progressPercent = Math.round(status.getProgress());
                if(progressPercent < 0) {
                    LOGGER.warning("Progress reported is below zero, fixing it to 0: " + progressPercent);
                    progressPercent = 0;
                } else if(progressPercent > 100) {
                    LOGGER.warning("Progress reported is above 100, fixing it to 100: " + progressPercent);
                    progressPercent = 100;
                }
                startedType.setPercentCompleted(new BigInteger(String.valueOf(progressPercent)));
                startedType.setValue(status.getTask());
                response.getStatus().setProcessStarted(startedType);
            } else if (status.getPhase() == ProcessState.COMPLETED) {
                response.getStatus().setProcessSucceeded("Process succeeded.");
            } else {
                ServiceException reportException = getException(status.getPhase());
                setResponseFailed(response, reportException);
            }
        }

        // status location, if asynch
        if (helper.isAsynchronous() && request.getBaseUrl() != null && executionId != null) {
            Map<String, String> kvp = new LinkedHashMap<String, String>();
            kvp.put("service", "WPS");
            kvp.put("version", "1.0.0");
            kvp.put("request", "GetExecutionStatus");
            kvp.put("executionId", executionId);
            response.setStatusLocation(ResponseUtils.buildURL(request.getBaseUrl(), "ows", kvp, URLType.SERVICE));

        }

        // lineage, should be included only if requested, the response should contain it
        // even if the process is not done computing. From the spec:
        // * If lineage is "true" the server shall include in the execute response a complete copy
        // of
        // the DataInputs and OutputDefinition elements _as received in the execute request_.
        // *If lineage is "false" then/ these elements shall be omitted from the response
        if (helper.isLineageRequested()) {
            // inputs
            if (request.getDataInputs() != null && request.getDataInputs().getInput().size() > 0) {
                response.setDataInputs(f.createDataInputsType1());
                for (Iterator i = request.getDataInputs().getInput().iterator(); i.hasNext();) {
                    InputType input = (InputType) i.next();
                    response.getDataInputs().getInput().add(EMFUtils.clone(input, f, true));
                }
            }

            // output definitions, if any was requested explicitly
            List<DocumentOutputDefinitionType> outputList = helper.getRequestedOutputs();
            if (outputList != null) {
                OutputDefinitionsType outputs = f.createOutputDefinitionsType();
                response.setOutputDefinitions(outputs);
                for (DocumentOutputDefinitionType output : outputList) {
                    outputs.getOutput().add(EMFUtils.clone(output, f, true));
                }
            }
        }

        // process outputs
        if (exception == null && outputs != null) {
            ProcessOutputsType1 processOutputs = f.createProcessOutputsType1();
            response.setProcessOutputs(processOutputs);

            Map<String, Parameter<?>> resultInfo = pf.getResultInfo(processName, null);

            if (request.getResponseForm() != null
                    && request.getResponseForm().getResponseDocument() != null
                    && request.getResponseForm().getResponseDocument().getOutput() != null
                    && request.getResponseForm().getResponseDocument().getOutput().size() > 0) {
                // we have a selection of outputs, possibly with indication of mime type
                // and reference encoding
                EList outputs = request.getResponseForm().getResponseDocument().getOutput();
                for (Object object : outputs) {
                    DocumentOutputDefinitionType odt = (DocumentOutputDefinitionType) object;
                    String key = odt.getIdentifier().getValue();
                    Parameter<?> outputParam = resultInfo.get(key);
                    if (outputParam == null) {
                        throw new WPSException("Unknown output " + key + " possible values are: "
                                + resultInfo.keySet());
                    }

                    String mimeType = odt.getMimeType();
                    OutputDataType output = encodeOutput(key, outputParam, mimeType, odt.isAsReference());
                    processOutputs.getOutput().add(output);
                }
            } else {
                // encode all as inline for the moment
                for (String key : outputs.keySet()) {
                    Parameter<?> outputParam = resultInfo.get(key);
                    OutputDataType output = encodeOutput(key, outputParam, null, false);
                    processOutputs.getOutput().add(output);
                }
            }
        }

        return response;
    }

    OutputDataType encodeOutput(String key, Parameter<?> outputParam, String mimeType,
            boolean reference) {
        Wps10Factory f = Wps10Factory.eINSTANCE;
        OutputDataType output = f.createOutputDataType();
        output.setIdentifier(Ows11Util.code(key));
        output.setTitle(Ows11Util.languageString(outputParam.description));

        final Object o = outputs.get(key);
        if (mimeType == null) {
            mimeType = getOutputMimeType(key);
        }
        ProcessParameterIO ppio = ProcessParameterIO.find(outputParam, context, mimeType);

        if (ppio == null) {
            throw new WPSException("Don't know how to encode output " + key + " in mime type "
                    + mimeType);
        }

        try {
            if (reference && ppio instanceof ComplexPPIO) {
                // encode as reference
                OutputReferenceType outputReference = f.createOutputReferenceType();
                output.setReference(outputReference);
               
                ComplexPPIO cppio = (ComplexPPIO) ppio;
                File file = resourceManager.getOutputFile(executionId, key + "." + cppio.getFileExtension());
               
                // write out the file
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(file);
                    cppio.encode(o, fos);
                } finally {
                    IOUtils.closeQuietly(fos);
                }
               
                // create the link
                Map<String, String> kvp = new LinkedHashMap<String, String>();
                kvp.put("service", "WPS");
                kvp.put("version", "1.0.0");
                kvp.put("request", "GetExecutionResult");
                kvp.put("executionId", executionId);
                kvp.put("outputId", file.getName());
                if(o instanceof RawData) {
                  RawData rawData = (RawData) o;
                  kvp.put("mimetype", rawData.getMimeType());
                } else {
                  kvp.put("mimetype", cppio.getMimeType());
                }
                outputReference.setHref(ResponseUtils.buildURL(request.getBaseUrl(), "ows", kvp, URLType.SERVICE));
                outputReference.setMimeType(cppio.getMimeType());
            } else {
                // encode as data
                DataType data = f.createDataType();
                output.setData(data);

                if (ppio instanceof LiteralPPIO) {
                    LiteralDataType literal = f.createLiteralDataType();
                    data.setLiteralData(literal);

                    literal.setValue(((LiteralPPIO) ppio).encode(o));
                } else if (ppio instanceof BoundingBoxPPIO) {
                    BoundingBoxType bbox = ((BoundingBoxPPIO) ppio).encode(o);
                    data.setBoundingBoxData(bbox);
                } else if (ppio instanceof ComplexPPIO) {
                    ComplexDataType complex = f.createComplexDataType();
                    data.setComplexData(complex);

                    ComplexPPIO cppio = (ComplexPPIO) ppio;
                    complex.setMimeType(cppio.getMimeType());

                    if (cppio instanceof RawDataPPIO) {
                        RawData rawData = (RawData) o;
                        complex.setMimeType(rawData.getMimeType());
                        complex.setEncoding("base64");
                        complex.getData().add(new RawDataEncoderDelegate(rawData));
                    } else if (cppio instanceof XMLPPIO) {
                        // encode directly
                        complex.getData().add(new XMLEncoderDelegate((XMLPPIO) cppio, o));
                    } else if (cppio instanceof CDataPPIO) {
                        complex.getData().add(new CDataEncoderDelegate((CDataPPIO) cppio, o));
                    } else if (cppio instanceof BinaryPPIO) {
                        complex.setEncoding("base64");
                        complex.getData().add(new BinaryEncoderDelegate((BinaryPPIO) cppio, o));
                    } else {
                        throw new WPSException("Don't know how to encode an output whose PPIO is "
                                + cppio);
                    }
                }
            }
        } catch (Exception e) {
            throw new WPSException("Failed to encode the " + key + " output", e);
        }
        return output;
    }

    void setResponseFailed(ExecuteResponseType response, ServiceException reportException) {
        Wps10Factory f = Wps10Factory.eINSTANCE;
        ProcessFailedType failedType = f.createProcessFailedType();
        ExceptionReportType report = Ows11Util.exceptionReport(reportException, verboseExceptions,
                "1.1.0");
        failedType.setExceptionReport(report);
        response.getStatus().setProcessFailed(failedType);
    }

    /**
     * Gets the mime type for the specified output
     *
     * @param key
     * @return
     */
    private String getOutputMimeType(String key) {
        // lookup for the OutputDefinitionType
        OutputDefinitionType odt = request.getResponseForm().getRawDataOutput();
        ResponseDocumentType responseDocument = request.getResponseForm().getResponseDocument();
        if (responseDocument != null && odt == null) {
            Iterator it = responseDocument.getOutput().iterator();
            while (it.hasNext()) {
                OutputDefinitionType curr = (OutputDefinitionType) it.next();
                if (curr.getIdentifier().getValue().equals(key)) {
                    odt = curr;
                    break;
                }
            }
        }

        // have we got anything?
        if (odt != null) {
            return odt.getMimeType();
        } else {
            return null;
        }
    }

    private ServiceException getException(ProcessState phase) {
        if (phase == ProcessState.CANCELLED) {
            return new WPSException("Process was cancelled by the administrator");
        } else {
            return new WPSException("Process failed during execution", exception);
        }
    }

    public void setException(Throwable exception) {
        this.exception = exception;
    }

    public void setExecutionId(String executionId) {
        this.executionId = executionId;
    }
}
TOP

Related Classes of org.geoserver.wps.executor.ExecuteResponseBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.