org.deegree.services.wps.WPService.java Source code

Java tutorial

Introduction

Here is the source code for org.deegree.services.wps.WPService.java

Source

//$HeadURL$
/*----------------------------------------------------------------------------
 This file is part of deegree, http://deegree.org/
 Copyright (C) 2001-2009 by:
 Department of Geography, University of Bonn
 and
 lat/lon GmbH
    
 This library is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free
 Software Foundation; either version 2.1 of the License, or (at your option)
 any later version.
 This library is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 details.
 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, write to the Free Software Foundation, Inc.,
 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 Contact information:
    
 lat/lon GmbH
 Aennchenstr. 19, 53177 Bonn
 Germany
 http://lat-lon.de/
    
 Department of Geography, University of Bonn
 Prof. Dr. Klaus Greve
 Postfach 1147, 53001 Bonn
 Germany
 http://www.geographie.uni-bonn.de/deegree/
    
 e-mail: info@deegree.org
 ----------------------------------------------------------------------------*/

package org.deegree.services.wps;

import static org.deegree.commons.ows.exception.OWSException.NO_APPLICABLE_CODE;
import static org.deegree.commons.ows.exception.OWSException.OPERATION_NOT_SUPPORTED;
import static org.deegree.protocol.wps.WPSConstants.VERSION_100;
import static org.deegree.services.controller.OGCFrontController.getHttpGetURL;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FileUtils;
import org.deegree.commons.ows.exception.OWSException;
import org.deegree.commons.tom.ows.CodeType;
import org.deegree.commons.tom.ows.Version;
import org.deegree.commons.utils.kvp.KVPUtils;
import org.deegree.commons.utils.kvp.MissingParameterException;
import org.deegree.cs.exceptions.UnknownCRSException;
import org.deegree.process.jaxb.java.ProcessDefinition;
import org.deegree.protocol.ows.getcapabilities.GetCapabilities;
import org.deegree.protocol.ows.getcapabilities.GetCapabilitiesKVPParser;
import org.deegree.protocol.wps.WPSConstants.WPSRequestType;
import org.deegree.protocol.wps.capabilities.GetCapabilitiesXMLAdapter;
import org.deegree.protocol.wps.describeprocess.DescribeProcessRequest;
import org.deegree.protocol.wps.describeprocess.DescribeProcessRequestKVPAdapter;
import org.deegree.protocol.wps.describeprocess.DescribeProcessRequestXMLAdapter;
import org.deegree.services.OWS;
import org.deegree.services.OWSProvider;
import org.deegree.services.controller.AbstractOWS;
import org.deegree.services.controller.ImplementationMetadata;
import org.deegree.services.controller.OGCFrontController;
import org.deegree.services.controller.exception.serializer.XMLExceptionSerializer;
import org.deegree.services.controller.utils.HttpResponseBuffer;
import org.deegree.services.jaxb.controller.DeegreeServiceControllerType;
import org.deegree.services.jaxb.metadata.DeegreeServicesMetadataType;
import org.deegree.services.jaxb.wps.DeegreeWPS;
import org.deegree.services.jaxb.wps.DefaultExecutionManager;
import org.deegree.services.ows.OWS110ExceptionReportSerializer;
import org.deegree.services.wps.capabilities.CapabilitiesXMLWriter;
import org.deegree.services.wps.describeprocess.DescribeProcessResponseXMLAdapter;
import org.deegree.services.wps.execute.ExecuteRequest;
import org.deegree.services.wps.execute.ExecuteRequestKVPAdapter;
import org.deegree.services.wps.execute.ExecuteRequestXMLAdapter;
import org.deegree.services.wps.execute.RawDataOutput;
import org.deegree.services.wps.execute.ResponseDocument;
import org.deegree.services.wps.execute.ResponseForm;
import org.deegree.services.wps.storage.OutputStorage;
import org.deegree.services.wps.storage.ResponseDocumentStorage;
import org.deegree.services.wps.storage.StorageManager;
import org.deegree.services.wps.wsdl.WSDL;
import org.deegree.workspace.ResourceInitException;
import org.deegree.workspace.ResourceMetadata;
import org.deegree.workspace.Workspace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of the <a href="http://www.opengeospatial.org/standards/wps">OpenGIS Web Processing Service</a> server
 * protocol.
 * <p>
 * Supported WPS protocol versions:
 * <ul>
 * <li>1.0.0</li>
 * </ul>
 * </p>
 * 
 * @see OGCFrontController
 * @see ProcessManager
 * @see ExecutionManager
 * 
 * @author <a href="mailto:padberg@uni-bonn.de">Alexander Padberg</a>
 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
 * @author last edited by: $Author$
 * 
 * @version $Revision$, $Date$
 */
