/* (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.response; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Map; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import net.opengis.ows11.BoundingBoxType; import net.opengis.wps10.ComplexDataType; import net.opengis.wps10.ExecuteResponseType; import net.opengis.wps10.ExecuteType; import net.opengis.wps10.LiteralDataType; import net.opengis.wps10.OutputDataType; import org.geoserver.ows.Ows11Util; import org.geoserver.ows.Response; import org.geoserver.ows.XmlObjectEncodingResponse; import org.geoserver.platform.Operation; import org.geoserver.platform.ServiceException; import org.geoserver.wps.BinaryEncoderDelegate; import org.geoserver.wps.CDataEncoderDelegate; import org.geoserver.wps.Execute; import org.geoserver.wps.RawDataEncoderDelegate; import org.geoserver.wps.WPSException; import org.geoserver.wps.XMLEncoderDelegate; import org.geoserver.wps.ppio.ComplexPPIO; import org.geoserver.wps.ppio.ProcessParameterIO; import org.geoserver.wps.process.GeoServerProcessors; import org.geotools.data.Parameter; import org.geotools.ows.v1_1.OWS; import org.geotools.ows.v1_1.OWSConfiguration; import org.geotools.process.ProcessFactory; import org.geotools.xml.Encoder; import org.opengis.feature.type.Name; import org.springframework.context.ApplicationContext; /** * Encodes the Execute response either in the normal XML format or in the raw format * @author Andrea Aime * */ public class ExecuteProcessResponse extends Response { XmlObjectEncodingResponse standardResponse; ApplicationContext ctx; public ExecuteProcessResponse(Class binding, String elementName, Class xmlConfiguration) { super(ExecuteResponseType.class); this.standardResponse = new XmlObjectEncodingResponse(binding, elementName, xmlConfiguration); } @Override public String getMimeType(Object value, Operation operation) throws ServiceException { if (isStandardDocumentResponse(operation)) { // normal execute response encoding return standardResponse.getMimeType(value, operation); } else { // raw response, let's see what the output is ExecuteResponseType response = (ExecuteResponseType) value; if(response.getProcessOutputs() == null) { // just a status report or a failure report return "text/xml"; } OutputDataType result = (OutputDataType) response.getProcessOutputs().getOutput().get(0); LiteralDataType literal = result.getData().getLiteralData(); ComplexDataType complex = result.getData().getComplexData(); if(literal != null) { // literals are encoded as plain strings return "text/plain"; } else if(complex != null) { // Execute should have properly setup the mime type return complex.getMimeType(); } else { // bbox return "text/xml"; } } } private boolean isStandardDocumentResponse(Operation operation) { if(operation.getParameters()[0] instanceof ExecuteType) { ExecuteType execute = (ExecuteType) operation.getParameters()[0]; return execute.getResponseForm() == null || execute.getResponseForm().getRawDataOutput() == null; } return true; } @Override public String getPreferredDisposition(Object value, Operation operation) { String mimeType = getMimeType(value, operation); String disposition = DISPOSITION_INLINE; if (mimeType != null) { // if there is a BinaryEncoder we could expose a method that allows // expressing preferred type as opposed to this ugliness if (mimeType.indexOf("image") == 0) { // tiff for download if (mimeType.indexOf("tiff") > 0) { disposition = DISPOSITION_ATTACH; } } else if (mimeType.equals("application/zip")) { disposition = DISPOSITION_ATTACH; } else if (mimeType.equals("application/arcgrid")) { disposition = DISPOSITION_ATTACH; } } return disposition; } @Override public String getAttachmentFileName(Object value, Operation operation) { if(isStandardDocumentResponse(operation)) { return "execute.xml"; } else { ExecuteType execute = (ExecuteType) operation.getParameters()[0]; ExecuteResponseType response = (ExecuteResponseType) value; if(response.getProcessOutputs() == null) { // just a status report or a failure report return "execute.xml"; } OutputDataType result = (OutputDataType) response.getProcessOutputs().getOutput().get(0); String fname = result.getIdentifier().getValue(); LiteralDataType literal = result.getData().getLiteralData(); ComplexDataType complex = result.getData().getComplexData(); String fext = null; // if it's a literal, use text, otherwise get the complex ppio and ask for the extension if (literal != null) { fext = "txt"; } else if(complex != null) { Name name = Ows11Util.name(response.getProcess().getIdentifier()); ProcessFactory factory = GeoServerProcessors.createProcessFactory(name, true); if (factory != null) { Map<String, Parameter<?>> resultInfo = factory.getResultInfo(name, null); Parameter p = resultInfo.get(result.getIdentifier().getValue()); if (p != null) { ProcessParameterIO ppio = ProcessParameterIO.find(p, ctx, complex.getMimeType()); if (ppio instanceof ComplexPPIO) { fext = ((ComplexPPIO) ppio).getFileExtension(result.getData() .getComplexData().getData().get(0)); } } } } // fallback if (fext == null) { fext = "bin"; } return fname + "." + fext; } } @Override public void write(Object value, OutputStream output, Operation operation) throws IOException, ServiceException { ExecuteResponseType response = (ExecuteResponseType) value; // From the spec: // In the most primitive case, when a response form of ―RawDataOutput‖ is requested, // process execution is successful, and only one complex output is produced, then the // Execute operation response will consist simply of that one complex output in its raw form // returned directly to the client. // In all other cases, the response to a valid Execute operation request is an // ExecuteResponse XML document if (isStandardDocumentResponse(operation) || response.getStatus().getProcessSucceeded() == null) { // normal execute response encoding standardResponse.write(value, output, operation); } else { // raw response, let's see what the output is OutputDataType result = (OutputDataType) response .getProcessOutputs().getOutput().get(0); LiteralDataType literal = result.getData().getLiteralData(); BoundingBoxType bbox = result.getData().getBoundingBoxData(); if (literal != null) { writeLiteral(output, literal); } else if(bbox != null) { writeBBox(output, bbox); } else { writeComplex(output, result); } } } private void writeBBox(OutputStream os, BoundingBoxType bbox) throws IOException { Encoder encoder = new Encoder(new OWSConfiguration()); encoder.encode(bbox, OWS.BoundingBox, os); } /** * Write out complex data assuming {@link Execute} has set up the proper * encoder as the output * * @param output * @param result * @throws IOException */ void writeComplex(OutputStream output, OutputDataType result) throws IOException { Object rawResult = result.getData().getComplexData().getData().get(0); if (rawResult instanceof RawDataEncoderDelegate) { RawDataEncoderDelegate delegate = (RawDataEncoderDelegate) rawResult; delegate.encode(output); } else if (rawResult instanceof XMLEncoderDelegate) { XMLEncoderDelegate delegate = (XMLEncoderDelegate) rawResult; try { TransformerHandler xmls = ((SAXTransformerFactory)SAXTransformerFactory.newInstance()).newTransformerHandler(); xmls.setResult(new StreamResult(output)); delegate.encode(xmls); } catch (IOException e) { throw e; } catch (Exception e) { throw new WPSException("An error occurred while encoding " + "the results of the process", e); } } else if(rawResult instanceof CDataEncoderDelegate) { try { ((CDataEncoderDelegate) rawResult).encode(output); } catch(Exception e) { throw new WPSException("An error occurred while encoding " + "the results of the process", e); } } else if(rawResult instanceof BinaryEncoderDelegate) { try { ((BinaryEncoderDelegate) rawResult).encode(output); } catch(Exception e) { throw new WPSException("An error occurred while encoding " + "the results of the process", e); } } else { throw new WPSException("Cannot encode an object of class " + rawResult.getClass() + " in raw form"); } } /** * Write out literal results by converting them to strings * @param output * @param literal */ void writeLiteral(OutputStream output, LiteralDataType literal) { PrintWriter writer = new PrintWriter(output); writer.write(literal.getValue()); writer.flush(); } }