52n-wps-server-gcube/src/main/java/org/n52/wps/server/request/InputHandler.java

1330 lines
50 KiB
Java
Raw Normal View History

/**
* Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* Apache License, version 2.0
* Apache Software License, version 1.0
* GNU Lesser General Public License, version 3
* Mozilla Public License, versions 1.0, 1.1 and 2.0
* Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* License version 2 and the aforementioned licenses.
*
* This program 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 General
* Public License for more details.
*/
package org.n52.wps.server.request;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import net.opengis.ows.x11.BoundingBoxType;
import net.opengis.ows.x11.DomainMetadataType;
import net.opengis.ows.x11.RangeType;
import net.opengis.ows.x11.ValueType;
import net.opengis.wps.x100.ComplexDataDescriptionType;
import net.opengis.wps.x100.ComplexDataType;
import net.opengis.wps.x100.InputDescriptionType;
import net.opengis.wps.x100.InputReferenceType;
import net.opengis.wps.x100.InputType;
import net.opengis.wps.x100.ProcessDescriptionType;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.n52.wps.commons.XMLUtil;
import org.n52.wps.io.BasicXMLTypeFactory;
import org.n52.wps.io.IOHandler;
import org.n52.wps.io.IParser;
import org.n52.wps.io.ParserFactory;
import org.n52.wps.io.data.IData;
import org.n52.wps.io.data.binding.bbox.BoundingBoxData;
import org.n52.wps.io.data.binding.literal.AbstractLiteralDataBinding;
import org.n52.wps.io.data.binding.literal.LiteralByteBinding;
import org.n52.wps.io.data.binding.literal.LiteralDoubleBinding;
import org.n52.wps.io.data.binding.literal.LiteralFloatBinding;
import org.n52.wps.io.data.binding.literal.LiteralIntBinding;
import org.n52.wps.io.data.binding.literal.LiteralLongBinding;
import org.n52.wps.io.data.binding.literal.LiteralShortBinding;
import org.n52.wps.server.ExceptionReport;
import org.n52.wps.server.RepositoryManager;
import org.n52.wps.server.handler.DataInputInterceptors;
import org.n52.wps.server.handler.DataInputInterceptors.DataInputInterceptorImplementations;
import org.n52.wps.server.handler.DataInputInterceptors.InterceptorInstance;
import org.n52.wps.server.request.strategy.ReferenceInputStream;
import org.n52.wps.server.request.strategy.ReferenceStrategyRegister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import com.google.common.primitives.Doubles;
/**
* Handles the input of the client and stores it into a Map.
*/
public class InputHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(InputHandler.class);
private static final BigInteger INT_MAX
= BigInteger.valueOf(Integer.MAX_VALUE);
private static final BigInteger INT_MIN
= BigInteger.valueOf(Integer.MIN_VALUE);
private Map<String, List<IData>> inputData = new HashMap<String, List<IData>>();
private ProcessDescriptionType processDesc;
private String algorithmIdentifier = null; // Needed to take care of handling a conflict between different parsers.
public static class Builder {
protected InputType[] inputs;
protected String algorithmIdentifier = null;
public Builder(InputType[] inputs, String algorithmIdentifier) {
this.inputs = inputs;
this.algorithmIdentifier = algorithmIdentifier;
}
public Builder inputs(InputType[] val) {
inputs = val;
return this;
}
public Builder algorithmIdentifier(String val) {
algorithmIdentifier = val;
return this;
}
public InputHandler build() throws ExceptionReport {
return new InputHandler(this);
}
}
/**
* Initializes a parser that handles each (line of) input based on the type of input.
* @see #handleComplexData(IOValueType)
* @see #handleComplexValueReference(IOValueType)
* @see #handleLiteralData(IOValueType)
* @see #handleBBoxValue(IOValueType)
* @param builder
* @throws ExceptionReport
*/
private InputHandler(Builder builder) throws ExceptionReport {
this.algorithmIdentifier = builder.algorithmIdentifier;
this.processDesc = RepositoryManager.getInstance().getProcessDescription(algorithmIdentifier);
if (processDesc == null) {
throw new ExceptionReport("Error while accessing the process description for " + algorithmIdentifier,
ExceptionReport.INVALID_PARAMETER_VALUE);
}
Map<String, InterceptorInstance> inputInterceptors = resolveInputInterceptors(algorithmIdentifier);
for (InputType input : builder.inputs) {
String inputId = input.getIdentifier().getStringValue().trim();
if (inputInterceptors.containsKey(inputId)) {
InterceptorInstance interceptor = inputInterceptors.get(inputId);
List<IData> result = interceptor.applyInterception(input);
if (result != null && !result.isEmpty()) {
this.inputData.put(inputId, result);
continue;
}
}
if(input.getData() != null) {
if(input.getData().getComplexData() != null) {
handleComplexData(input, inputId);
}
else if(input.getData().getLiteralData() != null) {
handleLiteralData(input);
}
else if(input.getData().getBoundingBoxData() != null) {
handleBBoxValue(input);
}
}
else if(input.getReference() != null) {
handleComplexValueReference(input);
}
else {
throw new ExceptionReport("Error while accessing the inputValue: " + inputId,
ExceptionReport.INVALID_PARAMETER_VALUE);
}
}
}
Map<String, InterceptorInstance> resolveInputInterceptors(String algorithmClassName) {
Map<String,InterceptorInstance> result = new HashMap<String, InterceptorInstance>();
Class<?> clazz;
try {
clazz = Class.forName(algorithmClassName, false, getClass().getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.warn("Could not find class {}", algorithmClassName);
return result;
}
DataInputInterceptorImplementations annotation = clazz.getAnnotation(DataInputInterceptors.DataInputInterceptorImplementations.class);
if (annotation != null) {
Class<?> interceptorClazz;
try {
interceptorClazz = Class.forName(annotation.value());
} catch (ClassNotFoundException e) {
LOGGER.warn("Could not find class "+ annotation.value(), e);
return result;
}
if (DataInputInterceptors.class.isAssignableFrom(interceptorClazz)) {
DataInputInterceptors instance;
try {
instance = (DataInputInterceptors) interceptorClazz.newInstance();
} catch (InstantiationException e) {
LOGGER.warn("Could not instantiate class "+ interceptorClazz, e);
return result;
} catch (IllegalAccessException e) {
LOGGER.warn("Could not access class "+ interceptorClazz, e);
return result;
}
return instance.getInterceptors();
}
}
return result;
}
InputDescriptionType getInputReferenceDescriptionType(String inputId) {
for (InputDescriptionType tempDesc : this.processDesc.getDataInputs().getInputArray()) {
if (inputId.equals(tempDesc.getIdentifier().getStringValue())) {
return tempDesc;
}
}
return null;
}
ComplexDataDescriptionType getNonDefaultFormat(InputDescriptionType inputRefDesc, String dataMimeType, String dataSchema, String dataEncoding) {
if (inputRefDesc.getComplexData() == null) {
return null; // No complex data within inputs
}
ComplexDataDescriptionType[] formats = inputRefDesc.getComplexData().getSupported().getFormatArray();
for (ComplexDataDescriptionType potentialFormat : formats) {
String pFormatSchema = potentialFormat.getSchema();
String pFormatEncoding = potentialFormat.getEncoding();
if (potentialFormat.getMimeType().equalsIgnoreCase(dataMimeType)) {
if (dataSchema != null && dataEncoding == null) {
if (dataSchema.equalsIgnoreCase(pFormatSchema)) {
return potentialFormat;
}
}
if (dataSchema == null && dataEncoding != null) {
if (dataEncoding.equalsIgnoreCase(pFormatEncoding)) {
return potentialFormat;
}
}
if (dataSchema != null && dataEncoding != null) {
if (dataSchema.equalsIgnoreCase(pFormatSchema)
&& dataEncoding.equalsIgnoreCase(pFormatEncoding)) {
return potentialFormat;
}
}
if (dataSchema == null && dataEncoding == null) {
return potentialFormat;
}
}
}
return null;
}
protected String getComplexValueNodeString(Node complexValueNode) {
String complexValue;
try {
//handle different contents of complexdata
if(complexValueNode.getChildNodes().getLength() > 1){
complexValue = complexValueNode.getChildNodes().item(1).getNodeValue();
if(complexValue == null){
return XMLUtil.nodeToString(complexValueNode.getChildNodes().item(1));
}
}else{
complexValue = complexValueNode.getFirstChild().getNodeValue();
}
if(complexValue == null){
return XMLUtil.nodeToString(complexValueNode.getFirstChild());
}
} catch (TransformerFactoryConfigurationError e1) {
throw new TransformerFactoryConfigurationError("Could not parse inline data. Reason " + e1);
} catch (TransformerException e1) {
throw new TransformerFactoryConfigurationError("Could not parse inline data. Reason " + e1);
}
return complexValue;
}
/**
* Handles the complexValue, which in this case should always include XML
* which can be parsed into a FeatureCollection.
* @param input The client input
* @param inputId
* @throws ExceptionReport If error occured while parsing XML
*/
protected void handleComplexData(InputType input, String inputId) throws ExceptionReport{
String complexValue;
InputDescriptionType inputReferenceDesc;
ComplexDataType data;
Node complexValueNode;
ComplexDataDescriptionType format = null;
String dataSchema;
String dataEncoding;
String dataMimeType = null;
String formatSchema = null;
String formatEncoding = null;
String potentialFormatSchema = null;
String potentialFormatEncoding = null;
inputReferenceDesc = getInputReferenceDescriptionType(inputId);
if(inputReferenceDesc == null) {
LOGGER.debug("Input cannot be found in description for " + processDesc.getIdentifier().getStringValue() + "," + inputId);
}
data = input.getData().getComplexData();
dataSchema = data.getSchema();
dataMimeType = data.getMimeType();
dataEncoding = data.getEncoding();
complexValueNode = input.getData().getComplexData().getDomNode();
complexValue = getComplexValueNodeString(complexValueNode);
//select parser
//1. mimeType set?
//yes--> set it
//1.1 schema/encoding set?
//yes-->set it
//not-->set default values for parser with matching mime type
//no--> schema or/and encoding are set?
//yes-->use it, look if only one mime type can be found;
//not-->use default values
// overwrite with data format from request if appropriate
if (data.isSetMimeType() && dataMimeType != null){
format = findComplexDataDescriptionType(inputReferenceDesc, dataMimeType, dataSchema, dataEncoding, potentialFormatSchema, potentialFormatEncoding);
if(format == null){
throw new ExceptionReport("Could not determine intput format", ExceptionReport.INVALID_PARAMETER_VALUE);
}
dataMimeType = format.getMimeType();
//no encoding provided--> select default one for mimeType
if(format.isSetEncoding()){
formatEncoding = format.getEncoding();
}
//no encoding provided--> select default one for mimeType
if(format.isSetSchema()){
formatSchema = format.getSchema();
}
} else {
//mimeType not in request
if(StringUtils.isBlank(dataMimeType) && !data.isSetEncoding() && !data.isSetSchema()){
//nothing set, use default values
formatSchema = inputReferenceDesc.getComplexData().getDefault().getFormat().getSchema();
dataMimeType = inputReferenceDesc.getComplexData().getDefault().getFormat().getMimeType();
formatEncoding = inputReferenceDesc.getComplexData().getDefault().getFormat().getEncoding();
}else{
//do a smart search an look if a mimeType can be found for either schema and/or encoding
if(StringUtils.isBlank(dataMimeType)){
if(data.isSetEncoding() && !data.isSetSchema()){
//encoding set only
int foundCount = 0;
String defaultEncoding = inputReferenceDesc.getComplexData().getDefault().getFormat().getEncoding();
ComplexDataDescriptionType encodingFormat = null;
String foundEncoding = null;
if(defaultEncoding.equalsIgnoreCase(data.getEncoding())){
foundEncoding = inputReferenceDesc.getComplexData().getDefault().getFormat().getEncoding();
encodingFormat = inputReferenceDesc.getComplexData().getDefault().getFormat();
foundCount++;
}else{
ComplexDataDescriptionType[] formats = inputReferenceDesc.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
if(tempFormat.getEncoding().equalsIgnoreCase(data.getEncoding())){
foundEncoding = tempFormat.getEncoding();
encodingFormat = tempFormat;
foundCount++;
}
}
}
if(foundCount == 1){
formatEncoding = foundEncoding;
dataMimeType = encodingFormat.getMimeType();
if(encodingFormat.isSetSchema()){
formatSchema = encodingFormat.getSchema();
}
}else{
throw new ExceptionReport("Request incomplete. Could not determine a suitable input format based on the given input [mime Type missing and given encoding not unique]", ExceptionReport.MISSING_PARAMETER_VALUE);
}
} else if(data.isSetSchema() && !data.isSetEncoding()){
//schema set only
ComplexDataDescriptionType schemaFormat = null;
String defaultSchema = inputReferenceDesc.getComplexData().getDefault().getFormat().getSchema();
int found = 0;
String foundSchema = null;
//TODO: please review change
//Old version causes NullPointerException if default input is given by mimetype and not by schema:
/*
if(defaultSchema != null && defaultSchema.equalsIgnoreCase(data.getSchema())){
...
}
* */
if(!StringUtils.isBlank(defaultSchema) && defaultSchema.equalsIgnoreCase(data.getSchema())){
foundSchema = inputReferenceDesc.getComplexData().getDefault().getFormat().getSchema();
schemaFormat = inputReferenceDesc.getComplexData().getDefault().getFormat();
found++;
}else{
ComplexDataDescriptionType[] formats = inputReferenceDesc.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
//TODO: please review change
//Old if-clause wouldn't be true ever and causes NullPointerException if one of the supported types is given by mimetype and not by schema:
/*
if(tempFormat.getEncoding().equalsIgnoreCase(data.getSchema())){
foundSchema = tempFormat.getSchema();
schemaFormat =tempFormat;
found = found +1;
}
*/
if(tempFormat.isSetSchema() && tempFormat.getSchema().equalsIgnoreCase(data.getSchema())){
foundSchema = tempFormat.getSchema();
schemaFormat =tempFormat;
found++;
}
}
}
if(found == 1){
formatSchema = foundSchema;
dataMimeType = schemaFormat.getMimeType();
if(schemaFormat.isSetEncoding()){
formatEncoding = schemaFormat.getEncoding();
}
}else{
throw new ExceptionReport("Request incomplete. Could not determine a suitable input format based on the given input [mime Type missing and given schema not unique]", ExceptionReport.MISSING_PARAMETER_VALUE);
}
} else if(data.isSetEncoding() && data.isSetSchema()){
//schema and encoding set
//encoding
String defaultEncoding = inputReferenceDesc.getComplexData().getDefault().getFormat().getEncoding();
List<ComplexDataDescriptionType> foundEncodingList = new ArrayList<ComplexDataDescriptionType>();
if(defaultEncoding.equalsIgnoreCase(data.getEncoding())){
foundEncodingList.add(inputReferenceDesc.getComplexData().getDefault().getFormat());
}else{
ComplexDataDescriptionType[] formats = inputReferenceDesc.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
if(tempFormat.getEncoding().equalsIgnoreCase(data.getEncoding())){
foundEncodingList.add(tempFormat);
}
}
//schema
List<ComplexDataDescriptionType> foundSchemaList = new ArrayList<ComplexDataDescriptionType>();
String defaultSchema = inputReferenceDesc.getComplexData().getDefault().getFormat().getSchema();
//TODO: please review change
//Old version causes NullPointerException if default input is given by mimetype and not by schema:
//
//if(defaultSchema.equalsIgnoreCase(data.getSchema())){...
//
if(defaultSchema!= null && defaultSchema.equalsIgnoreCase(data.getSchema())){
foundSchemaList.add(inputReferenceDesc.getComplexData().getDefault().getFormat());
}else{
formats = inputReferenceDesc.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
/*
* TODO please review Change
* Old if-clause wouldn't be true ever and causes NullPointerException if one of the supported types is given by mimetype and not by schema:
*
* old code:
if(tempFormat.getEncoding().equalsIgnoreCase(data.getSchema())){
foundSchemaList.add(tempFormat);
}
*/
if(tempFormat.getSchema()!=null && tempFormat.getSchema().equalsIgnoreCase(data.getSchema())){
foundSchemaList.add(tempFormat);
}
}
}
//results
ComplexDataDescriptionType foundCommonFormat = null;
for(ComplexDataDescriptionType encodingFormat : foundEncodingList){
for(ComplexDataDescriptionType schemaFormat : foundSchemaList){
if(encodingFormat.equals(schemaFormat)){
foundCommonFormat = encodingFormat;
}
}
}
if(foundCommonFormat!=null){
dataMimeType = foundCommonFormat.getMimeType();
if(foundCommonFormat.isSetEncoding()){
formatEncoding = foundCommonFormat.getEncoding();
}
if(foundCommonFormat.isSetSchema()){
formatSchema = foundCommonFormat.getSchema();
}
}else{
throw new ExceptionReport("Request incomplete. Could not determine a suitable input format based on the given input [mime Type missing and given encoding and schema are not unique]", ExceptionReport.MISSING_PARAMETER_VALUE);
}
}
}
}
}
}
IParser parser = null;
try {
LOGGER.debug("Looking for matching Parser ..." +
" schema: " + formatSchema +
" mimeType: " + dataMimeType +
" encoding: " + formatEncoding);
Class<?> algorithmInput = RepositoryManager.getInstance().getInputDataTypeForAlgorithm(this.algorithmIdentifier, inputId);
parser = ParserFactory.getInstance().getParser(formatSchema, dataMimeType, formatEncoding, algorithmInput);
} catch (RuntimeException e) {
throw new ExceptionReport("Error obtaining input data", ExceptionReport.NO_APPLICABLE_CODE, e);
}
if(parser == null) {
throw new ExceptionReport("Error. No applicable parser found for " + formatSchema + "," + dataMimeType + "," + formatEncoding, ExceptionReport.NO_APPLICABLE_CODE);
}
IData collection = parseComplexValue(formatEncoding, complexValue, dataMimeType, formatSchema, parser);
//enable maxoccurs of parameters with the same name.
List<IData> list = new ArrayList<IData>();
if(inputData.containsKey(inputId)) {
list = inputData.get(inputId);
}
list.add(collection);
inputData.put(inputId, list);
}
protected ComplexDataDescriptionType findComplexDataDescriptionType(InputDescriptionType inputReferenceDesc, String dataMimeType, String dataSchema, String dataEncoding, String potentialFormatSchema, String potentialFormatEncoding) {
ComplexDataDescriptionType result = null;
boolean canUseDefault = false;
String defaultMimeType = inputReferenceDesc.getComplexData().getDefault().getFormat().getMimeType();
if(defaultMimeType.equalsIgnoreCase(dataMimeType)){
ComplexDataDescriptionType potentialFormat = inputReferenceDesc.getComplexData().getDefault().getFormat();
LOGGER.debug("FindComplexDataDescriptionType->data schema: "+dataSchema+" data encoding "+dataEncoding);
LOGGER.debug("FindComplexDataDescriptionType->potential format schema : "+potentialFormatSchema);
//added by Gianpaolo Coro on 17/07/15
if (dataSchema!= null && dataSchema.trim().length()==0)
dataSchema = null;
if(dataSchema != null && dataEncoding == null){
if(dataSchema.equalsIgnoreCase(potentialFormatSchema)){
canUseDefault = true;
result = potentialFormat;
}
} else if(dataSchema != null && dataEncoding != null) {
if(dataSchema.equalsIgnoreCase(potentialFormatSchema)
&& dataEncoding.equalsIgnoreCase(potentialFormatEncoding)){
canUseDefault = true;
result = potentialFormat;
}
} else if(dataSchema == null && dataEncoding != null){
if(dataEncoding.equalsIgnoreCase(potentialFormatEncoding)){
canUseDefault = true;
result = potentialFormat;
}
} else {
canUseDefault = true;
result = potentialFormat;
}
}
LOGGER.debug("FindComplexDataDescriptionType->canUseDefault: "+canUseDefault);
LOGGER.debug("FindComplexDataDescriptionType->result: "+result);
if (!canUseDefault) {
result = getNonDefaultFormat(inputReferenceDesc, dataMimeType, dataSchema, dataEncoding);
}
return result;
}
protected IData parseComplexValue(String formatEncoding, String complexValue, String dataMimeType, String formatSchema, IParser parser) throws ExceptionReport {
IData idata;
String complexValueCopy = complexValue.toString();
// encoding is UTF-8 (or nothing and we default to UTF-8)
// everything that goes to this condition should be inline xml data
if (StringUtils.isBlank(formatEncoding) || formatEncoding.equalsIgnoreCase(IOHandler.DEFAULT_ENCODING)){
try {
if(!complexValueCopy.contains("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"")){
complexValueCopy = complexValueCopy.replace("xsi:schemaLocation", "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation");
}
idata = parser.parse(new ByteArrayInputStream(complexValueCopy.getBytes()), dataMimeType, formatSchema);
} catch(RuntimeException e) {
throw new ExceptionReport("Error occured, while XML parsing", ExceptionReport.NO_APPLICABLE_CODE, e);
}
} else if (formatEncoding.equalsIgnoreCase(IOHandler.ENCODING_BASE64)){
// in case encoding is base64
// everything that goes to this condition should be inline base64 data
idata = getBase64EncodedData(complexValue, parser, dataMimeType, formatSchema);
} else {
throw new ExceptionReport("Unable to generate encoding " + formatEncoding, ExceptionReport.NO_APPLICABLE_CODE);
}
return idata;
}
//TODO-- Needs testing
protected IData getBase64EncodedData(String complexValue, IParser parser, String dataMimeType, String formatSchema) throws ExceptionReport {
File f = null;
String complexValueCopy = complexValue.toString();
try {
f = File.createTempFile("wps" + UUID.randomUUID(), "tmp");
if (complexValueCopy.startsWith("<xml-fragment")) {
int startIndex = complexValueCopy.indexOf(">");
complexValueCopy = complexValueCopy.substring(startIndex + 1);
int endIndex = complexValueCopy.indexOf("</xml-fragment");
complexValueCopy = complexValueCopy.substring(0, endIndex);
}
FileUtils.write(f, complexValueCopy);
return parser.parseBase64(new FileInputStream(f), dataMimeType, formatSchema);
} catch (IOException e) {
throw new ExceptionReport("Error occured, while Base64 extracting", ExceptionReport.NO_APPLICABLE_CODE, e);
} finally {
FileUtils.deleteQuietly(f);
System.gc();
}
}
/**
* Handles the literalData
* @param input The client's input
* @throws ExceptionReport If the type of the parameter is invalid.
*/
private void handleLiteralData(InputType input) throws ExceptionReport {
String inputID = input.getIdentifier().getStringValue();
String parameter = input.getData().getLiteralData().getStringValue();
String xmlDataType = input.getData().getLiteralData().getDataType();
String uom = input.getData().getLiteralData().getUom();
InputDescriptionType inputDesc = null;
for(InputDescriptionType tempDesc : this.processDesc.getDataInputs().getInputArray()) {
if(inputID.equals(tempDesc.getIdentifier().getStringValue())) {
inputDesc = tempDesc;
break;
}
}
if(xmlDataType == null) {
DomainMetadataType dataType = inputDesc.getLiteralData().getDataType();
xmlDataType = dataType != null ? dataType.getReference() : null;
}
//still null, assume string as default
if(xmlDataType == null) {
xmlDataType = BasicXMLTypeFactory.STRING_URI;
} else if(xmlDataType.contains("http://www.w3.org/TR/xmlschema-2#")){
xmlDataType = xmlDataType.replace("http://www.w3.org/TR/xmlschema-2#","xs:");
}
xmlDataType = xmlDataType.toLowerCase();
IData parameterObj = null;
try {
parameterObj = BasicXMLTypeFactory.getBasicJavaObject(xmlDataType, parameter);
}
catch(RuntimeException e) {
throw new ExceptionReport("The passed parameterValue: " + parameter + " for input " + inputID + " is not of type: " + xmlDataType, ExceptionReport.INVALID_PARAMETER_VALUE);
}
//validate allowed values.
if(inputDesc.getLiteralData().isSetAllowedValues()){
if((!inputDesc.getLiteralData().isSetAnyValue())){
ValueType[] allowedValues = inputDesc.getLiteralData().getAllowedValues().getValueArray();
boolean foundAllowedValue = false;
for(ValueType allowedValue : allowedValues){
if(input.getData().getLiteralData().getStringValue().equals(allowedValue.getStringValue())){
foundAllowedValue = true;
}
}
RangeType[] allowedRanges = {};
if(parameterObj instanceof LiteralIntBinding || parameterObj instanceof LiteralDoubleBinding || parameterObj instanceof LiteralShortBinding || parameterObj instanceof LiteralFloatBinding || parameterObj instanceof LiteralLongBinding || parameterObj instanceof LiteralByteBinding){
allowedRanges = inputDesc.getLiteralData().getAllowedValues().getRangeArray();
for(RangeType allowedRange : allowedRanges){
foundAllowedValue = checkRange(parameterObj, allowedRange);
}
}
if(!foundAllowedValue && (allowedValues.length!=0 || allowedRanges.length!=0)){
throw new ExceptionReport("Input with ID " + inputID + " does not contain an allowed value. See ProcessDescription.", ExceptionReport.INVALID_PARAMETER_VALUE);
}
}
}
if(parameterObj == null) {
throw new ExceptionReport("XML datatype as LiteralParameter is not supported by the server: dataType " + xmlDataType,
ExceptionReport.INVALID_PARAMETER_VALUE);
}
if(uom != null && !uom.equals("")){
if(parameterObj instanceof AbstractLiteralDataBinding){
((AbstractLiteralDataBinding)parameterObj).setUnitOfMeasurement(uom);
}
}
//enable maxxoccurs of parameters with the same name.
if(inputData.containsKey(inputID)) {
List<IData> list = inputData.get(inputID);
list.add(parameterObj);
}
else {
List<IData> list = new ArrayList<IData>();
list.add(parameterObj);
inputData.put(inputID, list);
}
}
private boolean checkRange(IData parameterObj, RangeType allowedRange){
List<?> l = allowedRange.getRangeClosure();
/*
* no closure info or RangeClosure is "closed", so include boundaries
*/
if(l == null || l.isEmpty() || l.get(0).equals("closed")){
if((parameterObj instanceof LiteralIntBinding)){
int min = new Integer(allowedRange.getMinimumValue().getStringValue());
int max = new Integer(allowedRange.getMaximumValue().getStringValue());
if((Integer)(parameterObj.getPayload())>=min && (Integer)parameterObj.getPayload()<=max){
return true;
}
}
if((parameterObj instanceof LiteralDoubleBinding)){
Double min = new Double(allowedRange.getMinimumValue().getStringValue());
Double max = new Double(allowedRange.getMaximumValue().getStringValue());
if((Double)(parameterObj.getPayload())>=min && (Double)parameterObj.getPayload()<=max){
return true;
}
}
if((parameterObj instanceof LiteralShortBinding)){
Short min = new Short(allowedRange.getMinimumValue().getStringValue());
Short max = new Short(allowedRange.getMaximumValue().getStringValue());
if((Short)(parameterObj.getPayload())>=min && (Short)parameterObj.getPayload()<=max){
return true;
}
}
if((parameterObj instanceof LiteralFloatBinding)){
Float min = new Float(allowedRange.getMinimumValue().getStringValue());
Float max = new Float(allowedRange.getMaximumValue().getStringValue());
if((Float)(parameterObj.getPayload())>=min && (Float)parameterObj.getPayload()<=max){
return true;
}
}
if((parameterObj instanceof LiteralLongBinding)){
Long min = new Long(allowedRange.getMinimumValue().getStringValue());
Long max = new Long(allowedRange.getMaximumValue().getStringValue());
if((Long)(parameterObj.getPayload())>=min && (Long)parameterObj.getPayload()<=max){
return true;
}
}
if((parameterObj instanceof LiteralByteBinding)){
Byte min = new Byte(allowedRange.getMinimumValue().getStringValue());
Byte max = new Byte(allowedRange.getMaximumValue().getStringValue());
if((Byte)(parameterObj.getPayload())>=min && (Byte)parameterObj.getPayload()<=max){
return true;
}
}
return false;
}
/*
* TODO:implement other closure cases
*/
return false;
}
/**
* Handles the ComplexValueReference
* @param input The client input
* @throws ExceptionReport If the input (as url) is invalid, or there is an error while parsing the XML.
*/
private void handleComplexValueReference(InputType input) throws ExceptionReport{
String inputID = input.getIdentifier().getStringValue();
ReferenceStrategyRegister register = ReferenceStrategyRegister.getInstance();
ReferenceInputStream stream = register.resolveReference(input);
String dataURLString = input.getReference().getHref();
//dataURLString = URLDecoder.decode(dataURLString);
//dataURLString = dataURLString.replace("&amp;", "");
LOGGER.debug("Loading data from: " + dataURLString);
/**
* initialize data format with default values defaults and overwrite with defaults from request if applicable
*/
InputDescriptionType inputPD = null;
for(InputDescriptionType tempDesc : this.processDesc.getDataInputs().getInputArray()) {
if(inputID.equals(tempDesc.getIdentifier().getStringValue())) {
inputPD = tempDesc;
break;
}
}
if(inputPD == null) { // check if there is a corresponding input identifier in the process description
LOGGER.debug("Input cannot be found in description for " + this.processDesc.getIdentifier().getStringValue() + "," + inputID);
throw new RuntimeException("Input cannot be found in description for " + this.processDesc.getIdentifier().getStringValue() + "," + inputID);
}
//select parser
//1. mimeType set?
//yes--> set it
//1.1 schema/encoding set?
//yes-->set it
//not-->set default values for parser with matching mime type
//no--> look in http stream
//2. mimeType set in http stream
//yes -->set it
//2.1 schema/encoding set?
//yes-->set it
//not-->set default values for parser with matching mime type
//no--> schema or/and encoding are set?
//yes-->use it, look if only one mime type can be found
//not-->use default values
String schema = null;
String mimeType = null;
String encoding = null;
// overwrite with data format from request if appropriate
InputReferenceType referenceData = input.getReference();
if (referenceData.isSetMimeType() && referenceData.getMimeType() != null){
//mime type in request
mimeType = referenceData.getMimeType();
ComplexDataDescriptionType format = null;
String defaultMimeType = inputPD.getComplexData().getDefault().getFormat().getMimeType();
boolean canUseDefault = false;
if(defaultMimeType.equalsIgnoreCase(mimeType)){
ComplexDataDescriptionType potentialFormat = inputPD.getComplexData().getDefault().getFormat();
if(referenceData.getSchema() != null && referenceData.getEncoding() == null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema())){
canUseDefault = true;
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() != null){
if(referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
canUseDefault = true;
format = potentialFormat;
}
}
if(referenceData.getSchema() != null && referenceData.getEncoding() != null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema()) && referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
canUseDefault = true;
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() == null){
canUseDefault = true;
format = potentialFormat;
}
}
if(!canUseDefault){
ComplexDataDescriptionType[] formats = inputPD.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType potentialFormat : formats){
if(potentialFormat.getMimeType().equalsIgnoreCase(mimeType)){
if(referenceData.getSchema() != null && referenceData.getEncoding() == null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema())){
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() != null){
if(referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
format = potentialFormat;
}
}
if(referenceData.getSchema() != null && referenceData.getEncoding() != null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema()) && referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() == null){
format = potentialFormat;
}
}
}
}
if(format == null){
throw new ExceptionReport("Possibly multiple or none matching generators found for the input data with id = \"" + inputPD.getIdentifier().getStringValue() + "\". Is the MimeType (\"" + referenceData.getMimeType() + "\") correctly set?", ExceptionReport.INVALID_PARAMETER_VALUE);
//throw new ExceptionReport("Could not determine format of the input data (id= \"" + inputPD.getIdentifier().getStringValue() + "\"), given the mimetype \"" + referenceData.getMimeType() + "\"", ExceptionReport.INVALID_PARAMETER_VALUE);
}
mimeType = format.getMimeType();
if(format.isSetEncoding()){
//no encoding provided--> select default one for mimeType
encoding = format.getEncoding();
}
if(format.isSetSchema()){
//no encoding provided--> select default one for mimeType
schema = format.getSchema();
}
}else{
// mimeType not in request, fetch mimetype from reference response
mimeType = stream.getMimeType();
if(mimeType.contains("GML2")){
mimeType = "text/xml; subtype=gml/2.0.0";
}
if(mimeType.contains("GML3")){
mimeType = "text/xml; subtype=gml/3.0.0";
}
ComplexDataDescriptionType format = null;
if(mimeType != null){
String defaultMimeType = inputPD.getComplexData().getDefault().getFormat().getMimeType();
boolean canUseDefault = false;
if(defaultMimeType.equalsIgnoreCase(mimeType)){
ComplexDataDescriptionType potentialFormat = inputPD.getComplexData().getDefault().getFormat();
if(referenceData.getSchema() != null && referenceData.getEncoding() == null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema())){
canUseDefault = true;
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() != null){
if(referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
canUseDefault = true;
format = potentialFormat;
}
}
if(referenceData.getSchema() != null && referenceData.getEncoding() != null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema()) && referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
canUseDefault = true;
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() == null){
canUseDefault = true;
format = potentialFormat;
}
}
if(!canUseDefault){
ComplexDataDescriptionType[] formats = inputPD.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType potentialFormat : formats){
if(!StringUtils.isBlank(potentialFormat.getMimeType()) && potentialFormat.getMimeType().equalsIgnoreCase(mimeType)){
if(referenceData.getSchema() != null && referenceData.getEncoding() == null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema())){
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() != null){
if(referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
format = potentialFormat;
}
}
if(referenceData.getSchema() != null && referenceData.getEncoding() != null){
if(referenceData.getSchema().equalsIgnoreCase(potentialFormat.getSchema()) && referenceData.getEncoding().equalsIgnoreCase(potentialFormat.getEncoding())){
format = potentialFormat;
}
}
if(referenceData.getSchema() == null && referenceData.getEncoding() == null){
format = potentialFormat;
}
}
}
}
if(format == null){
//throw new ExceptionReport("Could not determine intput format. Possibly multiple or none matching generators found. MimeType Set?", ExceptionReport.INVALID_PARAMETER_VALUE);
// TODO Review error message
throw new ExceptionReport("Could not determine input format because none of the supported formats match the given schema (\"" + referenceData.getSchema() + "\") and encoding (\"" + referenceData.getEncoding() + "\"). (A mimetype was not specified)", ExceptionReport.INVALID_PARAMETER_VALUE);
}
mimeType = format.getMimeType();
if(format.isSetEncoding()){
//no encoding provided--> select default one for mimeType
encoding = format.getEncoding();
}
if(format.isSetSchema()){
//no encoding provided--> select default one for mimeType
schema = format.getSchema();
}
}
if(mimeType==null && !referenceData.isSetEncoding() && !referenceData.isSetSchema()){
//nothing set, use default values
schema = inputPD.getComplexData().getDefault().getFormat().getSchema();
mimeType = inputPD.getComplexData().getDefault().getFormat().getMimeType();
encoding = inputPD.getComplexData().getDefault().getFormat().getEncoding();
}else{
//do a smart search an look if a mimeType can be found for either schema and/or encoding
if(mimeType==null){
if(referenceData.isSetEncoding() && !referenceData.isSetSchema()){
//encoding set only
ComplexDataDescriptionType encodingFormat = null;
String defaultEncoding = inputPD.getComplexData().getDefault().getFormat().getEncoding();
int found = 0;
String foundEncoding = null;
if(defaultEncoding.equalsIgnoreCase(referenceData.getEncoding())){
foundEncoding = inputPD.getComplexData().getDefault().getFormat().getEncoding();
encodingFormat = inputPD.getComplexData().getDefault().getFormat();
found += 1;
}else{
ComplexDataDescriptionType[] formats = inputPD.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
if(tempFormat.getEncoding().equalsIgnoreCase(referenceData.getEncoding())){
foundEncoding = tempFormat.getEncoding();
encodingFormat = tempFormat;
found += 1;
}
}
}
if(found == 1){
encoding = foundEncoding;
mimeType = encodingFormat.getMimeType();
if(encodingFormat.isSetSchema()){
schema = encodingFormat.getSchema();
}
}else{
throw new ExceptionReport("Request incomplete. Could not determine a suitable input format based on the given input [mime Type missing and given encoding not unique]", ExceptionReport.MISSING_PARAMETER_VALUE);
}
}
if(referenceData.isSetSchema() && !referenceData.isSetEncoding()){
//schema set only
ComplexDataDescriptionType schemaFormat = null;
String defaultSchema = inputPD.getComplexData().getDefault().getFormat().getSchema();
int found = 0;
String foundSchema = null;
if(defaultSchema.equalsIgnoreCase(referenceData.getSchema())){
foundSchema = inputPD.getComplexData().getDefault().getFormat().getSchema();
schemaFormat = inputPD.getComplexData().getDefault().getFormat();
found += 1;
}else{
ComplexDataDescriptionType[] formats = inputPD.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
if(tempFormat.getEncoding().equalsIgnoreCase(referenceData.getSchema())){
foundSchema = tempFormat.getSchema();
schemaFormat =tempFormat;
found += 1;
}
}
}
if(found == 1){
schema = foundSchema;
mimeType = schemaFormat.getMimeType();
if(schemaFormat.isSetEncoding()){
encoding = schemaFormat.getEncoding();
}
}else{
throw new ExceptionReport("Request incomplete. Could not determine a suitable input format based on the given input [mime Type missing and given schema not unique]", ExceptionReport.MISSING_PARAMETER_VALUE);
}
}
if(referenceData.isSetEncoding() && referenceData.isSetSchema()){
//schema and encoding set
//encoding
String defaultEncoding = inputPD.getComplexData().getDefault().getFormat().getEncoding();
List<ComplexDataDescriptionType> foundEncodingList = new ArrayList<ComplexDataDescriptionType>();
if(defaultEncoding.equalsIgnoreCase(referenceData.getEncoding())){
foundEncodingList.add(inputPD.getComplexData().getDefault().getFormat());
}else{
ComplexDataDescriptionType[] formats = inputPD.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
if(tempFormat.getEncoding().equalsIgnoreCase(referenceData.getEncoding())){
foundEncodingList.add(tempFormat);
}
}
//schema
List<ComplexDataDescriptionType> foundSchemaList = new ArrayList<ComplexDataDescriptionType>();
String defaultSchema = inputPD.getComplexData().getDefault().getFormat().getSchema();
if(defaultSchema.equalsIgnoreCase(referenceData.getSchema())){
foundSchemaList.add(inputPD.getComplexData().getDefault().getFormat());
}else{
formats = inputPD.getComplexData().getSupported().getFormatArray();
for(ComplexDataDescriptionType tempFormat : formats){
if(tempFormat.getEncoding().equalsIgnoreCase(referenceData.getSchema())){
foundSchemaList.add(tempFormat);
}
}
}
//results
ComplexDataDescriptionType foundCommonFormat = null;
for(ComplexDataDescriptionType encodingFormat : foundEncodingList){
for(ComplexDataDescriptionType schemaFormat : foundSchemaList){
if(encodingFormat.equals(schemaFormat)){
foundCommonFormat = encodingFormat;
}
}
}
if(foundCommonFormat!=null){
mimeType = foundCommonFormat.getMimeType();
if(foundCommonFormat.isSetEncoding()){
encoding = foundCommonFormat.getEncoding();
}
if(foundCommonFormat.isSetSchema()){
schema = foundCommonFormat.getSchema();
}
}else{
throw new ExceptionReport("Request incomplete. Could not determine a suitable input format based on the given input [mime Type missing and given encoding and schema are not unique]", ExceptionReport.MISSING_PARAMETER_VALUE);
}
}
}
}
}
}
LOGGER.debug("Loading parser for: schema = \""+ schema
+ "\" , mimetype = \"" + mimeType
+ "\", encoding = \"" + encoding + "\"");
IParser parser = null;
try {
Class<?> algorithmInputClass = RepositoryManager.getInstance().getInputDataTypeForAlgorithm(this.algorithmIdentifier, inputID);
if(algorithmInputClass == null) {
throw new RuntimeException("Could not determine internal input class for input" + inputID);
}
LOGGER.info("Looking for matching Parser ..." +
" schema: \"" + schema +
"\", mimeType: \"" + mimeType +
"\", encoding: \"" + encoding + "\"");
parser = ParserFactory.getInstance().getParser(schema, mimeType, encoding, algorithmInputClass);
if(parser == null) {
throw new ExceptionReport("Error. No applicable parser found for schema=\"" + schema + "\", mimeType=\"" + mimeType + "\", encoding=\"" + encoding + "\"", ExceptionReport.NO_APPLICABLE_CODE);
}
} catch (RuntimeException e) {
throw new ExceptionReport("Error obtaining input data", ExceptionReport.NO_APPLICABLE_CODE, e);
}
/****PROXY*****/
/*String decodedURL = URLDecoder.decode(dataURLString);
decodedURL = decodedURL.replace("&amp;", "&");
if(decodedURL.indexOf("&BBOX")==-1){
decodedURL = decodedURL.replace("BBOX", "&BBOX");
decodedURL = decodedURL.replace("outputFormat", "&outputFormat");
decodedURL = decodedURL.replace("SRS", "&SRS");
decodedURL = decodedURL.replace("REQUEST", "&REQUEST");
decodedURL = decodedURL.replace("VERSION", "&VERSION");
decodedURL = decodedURL.replace("SERVICE", "&SERVICE");
decodedURL = decodedURL.replace("format", "&format");
}*/
//TODO lookup WFS -- we can't do that here.
// if(dataURLString.toUpperCase().contains("REQUEST=GETFEATURE") &&
// dataURLString.toUpperCase().contains("SERVICE=WFS")){
// if(parser instanceof SimpleGMLParser){
// parser = new GML2BasicParser();
// }
// if(parser instanceof GML2BasicParser && !dataURLString.toUpperCase().contains("OUTPUTFORMAT=GML2")){
// //make sure we get GML2
// dataURLString = dataURLString+"&outputFormat=GML2";
// }
// if(parser instanceof GML3BasicParser && !dataURLString.toUpperCase().contains("OUTPUTFORMAT=GML3")){
// //make sure we get GML3
// dataURLString = dataURLString+"&outputFormat=GML3";
// }
// }
IData parsedInputData = parser.parse(stream, mimeType, schema);
//enable maxxoccurs of parameters with the same name.
if(inputData.containsKey(inputID)) {
List<IData> list = inputData.get(inputID);
list.add(parsedInputData);
inputData.put(inputID, list);
}
else {
List<IData> list = new ArrayList<IData>();
list.add(parsedInputData);
inputData.put(inputID, list);
}
}
/**
* Handles BBoxValue
* @param input The client input
*/
private void handleBBoxValue(InputType input)
throws ExceptionReport {
IData envelope = parseBoundingBox(input.getData().getBoundingBoxData());
List<IData> resultList = inputData.get(input.getIdentifier()
.getStringValue());
if (resultList == null) {
inputData.put(input.getIdentifier().getStringValue(), resultList
= new ArrayList<IData>(1));
}
resultList.add(envelope);
}
private IData parseBoundingBox(BoundingBoxType bbt)
throws ExceptionReport {
final BigInteger dim = bbt.getDimensions();
final double[] lower, upper;
if (dim != null && (dim.compareTo(INT_MAX) > 0 ||
dim.compareTo(INT_MIN) < 0)) {
throw new ExceptionReport(
String.format("Unsupported BoundingBox dimension %s. Has to be betweeen %s and %s!",
dim, INT_MIN, INT_MAX),
ExceptionReport.INVALID_PARAMETER_VALUE);
}
try {
lower = parseCoordinate(bbt.getLowerCorner());
} catch (NumberFormatException e) {
throw new ExceptionReport("Invalid lower corner",
ExceptionReport.INVALID_PARAMETER_VALUE, e);
}
try {
upper = parseCoordinate(bbt.getUpperCorner());
} catch (NumberFormatException e) {
throw new ExceptionReport("Invalid upper corner",
ExceptionReport.INVALID_PARAMETER_VALUE, e);
}
if (upper.length != lower.length) {
throw new ExceptionReport(
String.format("Mismatching BoundingBox dimensions: %s vs %s!",
upper.length, lower.length),
ExceptionReport.INVALID_PARAMETER_VALUE);
}
if (dim != null && lower.length != dim.intValue()) {
throw new ExceptionReport(
String.format("Mismatching BoundingBox dimensions: %s vs %s!",
dim.intValue(), lower.length),
ExceptionReport.INVALID_PARAMETER_VALUE);
}
return new BoundingBoxData(lower, upper, bbt.getCrs());
}
private double[] parseCoordinate(List<?> ordinates)
throws NumberFormatException {
List<Number> coordinate = new ArrayList<Number>(ordinates.size());
for (Object o : ordinates) {
if (o instanceof Number) {
coordinate.add((Number) o);
} else {
coordinate.add(Double.parseDouble(String.valueOf(o)));
}
}
return Doubles.toArray(coordinate);
}
/**
* Gets the resulting InputLayers from the parser
* @return A map with the parsed input
*/
public Map<String, List<IData>> getParsedInputData(){
return inputData;
}
// private InputStream retrievingZippedContent(URLConnection conn) throws IOException{
// String contentType = conn.getContentEncoding();
// if(contentType != null && contentType.equals("gzip")) {
// return new GZIPInputStream(conn.getInputStream());
// }
// else{
// return conn.getInputStream();
// }
// }
}