public class WPService extends AbstractOWS {

    private static final Logger LOG = LoggerFactory.getLogger(WPService.class);

    private static final CodeType ALL_PROCESSES_IDENTIFIER = new CodeType("ALL");

    private ProcessManager processManager;

    private StorageManager storageManager;

    private ExecutionManager executeHandler;

    private DeegreeServicesMetadataType mainMetadataConf;

    public WPService(ResourceMetadata<OWS> metadata, Workspace workspace, Object jaxbConfig) {
        super(metadata, workspace, jaxbConfig);
    }

    @Override
    public void init(DeegreeServicesMetadataType serviceMetadata, DeegreeServiceControllerType mainConf,
            Object controllerConf) {
        LOG.info("Initializing WPS.");
        DeegreeWPS sc = (DeegreeWPS) controllerConf;

        String storage = "../var/wps";
        int trackedExecutions = 100;
        int inputDiskSwitchLimit = 1024 * 1024;
        if (sc.getAbstractExecutionManager() != null) {
            LOG.info("Explicit ExecutionManager config.");
            DefaultExecutionManager execManagerConfig = (DefaultExecutionManager) sc.getAbstractExecutionManager()
                    .getValue();
            if (execManagerConfig.getStorageDir() != null && !execManagerConfig.getStorageDir().isEmpty()) {
                storage = execManagerConfig.getStorageDir();
            }
            if (execManagerConfig.getTrackedExecutions() != null) {
                trackedExecutions = execManagerConfig.getTrackedExecutions().intValue();
            }
            if (execManagerConfig.getInputDiskSwitchLimit() != null) {
                inputDiskSwitchLimit = execManagerConfig.getInputDiskSwitchLimit().intValue();
            }
        }

        File storageDir = null;
        try {
            File base = metadata.getLocation().resolveToFile(".");
            storageDir = new File(storage);
            if (!storageDir.isAbsolute()) {
                storageDir = new File(base, storage).getCanonicalFile();
            }
            FileUtils.forceMkdir(storageDir);
        } catch (Throwable t) {
            String msg = "Storage directory error: " + t.getMessage();
            throw new ResourceInitException(msg);
        }
        storageManager = new StorageManager(storageDir, inputDiskSwitchLimit);

        this.processManager = workspace.getResourceManager(ProcessManager.class);

        validateAndSetOfferedVersions(sc.getSupportedVersions().getVersion());

        executeHandler = new ExecutionManager(this, storageManager, trackedExecutions);
        mainMetadataConf = serviceMetadata;
    }

    @Override
    public void destroy() {
        // rest should be done by workspace
    }

    @Override
    public void doKVP(Map<String, String> kvpParamsUC, HttpServletRequest request, HttpResponseBuffer response,
            List<FileItem> multiParts) throws ServletException, IOException {

        LOG.trace("doKVP invoked, version: " + kvpParamsUC.get("VERSION"));

        try {
            String requestName = KVPUtils.getRequired(kvpParamsUC, "REQUEST");
            WPSRequestType requestType = getRequestTypeByName(requestName);

            // check if requested version is supported and offered (except for GetCapabilities)
            if (requestType != WPSRequestType.GetCapabilities) {
                checkVersion(getVersion(KVPUtils.getRequired(kvpParamsUC, "VERSION")));
            }

            switch (requestType) {
            case GetCapabilities:
                GetCapabilities getCapabilitiesRequest = GetCapabilitiesKVPParser.parse(kvpParamsUC);
                doGetCapabilities(getCapabilitiesRequest, response);
                break;
            case DescribeProcess:
                DescribeProcessRequest describeProcessRequest = DescribeProcessRequestKVPAdapter
                        .parse100(kvpParamsUC);
                doDescribeProcess(describeProcessRequest, response);
                break;
            case Execute:
                ExecuteRequest executeRequest = ExecuteRequestKVPAdapter.parse100(kvpParamsUC,
                        processManager.getProcesses());
                doExecute(executeRequest, response);
                break;
            case GetOutput:
                doGetOutput(kvpParamsUC.get("IDENTIFIER"), response);
                break;
            case GetResponseDocument:
                doGetResponseDocument(kvpParamsUC.get("IDENTIFIER"), response);
                break;
            }
        } catch (MissingParameterException e) {
            sendServiceException(new OWSException(e.getMessage(), OWSException.MISSING_PARAMETER_VALUE), response);
        } catch (OWSException e) {
            sendServiceException(e, response);
        } catch (XMLStreamException e) {
            LOG.debug(e.getMessage());
        } catch (UnknownCRSException e) {
            LOG.debug(e.getMessage());
        }
    }

