/**
* Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* 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.n52.wps.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import net.opengis.ows.x11.DomainMetadataType;
import net.opengis.wps.x100.ComplexDataDescriptionType;
import net.opengis.wps.x100.ComplexDataType;
import net.opengis.wps.x100.DocumentOutputDefinitionType;
import net.opengis.wps.x100.ExecuteDocument;
import net.opengis.wps.x100.ExecuteDocument.Execute;
import net.opengis.wps.x100.InputDescriptionType;
import net.opengis.wps.x100.InputReferenceType;
import net.opengis.wps.x100.InputType;
import net.opengis.wps.x100.LiteralDataType;
import net.opengis.wps.x100.OutputDefinitionType;
import net.opengis.wps.x100.OutputDescriptionType;
import net.opengis.wps.x100.ProcessDescriptionType;
import net.opengis.wps.x100.ResponseDocumentType;
import net.opengis.wps.x100.ResponseFormType;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlString;
import org.n52.wps.io.GeneratorFactory;
import org.n52.wps.io.IGenerator;
import org.n52.wps.io.IOHandler;
import org.n52.wps.io.data.IData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author foerster
*/
public class ExecuteRequestBuilder {
ProcessDescriptionType processDesc;
ExecuteDocument execute;
String SUPPORTED_VERSION = "1.0.0";
private static Logger LOGGER = LoggerFactory.getLogger(ExecuteRequestBuilder.class);
public ExecuteRequestBuilder(ProcessDescriptionType processDesc) {
this.processDesc = processDesc;
execute = ExecuteDocument.Factory.newInstance();
Execute ex = execute.addNewExecute();
ex.setService("WPS");
ex.setVersion(SUPPORTED_VERSION);
ex.addNewIdentifier().setStringValue(processDesc.getIdentifier().getStringValue());
ex.addNewDataInputs();
}
public ExecuteRequestBuilder(ProcessDescriptionType processDesc, ExecuteDocument execute) {
this.processDesc = processDesc;
this.execute = execute;
}
/**
* add an input element. sets the data in the xml request
*
* @param parameterID the ID of the input (see process description)
* @param value the actual value (for xml data xml for binary data is should be base64 encoded data)
* @param schema schema if applicable otherwise null
* @param encoding encoding if not the default encoding (for default encoding set it to null) (i.e. binary data, use base64)
* @param mimeType mimetype of the data, has to be set
* @throws WPSClientException
*/
public void addComplexData(String parameterID, IData value, String schema, String encoding, String mimeType) throws WPSClientException {
GeneratorFactory fac = StaticDataHandlerRepository.getGeneratorFactory();
InputDescriptionType inputDesc = getParameterDescription(parameterID);
if (inputDesc == null) {
throw new IllegalArgumentException("inputDesription is null for: " + parameterID);
}
if (inputDesc.getComplexData() == null) {
throw new IllegalArgumentException("inputDescription is not of type ComplexData: " + parameterID);
}
LOGGER.debug("Looking for matching Generator ..." +
" schema: " + schema +
" mimeType: " + mimeType +
" encoding: " + encoding);
IGenerator generator = fac.getGenerator(schema, mimeType, encoding, value.getClass());
if (generator == null) {
// generator is still null
throw new IllegalArgumentException("Could not find an appropriate generator for parameter: " + parameterID);
}
InputStream stream = null;
InputType input = execute.getExecute().getDataInputs().addNewInput();
input.addNewIdentifier().setStringValue(
inputDesc.getIdentifier().getStringValue());
// encoding is UTF-8 (or nothing and we default to UTF-8)
// everything that goes to this condition should be inline xml data
try {
if (encoding == null || encoding.equals("")
|| encoding.equalsIgnoreCase(IOHandler.DEFAULT_ENCODING)) {
stream = generator.generateStream(value, mimeType, schema);
} else if (encoding.equalsIgnoreCase("base64")) {
stream = generator
.generateBase64Stream(value, mimeType, schema);
} else {
throw new WPSClientException("Encoding not supported");
}
ComplexDataType data = input.addNewData().addNewComplexData();
setComplexData(data, stream, schema, mimeType, encoding);
} catch (IOException e) {
throw new IllegalArgumentException(
"error reading generator output", e);
}
}
/**
* add an input element. sets the data in the xml request
*
* @param parameterID the ID of the input (see process description)
* @param value the actual value as String (for xml data xml for binary data is should be base64 encoded data)
* @param schema schema if applicable otherwise null
* @param encoding encoding if not the default encoding (for default encoding set it to null) (i.e. binary data, use base64)
* @param mimeType mimetype of the data, has to be set
* @throws WPSClientException
*/
public void addComplexData(String parameterID, String value, String schema, String encoding, String mimeType) throws WPSClientException {
InputDescriptionType inputDesc = getParameterDescription(parameterID);
if (inputDesc == null) {
throw new IllegalArgumentException("inputDesription is null for: " + parameterID);
}
if (inputDesc.getComplexData() == null) {
throw new IllegalArgumentException("inputDescription is not of type ComplexData: " + parameterID);
}
InputType input = execute.getExecute().getDataInputs().addNewInput();
input.addNewIdentifier().setStringValue(inputDesc.getIdentifier().getStringValue());
ComplexDataType data = input.addNewData().addNewComplexData();
setComplexData(data, value, schema, mimeType, encoding);
}
/**
* add an input element. sets the data in the xml request
*
* @param parameterID the ID of the input (see process description)
* @param value the actual value as String (for xml data xml for binary data is should be base64 encoded data)
* @param schema schema if applicable otherwise null
* @param encoding encoding if not the default encoding (for default encoding set it to null) (i.e. binary data, use base64)
* @param mimeType mimetype of the data, has to be set
* @throws WPSClientException
*/
public void addComplexData(String parameterID, String value, String schema, String encoding, String mimeType, boolean asReference) throws WPSClientException {
if(asReference){
addComplexDataReference(parameterID, value, schema, encoding, mimeType);
} else {
InputDescriptionType inputDesc = getParameterDescription(parameterID);
if (inputDesc == null) {
throw new IllegalArgumentException(
"inputDescription is null for: " + parameterID);
}
if (inputDesc.getComplexData() == null) {
throw new IllegalArgumentException(
"inputDescription is not of type ComplexData: "
+ parameterID);
}
InputType input = execute.getExecute().getDataInputs()
.addNewInput();
input.addNewIdentifier().setStringValue(
inputDesc.getIdentifier().getStringValue());
ComplexDataType data = input.addNewData().addNewComplexData();
setComplexData(data, value, schema, mimeType, encoding);
}
}
/**
* add an input element. sets the data in the xml request
*
* @param inputType
* @throws WPSClientException
*/
public void addComplexData(InputType inputType) {
String parameterID = inputType.getIdentifier().getStringValue();
InputDescriptionType inputDesc = getParameterDescription(parameterID);
if (inputDesc == null) {
throw new IllegalArgumentException("inputDescription is null for: " + parameterID);
}
if (inputDesc.getComplexData() == null) {
throw new IllegalArgumentException("inputDescription is not of type ComplexData: " + parameterID);
}
InputType[] newInputTypeArray;
InputType[] currentInputTypeArray = execute.getExecute().getDataInputs().getInputArray();
if(currentInputTypeArray != null){
newInputTypeArray = Arrays.copyOf(currentInputTypeArray, currentInputTypeArray.length + 1);
}else{
newInputTypeArray = new InputType[1];
}
newInputTypeArray[newInputTypeArray.length - 1] = inputType;
execute.getExecute().getDataInputs().setInputArray(newInputTypeArray);
}
/**
* Add literal data to the request
* @param parameterID the ID of the input paramter according to the describe process
* @param value the value. other types than strings have to be converted to string. The datatype is automatically determined and set accordingly to the process description
*/
public void addLiteralData(String parameterID, String value) {
InputDescriptionType inputDesc = this.getParameterDescription(parameterID);
if (inputDesc == null) {
throw new IllegalArgumentException("inputDescription is null for: " + parameterID);
}
if (inputDesc.getLiteralData() == null) {
throw new IllegalArgumentException("inputDescription is not of type literalData: " + parameterID);
}
InputType input = execute.getExecute().getDataInputs().addNewInput();
input.addNewIdentifier().setStringValue(parameterID);
input.addNewData().addNewLiteralData().setStringValue(value);
DomainMetadataType dataType = inputDesc.getLiteralData().getDataType();
if (dataType != null) {
input.getData().getLiteralData().setDataType(dataType.getReference());
}
}
/**
* Sets a reference to input data
*
* @param parameterID ID of the input element
* @param value reference URL
* @param schema schema if applicable otherwise null
* @param encoding encoding if applicable (typically not), otherwise null
* @param mimetype mimetype of the input according to the process description. has to be set
*/
public void addComplexDataReference(String parameterID, String value, String schema, String encoding, String mimetype) {
InputDescriptionType inputDesc = getParameterDescription(parameterID);
if (inputDesc == null) {
throw new IllegalArgumentException("inputDescription is null for: " + parameterID);
}
if (inputDesc.getComplexData() == null) {
throw new IllegalArgumentException("inputDescription is not of type complexData: " + parameterID);
}
InputType input = execute.getExecute().getDataInputs().addNewInput();
input.addNewIdentifier().setStringValue(parameterID);
input.addNewReference().setHref(value);
if (schema != null) {
input.getReference().setSchema(schema);
}
if (encoding != null) {
input.getReference().setEncoding(encoding);
}
if (mimetype != null) {
input.getReference().setMimeType(mimetype);
}
}
/**
* checks, if the execute, which has been build is valid according to the process description.
* @return
*/
public boolean isExecuteValid() {
return true;
}
/**
* this sets store for the specific output.
* @param parentInput
* @return
*/
public boolean setStoreSupport(String outputName, boolean storeSupport) {
// DocumentOutputDefinitionType outputDef = null;
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
ResponseFormType responseForm = execute.getExecute().getResponseForm();
if (!responseForm.isSetResponseDocument()) {
responseForm.addNewResponseDocument();
}
ResponseDocumentType responseDocument = responseForm.getResponseDocument();
responseDocument.setStoreExecuteResponse(storeSupport);
// for(DocumentOutputDefinitionType outputDefTemp: responseDocument.getOutputArray()) {
// if(outputDefTemp.getIdentifier().getStringValue().equals(outputName)) {
// outputDef = outputDefTemp;
// break;
// }
// }
// if (outputDef == null) {
// outputDef = responseDocument.addNewOutput();
// }
return true;
}
/**
* this sets store for the specific output.
* @param parentInput
* @return
*/
public boolean setAsReference(String outputName, boolean asReference) {
DocumentOutputDefinitionType outputDef = null;
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
if (!execute.getExecute().getResponseForm().isSetResponseDocument()) {
execute.getExecute().getResponseForm().addNewResponseDocument();
}
for(DocumentOutputDefinitionType outputDefTemp: execute.getExecute().getResponseForm().getResponseDocument().getOutputArray()) {
if(outputDefTemp.getIdentifier().getStringValue().equals(outputName)) {
outputDef = outputDefTemp;
break;
}
}
if (outputDef == null) {
outputDef = execute.getExecute().getResponseForm()
.getResponseDocument().addNewOutput();
}
for (OutputDescriptionType outputDesc : processDesc.getProcessOutputs().getOutputArray()) {
if (outputDesc.getIdentifier().getStringValue().equals(outputName)) {
outputDef.setAsReference(asReference);
}
}
return true;
}
/**
* this sets store for the specific output.
* @param parentInput
* @return
*/
public boolean setStatus(String outputName, boolean status) {
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
ResponseFormType responseForm = execute.getExecute().getResponseForm();
if (!responseForm.isSetResponseDocument()) {
responseForm.addNewResponseDocument();
}
ResponseDocumentType responseDocument = responseForm.getResponseDocument();
responseDocument.setStatus(status);
return true;
}
/**
* Set this if you want the data to a schema offered in the process description
* @param schema
* @param outputName
* @return
*/
public boolean setSchemaForOutput(String schema, String outputName) {
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
if (!execute.getExecute().getResponseForm().isSetResponseDocument()) {
execute.getExecute().getResponseForm().addNewResponseDocument();
}
OutputDescriptionType outputDesc = getOutputDescription(outputName);
DocumentOutputDefinitionType outputDef = getOutputDefinition(outputName);
if (outputDef == null) {
outputDef = execute.getExecute().getResponseForm()
.getResponseDocument().addNewOutput();
outputDef.setIdentifier(outputDesc.getIdentifier());
}
String defaultSchema = outputDesc.getComplexOutput().getDefault()
.getFormat().getSchema();
if ((defaultSchema != null && defaultSchema.equals(schema))
|| (defaultSchema == null && schema == null)) {
outputDef.setSchema(schema);
return true;
} else {
for (ComplexDataDescriptionType data : outputDesc
.getComplexOutput().getSupported().getFormatArray()) {
if (data.getSchema() != null && data.getSchema().equals(schema)) {
outputDef.setSchema(schema);
return true;
} else if ((data.getSchema() == null && schema == null)) {
return true;
}
}
}
return false;
}
/**
* sets the desired mimetype of the output. if not set, the default mimetype will be used as stated in the process description
* @param mimeType the name of the mimetype as announced in the processdescription
* @param outputName the Identifier of the output element
* @return success
*/
public boolean setMimeTypeForOutput(String mimeType, String outputName) {
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
if (!execute.getExecute().getResponseForm().isSetResponseDocument()) {
execute.getExecute().getResponseForm().addNewResponseDocument();
}
OutputDescriptionType outputDesc = getOutputDescription(outputName);
DocumentOutputDefinitionType outputDef = getOutputDefinition(outputName);
if (outputDef == null) {
outputDef = execute.getExecute().getResponseForm()
.getResponseDocument().addNewOutput();
outputDef.setIdentifier(outputDesc.getIdentifier());
}
String defaultMimeType = outputDesc.getComplexOutput().getDefault()
.getFormat().getMimeType();
if (defaultMimeType == null) {
defaultMimeType = "text/xml";
}
if (defaultMimeType.equals(mimeType)) {
outputDef.setMimeType(mimeType);
return true;
} else {
for (ComplexDataDescriptionType data : outputDesc
.getComplexOutput().getSupported().getFormatArray()) {
String m = data.getMimeType();
if (m != null && m.equals(mimeType)) {
outputDef.setMimeType(mimeType);
return true;
}
}
}
return false;
}
/**
* sets the encoding. necessary if data should not be retrieved in the default encoding (i.e. binary data in XML responses not raw data responses)
* @param encoding use base64
* @param outputName ID of the output
* @return
*/
public boolean setEncodingForOutput(String encoding, String outputName) {
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
if (!execute.getExecute().getResponseForm().isSetResponseDocument()) {
execute.getExecute().getResponseForm().addNewResponseDocument();
}
OutputDescriptionType outputDesc = getOutputDescription(outputName);
DocumentOutputDefinitionType outputDef = getOutputDefinition(outputName);
if (outputDef == null) {
outputDef = execute.getExecute().getResponseForm()
.getResponseDocument().addNewOutput();
outputDef.setIdentifier(outputDesc.getIdentifier());
}
String defaultEncoding = outputDesc.getComplexOutput().getDefault()
.getFormat().getEncoding();
if (defaultEncoding == null) {
defaultEncoding = IOHandler.DEFAULT_ENCODING;
}
if (defaultEncoding.equals(encoding)) {
return true;
} else {
ComplexDataDescriptionType[] supportedFormats = outputDesc
.getComplexOutput().getSupported().getFormatArray();
for (ComplexDataDescriptionType data : supportedFormats) {
String e = data.getEncoding();
if (e != null && e.equals(encoding)) {
outputDef.setEncoding(encoding);
return true;
}
}
}
return false;
}
private OutputDescriptionType getOutputDescription(String outputName) {
for (OutputDescriptionType outputDesc : processDesc.getProcessOutputs()
.getOutputArray()) {
if (outputDesc.getIdentifier().getStringValue().equals(outputName)) {
return outputDesc;
}
}
return null;
}
private DocumentOutputDefinitionType getOutputDefinition(String outputName) {
DocumentOutputDefinitionType[] outputs = execute.getExecute()
.getResponseForm().getResponseDocument().getOutputArray();
for (DocumentOutputDefinitionType outputDef : outputs) {
if (outputDef.getIdentifier().getStringValue().equals(outputName)) {
return outputDef;
}
}
return null;
}
public boolean setResponseDocument(String outputIdentifier, String schema, String encoding, String mimeType){
if (!execute.getExecute().isSetResponseForm()) {
execute.getExecute().addNewResponseForm();
}
if (!execute.getExecute().getResponseForm().isSetResponseDocument()) {
execute.getExecute().getResponseForm().addNewResponseDocument();
}
OutputDescriptionType outputDesc = getOutputDescription(outputIdentifier);
DocumentOutputDefinitionType outputDef = getOutputDefinition(outputIdentifier);
if (outputDef == null) {
outputDef = execute.getExecute().getResponseForm()
.getResponseDocument().addNewOutput();
outputDef.setIdentifier(outputDesc.getIdentifier());
if(schema != null){
outputDef.setSchema(schema);
}
if(encoding != null){
outputDef.setEncoding(encoding);
}
if(mimeType != null){
outputDef.setMimeType(mimeType);
}
}
return false;
}
/**
* Asks for data as raw data, i.e. without WPS XML wrapping
* @param schema if applicable otherwise null
* @param encoding if default encoding = null, otherwise base64
* @param mimeType requested mimetype of the output according to the process description. if not set, default mime type is used.
* @return
*/
public boolean setRawData(String outputIdentifier, String schema, String encoding, String mimeType) {
OutputDefinitionType output = execute.getExecute().addNewResponseForm().addNewRawDataOutput();
output.addNewIdentifier().setStringValue(outputIdentifier);
if (schema != null) {
output.setSchema(schema);
}
if (mimeType != null) {
output.setMimeType(mimeType);
}
if (encoding != null) {
output.setEncoding(encoding);
}
return true;
}
/**
* XML representation of the created request.
* @return
*/
public ExecuteDocument getExecute() {
return execute;
}
/**
* return a KVP representation for the created execute document.
* @return KVP request string
* @throws UnsupportedEncodingException if the URL encoding using UTF-8 fails
*/
public String getExecuteAsGETString() throws UnsupportedEncodingException {
String request = "?service=wps&request=execute&version=1.0.0&identifier=";
request = request + processDesc.getIdentifier().getStringValue();
request = request + "&DataInputs=";
InputType[] inputs = execute.getExecute().getDataInputs().getInputArray();
int inputCounter = 0;
for(InputType input : inputs){
request = request + input.getIdentifier().getStringValue();
if(input.isSetReference()){
//reference
InputReferenceType reference = input.getReference();
request = request + "="+"@xlink:href="+ URLEncoder.encode(reference.getHref(), "UTF-8");
if(reference.isSetEncoding()){
request = request + "@encoding="+reference.getEncoding();
}
if(reference.isSetMimeType()){
request = request + "@format="+reference.getMimeType();
}
if(reference.isSetEncoding()){
request = request + "@schema="+reference.getSchema();
}
}
if(input.isSetData()){
if(input.getData().isSetComplexData()){
//complex
ComplexDataType complexData = input.getData().getComplexData();
request = request + "=" + URLEncoder.encode(input.getData().getComplexData().xmlText(), "UTF-8");
if(complexData.isSetEncoding()){
request = request + "@encoding="+complexData.getEncoding();
}
if(complexData.isSetMimeType()){
request = request + "@format="+complexData.getMimeType();
}
if(complexData.isSetEncoding()){
request = request + "@schema="+complexData.getSchema();
}
}
if(input.getData().isSetLiteralData()){
//literal
LiteralDataType literalData = input.getData().getLiteralData();
request = request + "=" + literalData.getStringValue();
if(literalData.isSetDataType()){
request = request + "@datatype="+literalData.getDataType();
}
if(literalData.isSetUom()){
request = request + "@datatype="+literalData.getUom();
}
}
}
//concatenation for next input element
inputCounter = inputCounter +1;
if(inputCounter<inputs.length){
request = request + ";";
}
}
if(execute.getExecute().getResponseForm().getResponseDocument()==null){
throw new RuntimeException("ResponseDocument missing");
}
DocumentOutputDefinitionType[] outputs = execute.getExecute().getResponseForm().getResponseDocument().getOutputArray();
int outputCounter = 0;
if(execute.getExecute().getResponseForm().isSetRawDataOutput()){
request = request + "&rawdataoutput=";
}else{
request = request + "&responsedocument=";
}
for(DocumentOutputDefinitionType output : outputs){
request = request + output.getIdentifier().getStringValue();
if(output.isSetEncoding()){
request = request + "@encoding="+output.getEncoding();
}
if(output.isSetMimeType()){
request = request + "@format="+output.getMimeType();
}
if(output.isSetEncoding()){
request = request + "@schema="+output.getSchema();
}
if(output.isSetUom()){
request = request + "@datatype="+output.getUom();
}
//concatenation for next output element
outputCounter = outputCounter +1;
if(outputCounter<outputs.length){
request = request + ";";
}
}
if( execute.getExecute().getResponseForm().getResponseDocument().isSetStoreExecuteResponse()){
request = request + "&storeExecuteResponse=true";
}
if( execute.getExecute().getResponseForm().getResponseDocument().isSetStatus()){
request = request + "&status=true";
}
if( execute.getExecute().getResponseForm().getResponseDocument().isSetLineage()){
request = request + "&lineage=true";
}
return request;
}
/**
*
* @param id
* @return the specified parameterdescription. if not available it returns null.
*/
private InputDescriptionType getParameterDescription(String id) {
InputDescriptionType[] inputDescs = processDesc.getDataInputs().getInputArray();
for (InputDescriptionType inputDesc : inputDescs) {
if(inputDesc.getIdentifier().getStringValue().equals(id))
{
return inputDesc;
}
}
return null;
}
private void setComplexData(ComplexDataType data, Object value,
String schema, String mimeType, String encoding) {
if (value instanceof String) {
String valueString = (String) value;
try {
data.set(XmlObject.Factory.parse(valueString));
} catch (XmlException e) {
LOGGER.warn("error parsing data String as xml node, trying to parse data as xs:string");
XmlString xml = XmlString.Factory.newInstance();
xml.setStringValue(valueString);
data.set(xml);
}
} else if (value instanceof InputStream) {
InputStream stream = (InputStream) value;
try {
data.set(XmlObject.Factory.parse(stream));
} catch (XmlException e) {
LOGGER.warn("error parsing data stream as xml node, trying to parse data as xs:string");
String text = "";
int i = -1;
try {
while ((i = stream.read()) != -1) {
text = text + (char) i;
}
} catch (IOException e1) {
LOGGER.error("error parsing stream", e);
}
XmlString xml = XmlString.Factory.newInstance();
xml.setStringValue(text);
data.set(xml);
} catch (IOException e) {
LOGGER.error("error parsing stream", e);
}
}
if (schema != null) {
data.setSchema(schema);
}
if (mimeType != null) {
data.setMimeType(mimeType);
}
if (encoding != null) {
data.setEncoding(encoding);
}
}
}