398 lines
17 KiB
Java
398 lines
17 KiB
Java
|
/**
|
|||
|
* 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.server;
|
|||
|
|
|||
|
import java.util.ArrayList;
|
|||
|
import java.util.Collection;
|
|||
|
import java.util.Iterator;
|
|||
|
import java.util.List;
|
|||
|
|
|||
|
import net.opengis.ows.x11.AllowedValuesDocument.AllowedValues;
|
|||
|
import net.opengis.wps.x100.ComplexDataCombinationType;
|
|||
|
import net.opengis.wps.x100.ComplexDataCombinationsType;
|
|||
|
import net.opengis.wps.x100.ComplexDataDescriptionType;
|
|||
|
import net.opengis.wps.x100.InputDescriptionType;
|
|||
|
import net.opengis.wps.x100.LiteralInputType;
|
|||
|
import net.opengis.wps.x100.OutputDescriptionType;
|
|||
|
import net.opengis.wps.x100.ProcessDescriptionType;
|
|||
|
import net.opengis.wps.x100.ProcessDescriptionType.DataInputs;
|
|||
|
import net.opengis.wps.x100.ProcessDescriptionType.ProcessOutputs;
|
|||
|
import net.opengis.wps.x100.ProcessDescriptionsDocument;
|
|||
|
import net.opengis.wps.x100.ProcessDescriptionsDocument.ProcessDescriptions;
|
|||
|
import net.opengis.wps.x100.SupportedComplexDataInputType;
|
|||
|
import net.opengis.wps.x100.SupportedComplexDataType;
|
|||
|
|
|||
|
import org.apache.xmlbeans.XmlOptions;
|
|||
|
import org.apache.xmlbeans.XmlValidationError;
|
|||
|
import org.n52.wps.FormatDocument.Format;
|
|||
|
import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.ComplexDataInputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.ComplexDataOutputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.InputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor;
|
|||
|
import org.n52.wps.algorithm.descriptor.OutputDescriptor;
|
|||
|
import org.n52.wps.io.GeneratorFactory;
|
|||
|
import org.n52.wps.io.IGenerator;
|
|||
|
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.server.observerpattern.IObserver;
|
|||
|
import org.n52.wps.server.observerpattern.ISubject;
|
|||
|
import org.slf4j.Logger;
|
|||
|
import org.slf4j.LoggerFactory;
|
|||
|
|
|||
|
import com.google.common.base.Strings;
|
|||
|
|
|||
|
public abstract class AbstractDescriptorAlgorithm implements IAlgorithm, ISubject {
|
|||
|
|
|||
|
private final static Logger LOGGER = LoggerFactory.getLogger(AbstractDescriptorAlgorithm.class);
|
|||
|
|
|||
|
private AlgorithmDescriptor descriptor;
|
|||
|
private ProcessDescriptionType description;
|
|||
|
|
|||
|
public AbstractDescriptorAlgorithm() {
|
|||
|
super();
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public synchronized ProcessDescriptionType getDescription() {
|
|||
|
if (description == null) {
|
|||
|
description = createProcessDescription();
|
|||
|
}
|
|||
|
return description;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public String getWellKnownName() {
|
|||
|
return getAlgorithmDescriptor().getIdentifier();
|
|||
|
}
|
|||
|
|
|||
|
private ProcessDescriptionType createProcessDescription() {
|
|||
|
|
|||
|
AlgorithmDescriptor algorithmDescriptor = getAlgorithmDescriptor();
|
|||
|
|
|||
|
ProcessDescriptionsDocument document = ProcessDescriptionsDocument.Factory.newInstance();
|
|||
|
ProcessDescriptions processDescriptions = document.addNewProcessDescriptions();
|
|||
|
ProcessDescriptionType processDescription = processDescriptions.addNewProcessDescription();
|
|||
|
|
|||
|
if (algorithmDescriptor == null) {
|
|||
|
throw new IllegalStateException("Instance must have an algorithm descriptor");
|
|||
|
} else {
|
|||
|
|
|||
|
// 1. Identifier
|
|||
|
processDescription.setStatusSupported(algorithmDescriptor.getStatusSupported());
|
|||
|
processDescription.setStoreSupported(algorithmDescriptor.getStoreSupported());
|
|||
|
processDescription.setProcessVersion(algorithmDescriptor.getVersion());
|
|||
|
processDescription.addNewIdentifier().setStringValue(algorithmDescriptor.getIdentifier());
|
|||
|
processDescription.addNewTitle().setStringValue( algorithmDescriptor.hasTitle() ?
|
|||
|
algorithmDescriptor.getTitle() :
|
|||
|
algorithmDescriptor.getIdentifier());
|
|||
|
if (algorithmDescriptor.hasAbstract()) {
|
|||
|
processDescription.addNewAbstract().setStringValue(algorithmDescriptor.getAbstract());
|
|||
|
}
|
|||
|
|
|||
|
// 2. Inputs
|
|||
|
Collection<InputDescriptor> inputDescriptors = algorithmDescriptor.getInputDescriptors();
|
|||
|
DataInputs dataInputs = null;
|
|||
|
if (inputDescriptors.size() > 0) {
|
|||
|
dataInputs = processDescription.addNewDataInputs();
|
|||
|
}
|
|||
|
for (InputDescriptor inputDescriptor : inputDescriptors) {
|
|||
|
|
|||
|
InputDescriptionType dataInput = dataInputs.addNewInput();
|
|||
|
dataInput.setMinOccurs(inputDescriptor.getMinOccurs());
|
|||
|
dataInput.setMaxOccurs(inputDescriptor.getMaxOccurs());
|
|||
|
|
|||
|
dataInput.addNewIdentifier().setStringValue(inputDescriptor.getIdentifier());
|
|||
|
dataInput.addNewTitle().setStringValue( inputDescriptor.hasTitle() ?
|
|||
|
inputDescriptor.getTitle() :
|
|||
|
inputDescriptor.getIdentifier());
|
|||
|
if (inputDescriptor.hasAbstract()) {
|
|||
|
dataInput.addNewAbstract().setStringValue(inputDescriptor.getAbstract());
|
|||
|
}
|
|||
|
|
|||
|
if (inputDescriptor instanceof LiteralDataInputDescriptor) {
|
|||
|
LiteralDataInputDescriptor<?> literalDescriptor = (LiteralDataInputDescriptor)inputDescriptor;
|
|||
|
|
|||
|
LiteralInputType literalData = dataInput.addNewLiteralData();
|
|||
|
literalData.addNewDataType().setReference(literalDescriptor.getDataType());
|
|||
|
|
|||
|
if (literalDescriptor.hasDefaultValue()) {
|
|||
|
literalData.setDefaultValue(literalDescriptor.getDefaultValue());
|
|||
|
}
|
|||
|
if (literalDescriptor.hasAllowedValues()) {
|
|||
|
AllowedValues allowed = literalData.addNewAllowedValues();
|
|||
|
for (String allowedValue : literalDescriptor.getAllowedValues()) {
|
|||
|
allowed.addNewValue().setStringValue(allowedValue);
|
|||
|
}
|
|||
|
} else {
|
|||
|
literalData.addNewAnyValue();
|
|||
|
}
|
|||
|
|
|||
|
} else if (inputDescriptor instanceof ComplexDataInputDescriptor) {
|
|||
|
SupportedComplexDataInputType complexDataType = dataInput.addNewComplexData();
|
|||
|
ComplexDataInputDescriptor complexInputDescriptor =
|
|||
|
(ComplexDataInputDescriptor)inputDescriptor;
|
|||
|
if (complexInputDescriptor.hasMaximumMegaBytes()) {
|
|||
|
complexDataType.setMaximumMegabytes(complexInputDescriptor.getMaximumMegaBytes());
|
|||
|
}
|
|||
|
describeComplexDataInputType(complexDataType, inputDescriptor.getBinding());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 3. Outputs
|
|||
|
ProcessOutputs dataOutputs = processDescription.addNewProcessOutputs();
|
|||
|
Collection<OutputDescriptor> outputDescriptors = algorithmDescriptor.getOutputDescriptors();
|
|||
|
if (outputDescriptors.size() < 1) {
|
|||
|
LOGGER.error("No outputs found for algorithm {}", algorithmDescriptor.getIdentifier());
|
|||
|
}
|
|||
|
for (OutputDescriptor outputDescriptor : outputDescriptors) {
|
|||
|
|
|||
|
OutputDescriptionType dataOutput = dataOutputs.addNewOutput();
|
|||
|
dataOutput.addNewIdentifier().setStringValue(outputDescriptor.getIdentifier());
|
|||
|
dataOutput.addNewTitle().setStringValue( outputDescriptor.hasTitle() ?
|
|||
|
outputDescriptor.getTitle() :
|
|||
|
outputDescriptor.getIdentifier());
|
|||
|
if (outputDescriptor.hasAbstract()) {
|
|||
|
dataOutput.addNewAbstract().setStringValue(outputDescriptor.getAbstract());
|
|||
|
}
|
|||
|
|
|||
|
if (outputDescriptor instanceof LiteralDataOutputDescriptor) {
|
|||
|
LiteralDataOutputDescriptor<?> literalDescriptor = (LiteralDataOutputDescriptor)outputDescriptor;
|
|||
|
dataOutput.addNewLiteralOutput().addNewDataType().
|
|||
|
setReference(literalDescriptor.getDataType());
|
|||
|
} else if (outputDescriptor instanceof ComplexDataOutputDescriptor) {
|
|||
|
describeComplexDataOutputType(dataOutput.addNewComplexOutput(), outputDescriptor.getBinding());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return document.getProcessDescriptions().getProcessDescriptionArray(0);
|
|||
|
}
|
|||
|
|
|||
|
private void describeComplexDataInputType(SupportedComplexDataType complexData, Class dataTypeClass) {
|
|||
|
List<IParser> parsers = ParserFactory.getInstance().getAllParsers();
|
|||
|
List<IParser> foundParsers = new ArrayList<IParser>();
|
|||
|
for (IParser parser : parsers) {
|
|||
|
// /*2.0*/ Class[] supportedClasses = parser.getSupportedInternalOutputDataType();
|
|||
|
/*3.0*/ Class[] supportedClasses = parser.getSupportedDataBindings();
|
|||
|
for (Class clazz : supportedClasses) {
|
|||
|
if (dataTypeClass.isAssignableFrom(clazz)) {
|
|||
|
foundParsers.add(parser);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
describeComplexDataType(complexData, foundParsers);
|
|||
|
}
|
|||
|
|
|||
|
private void describeComplexDataOutputType(SupportedComplexDataType complexData, Class dataTypeClass) {
|
|||
|
|
|||
|
List<IGenerator> generators = GeneratorFactory.getInstance().getAllGenerators();
|
|||
|
List<IGenerator> foundGenerators = new ArrayList<IGenerator>();
|
|||
|
for (IGenerator generator : generators) {
|
|||
|
// /*2.0*/ Class[] supportedClasses = generator.getSupportedInternalInputDataType(); // appears to have been removed in 52n WPS 3.0
|
|||
|
/*3.0*/ Class[] supportedClasses = generator.getSupportedDataBindings();
|
|||
|
for (Class clazz : supportedClasses) {
|
|||
|
if (clazz.isAssignableFrom(dataTypeClass)) {
|
|||
|
foundGenerators.add(generator);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
describeComplexDataType(complexData, foundGenerators);
|
|||
|
}
|
|||
|
|
|||
|
private void describeComplexDataType(
|
|||
|
SupportedComplexDataType complexData,
|
|||
|
List<? extends IOHandler> handlers)
|
|||
|
{
|
|||
|
ComplexDataCombinationType defaultFormatType = complexData.addNewDefault();
|
|||
|
ComplexDataCombinationsType supportedFormatType = complexData.addNewSupported();
|
|||
|
|
|||
|
boolean needDefault = true;
|
|||
|
for (IOHandler handler : handlers) {
|
|||
|
|
|||
|
Format[] fullFormats = handler.getSupportedFullFormats();
|
|||
|
if (fullFormats != null && fullFormats.length > 0) {
|
|||
|
if (needDefault) {
|
|||
|
needDefault = false;
|
|||
|
describeComplexDataFormat(
|
|||
|
defaultFormatType.addNewFormat(),
|
|||
|
fullFormats[0]);
|
|||
|
}
|
|||
|
for (int formatIndex = 0, formatCount = fullFormats.length; formatIndex < formatCount; ++formatIndex) {
|
|||
|
describeComplexDataFormat(
|
|||
|
supportedFormatType.addNewFormat(),
|
|||
|
fullFormats[formatIndex]);
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
String[] formats = handler.getSupportedFormats();
|
|||
|
|
|||
|
if (formats == null || formats.length == 0) {
|
|||
|
LOGGER.warn("Skipping IOHandler {} in ProcessDescription generation for {}, no formats specified",
|
|||
|
handler.getClass().getSimpleName(),
|
|||
|
getWellKnownName());
|
|||
|
} else {
|
|||
|
// if formats, encodings or schemas arrays are 'null' or empty, create
|
|||
|
// new array with single 'null' element. We do this so we can utilize
|
|||
|
// a single set of nested loops to process all permutations. 'null'
|
|||
|
// values will not be output...
|
|||
|
String[] encodings = handler.getSupportedEncodings();
|
|||
|
if (encodings == null || encodings.length == 0) {
|
|||
|
encodings = new String[] { null };
|
|||
|
}
|
|||
|
String[] schemas = handler.getSupportedSchemas();
|
|||
|
if (schemas == null || schemas.length == 0) {
|
|||
|
schemas = new String[] { null };
|
|||
|
}
|
|||
|
|
|||
|
for (String format : formats) {
|
|||
|
for (String encoding : encodings) {
|
|||
|
for (String schema : schemas) {
|
|||
|
if (needDefault) {
|
|||
|
needDefault = false;
|
|||
|
describeComplexDataFormat(
|
|||
|
defaultFormatType.addNewFormat(),
|
|||
|
format, encoding, schema);
|
|||
|
}
|
|||
|
describeComplexDataFormat(
|
|||
|
supportedFormatType.addNewFormat(),
|
|||
|
format, encoding, schema);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void describeComplexDataFormat(
|
|||
|
ComplexDataDescriptionType description,
|
|||
|
Format format)
|
|||
|
{
|
|||
|
describeComplexDataFormat(description,
|
|||
|
format.getMimetype(),
|
|||
|
format.getEncoding(),
|
|||
|
format.getSchema());
|
|||
|
}
|
|||
|
|
|||
|
private void describeComplexDataFormat(ComplexDataDescriptionType description,
|
|||
|
String format,
|
|||
|
String encoding,
|
|||
|
String schema) {
|
|||
|
if ( !Strings.isNullOrEmpty(format)) {
|
|||
|
description.setMimeType(format);
|
|||
|
}
|
|||
|
if ( !Strings.isNullOrEmpty(encoding)) {
|
|||
|
description.setEncoding(encoding);
|
|||
|
}
|
|||
|
if ( !Strings.isNullOrEmpty(schema)) {
|
|||
|
description.setSchema(schema);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public boolean processDescriptionIsValid() {
|
|||
|
XmlOptions xmlOptions = new XmlOptions();
|
|||
|
List<XmlValidationError> xmlValidationErrorList = new ArrayList<XmlValidationError>();
|
|||
|
xmlOptions.setErrorListener(xmlValidationErrorList);
|
|||
|
boolean valid = getDescription().validate(xmlOptions);
|
|||
|
if (!valid) {
|
|||
|
LOGGER.error("Error validating process description for " + getClass().getCanonicalName());
|
|||
|
for (XmlValidationError xmlValidationError : xmlValidationErrorList) {
|
|||
|
LOGGER.error("\tMessage: {}", xmlValidationError.getMessage());
|
|||
|
LOGGER.error("\tLocation of invalid XML: {}",
|
|||
|
xmlValidationError.getCursorLocation().xmlText());
|
|||
|
}
|
|||
|
}
|
|||
|
return valid;
|
|||
|
}
|
|||
|
|
|||
|
protected final synchronized AlgorithmDescriptor getAlgorithmDescriptor() {
|
|||
|
if (descriptor == null) {
|
|||
|
descriptor = createAlgorithmDescriptor();
|
|||
|
}
|
|||
|
return descriptor;
|
|||
|
}
|
|||
|
|
|||
|
protected abstract AlgorithmDescriptor createAlgorithmDescriptor();
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends IData> getInputDataType(String identifier) {
|
|||
|
AlgorithmDescriptor algorithmDescriptor = getAlgorithmDescriptor();
|
|||
|
if (algorithmDescriptor != null) {
|
|||
|
return getAlgorithmDescriptor().getInputDescriptor(identifier).getBinding();
|
|||
|
} else {
|
|||
|
throw new IllegalStateException("Instance must have an algorithm descriptor");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public Class<? extends IData> getOutputDataType(String identifier) {
|
|||
|
AlgorithmDescriptor algorithmDescriptor = getAlgorithmDescriptor();
|
|||
|
if (algorithmDescriptor != null) {
|
|||
|
return getAlgorithmDescriptor().getOutputDescriptor(identifier).getBinding();
|
|||
|
} else {
|
|||
|
throw new IllegalStateException("Instance must have an algorithm descriptor");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private List observers = new ArrayList();
|
|||
|
private Object state = null;
|
|||
|
|
|||
|
@Override
|
|||
|
public Object getState() {
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public void update(Object state) {
|
|||
|
this.state = state;
|
|||
|
notifyObservers();
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public void addObserver(IObserver o) {
|
|||
|
observers.add(o);
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public void removeObserver(IObserver o) {
|
|||
|
observers.remove(o);
|
|||
|
}
|
|||
|
|
|||
|
public void notifyObservers() {
|
|||
|
Iterator i = observers.iterator();
|
|||
|
while (i.hasNext()) {
|
|||
|
IObserver o = (IObserver) i.next();
|
|||
|
o.update(this);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
List<String> errorList = new ArrayList();
|
|||
|
protected List<String> addError(String error) {
|
|||
|
errorList.add(error);
|
|||
|
return errorList;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public List<String> getErrors() {
|
|||
|
return errorList;
|
|||
|
}
|
|||
|
}
|