    @Override
    public void doXML(XMLStreamReader xmlStream, HttpServletRequest request, HttpResponseBuffer response,
            List<FileItem> multiParts) throws ServletException, IOException {

        LOG.trace("doXML invoked");

        try {
            WPSRequestType requestType = getRequestTypeByName(xmlStream.getLocalName());

            // check if requested version is supported and offered (except for GetCapabilities)
            Version requestVersion = getVersion(xmlStream.getAttributeValue(null, "version"));
            if (requestType != WPSRequestType.GetCapabilities) {
                checkVersion(requestVersion);
            }

            switch (requestType) {
            case GetCapabilities:
                GetCapabilitiesXMLAdapter getCapabilitiesAdapter = new GetCapabilitiesXMLAdapter();
                getCapabilitiesAdapter.load(xmlStream);
                GetCapabilities getCapabilitiesRequest = getCapabilitiesAdapter.parse100();
                doGetCapabilities(getCapabilitiesRequest, response);
                break;
            case DescribeProcess:
                DescribeProcessRequestXMLAdapter describeProcessAdapter = new DescribeProcessRequestXMLAdapter();
                describeProcessAdapter.load(xmlStream);
                DescribeProcessRequest describeProcessRequest = describeProcessAdapter.parse100();
                doDescribeProcess(describeProcessRequest, response);
                break;
            case Execute:
                // TODO switch to StaX-based parsing
                ExecuteRequestXMLAdapter executeAdapter = new ExecuteRequestXMLAdapter(
                        processManager.getProcesses(), storageManager);
                executeAdapter.load(xmlStream);
                ExecuteRequest executeRequest = executeAdapter.parse100();
                doExecute(executeRequest, response);
                break;
            case GetOutput:
            case GetResponseDocument:
                String msg = "Request type '" + requestType.name() + "' is only support as KVP request.";
                throw new OWSException(msg, OWSException.OPERATION_NOT_SUPPORTED);
            }
        } catch (OWSException e) {
            sendServiceException(e, response);
        } catch (XMLStreamException e) {
            LOG.debug(e.getMessage());
        } catch (UnknownCRSException e) {
            LOG.debug(e.getMessage());
        }
    }

    @Override
    public void doSOAP(SOAPEnvelope soapDoc, HttpServletRequest request, HttpResponseBuffer response,
            List<FileItem> multiParts, SOAPFactory factory) throws ServletException, IOException {

        LOG.trace("doSOAP invoked");
        OMElement requestElement = soapDoc.getBody().getFirstElement();
        try {
            WPSRequestType requestType = getRequestTypeByName(requestElement.getLocalName());

            // check if requested version is supported and offered (except for GetCapabilities)
            Version requestVersion = getVersion(requestElement.getAttributeValue(new QName("version")));
            if (requestType != WPSRequestType.GetCapabilities) {
                checkVersion(requestVersion);
            }

            beginSOAPResponse(response);

            switch (requestType) {
            case GetCapabilities:
                GetCapabilitiesXMLAdapter getCapabilitiesAdapter = new GetCapabilitiesXMLAdapter();
                getCapabilitiesAdapter.setRootElement(requestElement);
                // getCapabilitiesAdapter.setSystemId( soapDoc.getSystemId() );
                GetCapabilities getCapabilitiesRequest = getCapabilitiesAdapter.parse100();
                doGetCapabilities(getCapabilitiesRequest, response);
                break;
            case DescribeProcess:
                DescribeProcessRequestXMLAdapter describeProcessAdapter = new DescribeProcessRequestXMLAdapter();
                describeProcessAdapter.setRootElement(requestElement);
                // describeProcessAdapter.setSystemId( soapDoc.getSystemId() );
                DescribeProcessRequest describeProcessRequest = describeProcessAdapter.parse100();
                doDescribeProcess(describeProcessRequest, response);
                break;
            case Execute:
                // TODO switch to StaX-based parsing
                ExecuteRequestXMLAdapter executeAdapter = new ExecuteRequestXMLAdapter(
                        processManager.getProcesses(), storageManager);
                executeAdapter.setRootElement(requestElement);
                // executeAdapter.setSystemId( soapDoc.getSystemId() );
                ExecuteRequest executeRequest = executeAdapter.parse100();
                ResponseForm responseForm = executeRequest.getResponseForm();
                if (responseForm != null && responseForm instanceof RawDataOutput) {
                    String msg = "Response type RawDataOutput is not supported for SOAP requests.";
                    throw new OWSException(msg, OWSException.OPTION_NOT_SUPPORTED);
                }

                doExecute(executeRequest, response);
                break;
            case GetOutput:
            case GetResponseDocument:
                String msg = "Request type '" + requestType.name() + "' is only support as KVP request.";
                throw new OWSException(msg, OWSException.OPERATION_NOT_SUPPORTED);
            }

            endSOAPResponse(response);

        } catch (OWSException e) {
            sendSOAPException(soapDoc.getHeader(), factory, response, e, null, null, null, request.getServerName(),
                    request.getCharacterEncoding());
        } catch (XMLStreamException e) {
            LOG.debug(e.getMessage(), e);
        } catch (UnknownCRSException e) {
            LOG.debug(e.getMessage(), e);
        }
    }

    /**
     * Returns the underlying {@link ProcessManager} instance.
     * 
     * @return the underlying {@link ProcessManager}, never <code>null</code>
     */
    public ProcessManager getProcessManager() {
        return processManager;
    }

    /**
     * Returns the associated {@link ExecutionManager} instance.
     * 
     * @return the associated {@link ExecutionManager}, never <code>null</code>
     */
    public ExecutionManager getExecutionManager() {
        return executeHandler;
    }

    @Override
    public XMLExceptionSerializer getExceptionSerializer(Version requestVersion) {
        return new OWS110ExceptionReportSerializer(VERSION_100);
    }

    private WPSRequestType getRequestTypeByName(String requestName) throws OWSException {
        WPSRequestType requestType = null;
        try {
            requestType = (WPSRequestType) ((ImplementationMetadata) ((OWSProvider) metadata.getProvider())
                    .getImplementationMetadata()).getRequestTypeByName(requestName);
        } catch (IllegalArgumentException e) {
            throw new OWSException(e.getMessage(), OPERATION_NOT_SUPPORTED);
        }
        return requestType;
    }

    private Version getVersion(String versionString) throws OWSException {

        Version version = null;
        if (versionString != null) {
            try {
                version = Version.parseVersion(versionString);
            } catch (IllegalArgumentException e) {
                throw new OWSException(
                        "Specified request version '" + versionString + "' is not a valid OGC version string.",
                        OWSException.INVALID_PARAMETER_VALUE);
            }
        }
        return version;
    }

    private void doGetCapabilities(GetCapabilities request, HttpResponseBuffer response)
            throws OWSException, XMLStreamException, IOException {

        LOG.trace("doGetCapabilities invoked, request: " + request);

        // generic check if requested version is supported (currently this is only 1.0.0)
        negotiateVersion(request);

        response.setContentType("text/xml; charset=UTF-8");
        XMLStreamWriter xmlWriter = response.getXMLWriter();
        WSDL serviceWSDL = new WSDL("services" + File.separatorChar + "wps.wsdl");
        CapabilitiesXMLWriter.export100(xmlWriter, processManager.getProcesses(), mainMetadataConf, serviceWSDL);

        LOG.trace("doGetCapabilities finished");
    }

    private void doDescribeProcess(DescribeProcessRequest request, HttpResponseBuffer response)
            throws OWSException {

        LOG.trace("doDescribeProcess invoked, request: " + request);

        // check that all requested processes exist (and resolve special value 'ALL')
        List<WPSProcess> processes = new ArrayList<WPSProcess>();
        for (CodeType identifier : request.getIdentifiers()) {
            LOG.debug("Looking up process '" + identifier + "'");
            if (ALL_PROCESSES_IDENTIFIER.equals(identifier)) {
                processes.addAll(processManager.getProcesses().values());
                break;
            }
            WPSProcess process = processManager.getProcess(identifier);
            if (process != null) {
                processes.add(process);
            } else {
                throw new OWSException("InvalidParameterValue: Identifier\nNo process with id " + identifier
                        + " is registered in the WPS.", OWSException.INVALID_PARAMETER_VALUE);
            }
        }

        try {
            response.setContentType("text/xml; charset=UTF-8");
            XMLStreamWriter xmlWriter = response.getXMLWriter();

            Map<ProcessDefinition, String> processDefToWSDLUrl = new HashMap<ProcessDefinition, String>();
            for (WPSProcess process : processes) {
                ProcessDefinition processDef = process.getDescription();
                CodeType processId = new CodeType(processDef.getIdentifier().getValue(),
                        processDef.getIdentifier().getCodeSpace());
                // TODO WSDL
                // if ( processIdToWSDL.containsKey( processId ) ) {
                // String wsdlURL = OGCFrontController.getHttpGetURL()
                // + "service=WPS&version=1.0.0&request=GetWPSWSDL&identifier=" + processId.getCode();
                // processDefToWSDLUrl.put( processDef, wsdlURL );
                // }
            }

            DescribeProcessResponseXMLAdapter.export100(xmlWriter, processes, processDefToWSDLUrl);
            xmlWriter.flush();
        } catch (XMLStreamException e) {
            e.printStackTrace();
            LOG.error("Internal error: " + e.getMessage());
            throw new OWSException("Error occured while creating response for DescribeProcess operation",
                    NO_APPLICABLE_CODE);
        } catch (IOException e) {
            throw new OWSException("Error occured while creating response for DescribeProcess operation",
                    NO_APPLICABLE_CODE);
        } catch (Exception e) {
            e.printStackTrace();
            LOG.error("Internal error: " + e.getMessage());
        }

        LOG.trace("doDescribeProcess finished");
    }

    private void doExecute(ExecuteRequest request, HttpResponseBuffer response) throws OWSException {

        LOG.trace("doExecute invoked, request: " + request.toString());
        long start = System.currentTimeMillis();

        CodeType processId = request.getProcessId();
        WPSProcess process = processManager.getProcess(processId);
        if (process == null) {
            String msg = "Internal error. Process '" + processId + "' not found.";
            throw new OWSException(msg, OWSException.INVALID_PARAMETER_VALUE);
        }

        try {
            if (request.getResponseForm() == null || request.getResponseForm() instanceof ResponseDocument) {
                executeHandler.handleResponseDocumentOutput(request, response, process);
            } else {
                executeHandler.handleRawDataOutput(request, response, process);
            }
        } catch (OWSException e) {
            throw e;
        } catch (Exception e) {
            LOG.debug(e.getMessage(), e);
            throw new OWSException(e.getMessage(), NO_APPLICABLE_CODE);
        }

        long elapsed = System.currentTimeMillis() - start;
        LOG.debug("doExecute took " + elapsed + " milliseconds");

        LOG.trace("doExecute finished");
    }

    private void doGetOutput(String storedOutputId, HttpResponseBuffer response) {

        LOG.trace("doGetOutput invoked, requested stored output: " + storedOutputId);
        OutputStorage resource = storageManager.lookupOutputStorage(storedOutputId);

        if (resource == null) {
            try {
                response.sendError(404, "No stored output with id '" + storedOutputId + "' found.");
            } catch (IOException e) {
                LOG.debug("Error sending exception report to client.", e);
            }
        } else {
            resource.sendResource(response);
        }

        LOG.trace("doGetOutput finished");
    }

    private void doGetResponseDocument(String responseId, HttpResponseBuffer response) {

        LOG.trace("doGetResponseDocument invoked, requested stored response document: " + responseId);
        ResponseDocumentStorage resource = storageManager.lookupResponseDocumentStorage(responseId,
                getHttpGetURL());
        executeHandler.sendResponseDocument(response, resource);

        LOG.trace("doGetResponseDocument finished");
    }

    private void sendServiceException(OWSException ex, HttpResponseBuffer response) throws ServletException {
        sendException(null, new OWS110ExceptionReportSerializer(VERSION_100), ex, response);
    }

}