From a307d80a4fc2e5d919336acf10979c6340923b1b Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Fri, 28 Jul 2017 16:00:48 +0000 Subject: [PATCH] branch for release 4.6.1 git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/branches/data-analysis/52n-wps-server-d4s/3.6@151433 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 31 + .project | 23 + .settings/org.eclipse.core.resources.prefs | 5 + .settings/org.eclipse.jdt.core.prefs | 5 + .settings/org.eclipse.m2e.core.prefs | 4 + distro/LICENSE | 1 + distro/README | 69 + distro/changelog.xml | 5 + distro/descriptor.xml | 32 + distro/profile.xml | 30 + pom.xml | 196 +++ src/license/THIRD-PARTY.properties | 51 + .../wps/server/CapabilitiesConfiguration.java | 465 ++++++ .../server/CommunicationSizeLogFilter.java | 223 +++ .../org/n52/wps/server/ProcessIDRegistry.java | 103 ++ .../org/n52/wps/server/RepositoryManager.java | 345 +++++ .../org/n52/wps/server/ResponseURLFilter.java | 233 +++ .../n52/wps/server/RetrieveResultServlet.java | 260 ++++ .../n52/wps/server/WebProcessingService.java | 393 +++++ .../server/handler/DataInputInterceptors.java | 85 ++ .../wps/server/handler/RequestExecutor.java | 62 + .../wps/server/handler/RequestHandler.java | 398 +++++ .../server/request/CapabilitiesRequest.java | 159 ++ .../request/DescribeProcessRequest.java | 185 +++ .../wps/server/request/ExecuteRequest.java | 920 ++++++++++++ .../n52/wps/server/request/InputHandler.java | 1329 +++++++++++++++++ .../org/n52/wps/server/request/Request.java | 279 ++++ .../server/request/RetrieveResultRequest.java | 94 ++ .../strategy/DefaultReferenceStrategy.java | 183 +++ .../request/strategy/IReferenceStrategy.java | 39 + .../request/strategy/LazyHttpInputStream.java | 212 +++ .../strategy/ReferenceInputStream.java | 56 + .../strategy/ReferenceStrategyRegister.java | 71 + ...EmbeddedBase64OutputReferenceStrategy.java | 184 +++ .../server/response/CapabilitiesResponse.java | 68 + .../response/DescribeProcessResponse.java | 55 + .../wps/server/response/ExecuteResponse.java | 56 + .../response/ExecuteResponseBuilder.java | 381 +++++ .../wps/server/response/OutputDataItem.java | 257 ++++ .../org/n52/wps/server/response/RawData.java | 128 ++ .../org/n52/wps/server/response/Response.java | 61 + .../n52/wps/server/response/ResponseData.java | 359 +++++ .../response/RetrieveResultResponse.java | 48 + .../java/org/n52/wps/util/XMLBeansHelper.java | 130 ++ ...mmyTestClassAlgorithmInputHandlerTest.java | 148 ++ .../server/request/ExecuteRequestTest.java | 121 ++ .../wps/server/request/InputHandlerTest.java | 160 ++ ...SimpleBufferAlgorithmInputHandlerTest.java | 160 ++ .../wps/server/request/WPSConfigTestUtil.java | 61 + .../response/ExecuteResponseBuilderTest.java | 379 +++++ .../server/response/OutputDataItemTest.java | 256 ++++ .../n52/wps/server/response/RawDataTest.java | 129 ++ .../n52/wps/server/test/HttpInputTest.java | 84 ++ .../org/n52/wps/server/test/UUIDTest.java | 72 + .../resources/DTCExecuteBBOXOutputRawData.xml | 24 + .../DTCExecuteBBOXOutputResponseDoc.xml | 23 + ...DTCExecuteComplexOutputRawDataMimeTiff.xml | 19 + ...xecuteComplexOutputResponseDocMimeTiff.xml | 17 + .../DTCExecuteLiteralOutputRawData.xml | 20 + .../DTCExecuteLiteralOutputResponseDoc.xml | 19 + src/test/resources/DummyTestClass.xml | 23 + src/test/resources/InputTestExecuteSample.xml | 36 + ...LRDTCCorruptInputResponseDocStatusTrue.xml | 19 + ...TCExecuteComplexOutputResponseDocPerm1.xml | 47 + ...TCExecuteComplexOutputResponseDocPerm2.xml | 47 + .../services/org.n52.wps.server.IAlgorithm | 1 + src/test/resources/SimpleBufferAlgorithm.xml | 199 +++ .../inputhandler/generator/wps_config.xml | 1000 +++++++++++++ 68 files changed, 11337 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 distro/LICENSE create mode 100644 distro/README create mode 100644 distro/changelog.xml create mode 100644 distro/descriptor.xml create mode 100644 distro/profile.xml create mode 100644 pom.xml create mode 100644 src/license/THIRD-PARTY.properties create mode 100644 src/main/java/org/n52/wps/server/CapabilitiesConfiguration.java create mode 100644 src/main/java/org/n52/wps/server/CommunicationSizeLogFilter.java create mode 100644 src/main/java/org/n52/wps/server/ProcessIDRegistry.java create mode 100644 src/main/java/org/n52/wps/server/RepositoryManager.java create mode 100644 src/main/java/org/n52/wps/server/ResponseURLFilter.java create mode 100644 src/main/java/org/n52/wps/server/RetrieveResultServlet.java create mode 100644 src/main/java/org/n52/wps/server/WebProcessingService.java create mode 100644 src/main/java/org/n52/wps/server/handler/DataInputInterceptors.java create mode 100644 src/main/java/org/n52/wps/server/handler/RequestExecutor.java create mode 100644 src/main/java/org/n52/wps/server/handler/RequestHandler.java create mode 100644 src/main/java/org/n52/wps/server/request/CapabilitiesRequest.java create mode 100644 src/main/java/org/n52/wps/server/request/DescribeProcessRequest.java create mode 100644 src/main/java/org/n52/wps/server/request/ExecuteRequest.java create mode 100644 src/main/java/org/n52/wps/server/request/InputHandler.java create mode 100644 src/main/java/org/n52/wps/server/request/Request.java create mode 100644 src/main/java/org/n52/wps/server/request/RetrieveResultRequest.java create mode 100644 src/main/java/org/n52/wps/server/request/strategy/DefaultReferenceStrategy.java create mode 100644 src/main/java/org/n52/wps/server/request/strategy/IReferenceStrategy.java create mode 100644 src/main/java/org/n52/wps/server/request/strategy/LazyHttpInputStream.java create mode 100644 src/main/java/org/n52/wps/server/request/strategy/ReferenceInputStream.java create mode 100644 src/main/java/org/n52/wps/server/request/strategy/ReferenceStrategyRegister.java create mode 100644 src/main/java/org/n52/wps/server/request/strategy/WCS111XMLEmbeddedBase64OutputReferenceStrategy.java create mode 100644 src/main/java/org/n52/wps/server/response/CapabilitiesResponse.java create mode 100644 src/main/java/org/n52/wps/server/response/DescribeProcessResponse.java create mode 100644 src/main/java/org/n52/wps/server/response/ExecuteResponse.java create mode 100644 src/main/java/org/n52/wps/server/response/ExecuteResponseBuilder.java create mode 100644 src/main/java/org/n52/wps/server/response/OutputDataItem.java create mode 100644 src/main/java/org/n52/wps/server/response/RawData.java create mode 100644 src/main/java/org/n52/wps/server/response/Response.java create mode 100644 src/main/java/org/n52/wps/server/response/ResponseData.java create mode 100644 src/main/java/org/n52/wps/server/response/RetrieveResultResponse.java create mode 100644 src/main/java/org/n52/wps/util/XMLBeansHelper.java create mode 100644 src/test/java/org/n52/wps/server/request/DummyTestClassAlgorithmInputHandlerTest.java create mode 100644 src/test/java/org/n52/wps/server/request/ExecuteRequestTest.java create mode 100644 src/test/java/org/n52/wps/server/request/InputHandlerTest.java create mode 100644 src/test/java/org/n52/wps/server/request/SimpleBufferAlgorithmInputHandlerTest.java create mode 100644 src/test/java/org/n52/wps/server/request/WPSConfigTestUtil.java create mode 100644 src/test/java/org/n52/wps/server/response/ExecuteResponseBuilderTest.java create mode 100644 src/test/java/org/n52/wps/server/response/OutputDataItemTest.java create mode 100644 src/test/java/org/n52/wps/server/response/RawDataTest.java create mode 100644 src/test/java/org/n52/wps/server/test/HttpInputTest.java create mode 100644 src/test/java/org/n52/wps/server/test/UUIDTest.java create mode 100644 src/test/resources/DTCExecuteBBOXOutputRawData.xml create mode 100644 src/test/resources/DTCExecuteBBOXOutputResponseDoc.xml create mode 100644 src/test/resources/DTCExecuteComplexOutputRawDataMimeTiff.xml create mode 100644 src/test/resources/DTCExecuteComplexOutputResponseDocMimeTiff.xml create mode 100644 src/test/resources/DTCExecuteLiteralOutputRawData.xml create mode 100644 src/test/resources/DTCExecuteLiteralOutputResponseDoc.xml create mode 100644 src/test/resources/DummyTestClass.xml create mode 100644 src/test/resources/InputTestExecuteSample.xml create mode 100644 src/test/resources/LRDTCCorruptInputResponseDocStatusTrue.xml create mode 100644 src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm1.xml create mode 100644 src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm2.xml create mode 100644 src/test/resources/META-INF/services/org.n52.wps.server.IAlgorithm create mode 100644 src/test/resources/SimpleBufferAlgorithm.xml create mode 100644 src/test/resources/org/n52/wps/io/test/inputhandler/generator/wps_config.xml diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..b2acffc --- /dev/null +++ b/.classpath @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..d9e3b4a --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + 52n-wps-server-d4s + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..cdfe4f1 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..ec4300d --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/distro/LICENSE b/distro/LICENSE new file mode 100644 index 0000000..3695e26 --- /dev/null +++ b/distro/LICENSE @@ -0,0 +1 @@ +${gcube.license} diff --git a/distro/README b/distro/README new file mode 100644 index 0000000..47c07b4 --- /dev/null +++ b/distro/README @@ -0,0 +1,69 @@ +The gCube System - ${name} +-------------------------------------------------- + +${description} + + +${gcube.description} + +${gcube.funding} + + +Version +-------------------------------------------------- + +${version} (${buildDate}) + +Please see the file named "changelog.xml" in this directory for the release notes. + + +Authors +-------------------------------------------------- + +* Gianpaolo Coro (gianpaolo.coro-AT-isti.cnr.it), + Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo" CNR, Pisa IT + + +Maintainers +----------- + +* Gianpaolo Coro (gianpaolo.coro-AT-isti.cnr.it), + Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo" CNR, Pisa IT + + +Download information +-------------------------------------------------- + +Source code is available from SVN: + ${scm.url} + +Binaries can be downloaded from the gCube website: + ${gcube.website} + + +Installation +-------------------------------------------------- + +Installation documentation is available on-line in the gCube Wiki: + https://wiki.gcube-system.org/gcube/DataMiner_Installation + + +Documentation +-------------------------------------------------- + +Documentation is available on-line in the gCube Wiki: + https://wiki.gcube-system.org/gcube/DataMiner_Installation + + +Support +-------------------------------------------------- + +Bugs and support requests can be reported in the gCube issue tracking tool: + ${gcube.issueTracking} + + +Licensing +-------------------------------------------------- + +This software is licensed under the terms you may find in the file named "LICENSE" in this directory. + diff --git a/distro/changelog.xml b/distro/changelog.xml new file mode 100644 index 0000000..f7bad7f --- /dev/null +++ b/distro/changelog.xml @@ -0,0 +1,5 @@ + + + First Release + + \ No newline at end of file diff --git a/distro/descriptor.xml b/distro/descriptor.xml new file mode 100644 index 0000000..0eec2ec --- /dev/null +++ b/distro/descriptor.xml @@ -0,0 +1,32 @@ + + servicearchive + + tar.gz + + / + + + ${distroDirectory} + / + true + + README + LICENSE + changelog.xml + profile.xml + + 755 + true + + + + + target/${build.finalName}.${project.packaging} + /${artifactId} + + + + diff --git a/distro/profile.xml b/distro/profile.xml new file mode 100644 index 0000000..08b2fab --- /dev/null +++ b/distro/profile.xml @@ -0,0 +1,30 @@ + + + + Service + + ${project.description} + DataAnalysis + ${project.name} + 1.0.0 + + + ${project.name} + ${project.description} + ${version} + + ${project.groupId} + ${project.artifactId} + ${project.version} + + Service + + ${project.build.finalName}.${project.packaging} + + + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..adf058d --- /dev/null +++ b/pom.xml @@ -0,0 +1,196 @@ + + + + maven-parent + org.gcube.tools + 1.0.0 + + + 4.0.0 + org.gcube.dataanalysis + 52n-wps-server-gcube + 3.6.1-SNAPSHOT + 52North WPS Server - modified for gcube + Server frontend implementation (Servlet) of GET and POX binding for the 52 North WPS + + + + src/main/resources + + **/*.xml + + + + + + com.mycila + license-maven-plugin + +
../misc/licenses/license_header.txt
+
+
+ + org.jasig.maven + maven-notice-plugin + + + check-licenses + + + + +
+
+ + + org.n52.wps + 52n-wps-commons + 3.6.1 + + + org.n52.wps + 52n-wps-config + 1.2.1 + + + org.gcube.dataanalysis + 52n-wps-algorithm-gcube + [3.6.1-SNAPSHOT,3.7.0-SNAPSHOT) + + + org.n52.wps + 52n-wps-database + 3.6.1 + + + org.n52.wps + 52n-wps-io + 3.6.1 + + + org.n52.wps + 52n-wps-io-impl + 3.6.1 + + + + + javax.servlet + servlet-api + 2.5 + provided + + + javax.servlet.jsp + jsp-api + provided + 2.1 + + + + com.google.guava + guava + 13.0.1 + + + commons-collections + commons-collections + 3.2 + + + commons-codec + commons-codec + 1.5 + + + commons-httpclient + commons-httpclient + 3.1 + + + commons-io + commons-io + + 2.0 + + + commons-lang + commons-lang + 2.5 + + + org.apache.httpcomponents + httpclient + 4.2.1 + compile + + + org.apache.httpcomponents + httpcore + 4.2.1 + + + junit + junit + 4.12 + + + + org.slf4j + slf4j-api + 1.7.2 + + + org.hamcrest + hamcrest-all + 1.3 + test + + + org.n52.wps + 52n-wps-algorithm-geotools + 3.6.1 + test + + + org.n52.wps + 52n-wps-algorithm-impl + 3.6.1 + test + + + org.n52.wps + 52n-wps-io-geotools + 3.6.1 + test + + + org.slf4j + slf4j-simple + 1.7.2 + test + + + + + + + + + + + + n52-releases + 52n Releases + http://52north.org/maven/repo/releases + + true + + + false + + + +
+ diff --git a/src/license/THIRD-PARTY.properties b/src/license/THIRD-PARTY.properties new file mode 100644 index 0000000..f644252 --- /dev/null +++ b/src/license/THIRD-PARTY.properties @@ -0,0 +1,51 @@ +# Generated by org.codehaus.mojo.license.AddThirdPartyMojo +#------------------------------------------------------------------------------- +# Already used licenses in project : +# - Apache 2 +# - Apache License +# - Apache-Style with the acknowledgment clause removed +# - BSD +# - BSD License +# - BSD License for HSQL +# - Common Development and Distribution License 1.0 +# - Common Public License Version 1.0 +# - EPSG database distribution license +# - Eclipse Public License 1.0 +# - GNU GENERAL PUBLIC LICENSE Version 2, June 1991 +# - HSQLDB License +# - Lesser General Public License (LGPL) +# - Lesser General Public License (LGPL), Version 2.1 +# - MIT License +# - New BSD License +# - OGC copyright +# - Simplified BSD +# - Sun Binary Code License Agreement +# - The Apache Software License, Version 2.0 +# - The BSD 2-Clause License +# - The BSD License +# - The GNU General Public License (GPL) +#------------------------------------------------------------------------------- +# Please fill the missing licenses for dependencies : +# +# +#Wed Jan 08 13:06:02 CET 2014 +axis--axis-jaxrpc--1.4=The Apache Software License, Version 2.0 +axis--axis-saaj--1.4=The Apache Software License, Version 2.0 +axis--axis-wsdl4j--1.5.1=Common Public License Version 1.0 +commons-discovery--commons-discovery--0.2=The Apache Software License, Version 2.0 +edu.umn.gis--mapscript--6.0.3= +java3d--vecmath--1.3.2=The GNU General Public License (GPL) +javax.media--jai_codec--1.1.3=Sun Binary Code License Agreement +javax.media--jai_core--1.1.3=Sun Binary Code License Agreement +javax.media--jai_imageio--1.1=Sun Binary Code License Agreement +javax.servlet--servlet-api--2.5=Common Development and Distribution License 1.0 +jdom--jdom--1.0=Apache-Style with the acknowledgment clause removed +jgridshift--jgridshift--1.0=Lesser General Public License (LGPL), Version 2.1 +org.apache.derby--derby--10.2.2.0=The Apache Software License, Version 2.0 +org.eclipse.emf--common--2.6.0=Eclipse Public License 1.0 +org.eclipse.emf--ecore--2.6.1=Eclipse Public License 1.0 +org.eclipse.xsd--xsd--2.6.0=Eclipse Public License 1.0 +org.n52.wps--gmlpacket-2.0--0.4=GNU GENERAL PUBLIC LICENSE Version 2, June 1991 +org.n52.wps--ogckml2.2--1.0.0=GNU GENERAL PUBLIC LICENSE Version 2, June 1991 +picocontainer--picocontainer--1.2=The BSD 2-Clause License +xerces--xercesImpl--2.7.1=The Apache Software License, Version 2.0 diff --git a/src/main/java/org/n52/wps/server/CapabilitiesConfiguration.java b/src/main/java/org/n52/wps/server/CapabilitiesConfiguration.java new file mode 100644 index 0000000..14ccb7d --- /dev/null +++ b/src/main/java/org/n52/wps/server/CapabilitiesConfiguration.java @@ -0,0 +1,465 @@ +/** + * 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; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.concurrent.locks.ReentrantLock; + +import net.opengis.ows.x11.CodeType; +import net.opengis.ows.x11.DCPDocument.DCP; +import net.opengis.ows.x11.LanguageStringType; +import net.opengis.ows.x11.OperationDocument.Operation; +import net.opengis.ows.x11.RequestMethodType; +import net.opengis.wps.x100.CapabilitiesDocument; +import net.opengis.wps.x100.ProcessBriefType; +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessOfferingsDocument.ProcessOfferings; + +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.n52.wps.commons.WPSConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * Encapsulation of the WPS Capabilities document. This class has to be initialized with either a + * {@linkplain #getInstance(java.io.File) file}, {@linkplain #getInstance(java.net.URL) URL}, + * {@linkplain #getInstance(java.lang.String) path} or + * {@linkplain #getInstance(net.opengis.wps.x100.CapabilitiesDocument) instance}. + * + * @author foerster + * @author Christian Autermann + * + */ +public class CapabilitiesConfiguration { + private static final Logger LOG = LoggerFactory.getLogger(CapabilitiesConfiguration.class); + + private static final ReentrantLock lock = new ReentrantLock(); + + private static CapabilitiesDocument capabilitiesDocumentObj; + + private static CapabilitiesSkeletonLoadingStrategy loadingStrategy; + + public static String ENDPOINT_URL; + + private CapabilitiesConfiguration() { + /* nothing here */ + } + + /** + * Gets the WPS Capabilities using the specified file to obtain the skeleton. All future calls to + * {@link #getInstance()} and {@link #getInstance(boolean) + * } will use this file to obtain the skeleton. + * + * @param filePath + * the File pointing to a skeleton + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static CapabilitiesDocument getInstance(String filePath) throws XmlException, IOException { + return getInstance(new FileLoadingStrategy(filePath)); + } + + /** + * Gets the WPS Capabilities using the specified file to obtain the skeleton. All future calls to + * {@link #getInstance()} and {@link #getInstance(boolean) + * } will use this file to obtain the skeleton. + * + * @param file + * the File pointing to a skeleton + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static CapabilitiesDocument getInstance(File file) throws XmlException, IOException { + return getInstance(new FileLoadingStrategy(file)); + } + + /** + * Gets the WPS Capabilities using the specified URL to obtain the skeleton. All future calls to + * {@link #getInstance()} and {@link #getInstance(boolean) + * } will use this URL to obtain the skeleton. + * + * @param url + * the URL pointing to a skeleton + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static CapabilitiesDocument getInstance(URL url) throws XmlException, IOException { + return getInstance(new URLLoadingStrategy(url)); + } + + /** + * Gets the WPS Capabilities using the specified skeleton. All future calls to {@link #getInstance()} and + * {@link #getInstance(boolean) } will use this skeleton. + * + * @param skel + * the skeleton + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static CapabilitiesDocument getInstance(CapabilitiesDocument skel) throws XmlException, IOException { + return getInstance(new InstanceStrategy(skel)); + } + + /** + * Gets the WPS Capabilities using the specified strategy. All future calls to {@link #getInstance()} and + * {@link #getInstance(boolean) } will use this strategy. + * + * @param strategy + * the strategy to load the skeleton + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + private static CapabilitiesDocument getInstance(CapabilitiesSkeletonLoadingStrategy strategy) throws XmlException, + IOException { + Preconditions.checkNotNull(strategy); + lock.lock(); + try { + if (strategy.equals(loadingStrategy)) { + return getInstance(false); + } + else { + loadingStrategy = strategy; + return getInstance(true); + } + } + finally { + lock.unlock(); + } + } + + /** + * Get the WPS Capabilities for this service. The capabilities are reloaded if caching is not enabled in + * the WPS configuration. + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static CapabilitiesDocument getInstance() throws XmlException, IOException { + return getInstance( !WPSConfig.getInstance().getWPSConfig().getServer().getCacheCapabilites()); + } + + /** + * Get the WPS Capabilities for this service and optionally force a reload. + * + * @param reload + * if the capabilities should be reloaded + * + * @return the capabilities document + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static CapabilitiesDocument getInstance(boolean reload) throws XmlException, IOException { + lock.lock(); + try { + if (capabilitiesDocumentObj == null || reload) { + capabilitiesDocumentObj = loadingStrategy.loadSkeleton(); + initSkeleton(capabilitiesDocumentObj); + } + return capabilitiesDocumentObj; + } + finally { + lock.unlock(); + } + } + + /** + * Enriches a capabilities skeleton by adding the endpoint URL and creating the process offerings. + * + * @param skel + * the skeleton to enrich + * + * @throws UnknownHostException + * if the local host name can not be obtained + */ + private static void initSkeleton(CapabilitiesDocument skel) throws UnknownHostException { + ENDPOINT_URL = WPSConfig.getServerBaseURL() + "/" + WebProcessingService.SERVLET_PATH; + if (skel.getCapabilities() == null) { + skel.addNewCapabilities(); + } + initOperationsMetadata(skel, ENDPOINT_URL); + initProcessOfferings(skel); + } + + /** + * Enriches the capabilities skeleton by creating the process offerings. + * + * @param skel + * the skeleton to enrich + */ + private static void initProcessOfferings(CapabilitiesDocument skel) { + ProcessOfferings processes = skel.getCapabilities() + .addNewProcessOfferings(); + for (String algorithmName : RepositoryManager.getInstance() + .getAlgorithms()) { + try { + ProcessDescriptionType description = RepositoryManager + .getInstance().getProcessDescription(algorithmName); + if (description != null) { + ProcessBriefType process = processes.addNewProcess(); + CodeType ct = process.addNewIdentifier(); + ct.setStringValue(algorithmName); + LanguageStringType title = description.getTitle(); + String processVersion = description.getProcessVersion(); + process.setProcessVersion(processVersion); + process.setTitle(title); + } + } + catch (RuntimeException e) { + LOG.warn("Exception during instantiation of process {}", algorithmName, e); + } + } + } + + /** + * Enriches a capabilities skeleton by adding the endpoint URL to the operations meta data. + * + * @param skel + * the skeleton to enrich + * @param endpointUrl + * the endpoint URL of the service + * + */ + private static void initOperationsMetadata(CapabilitiesDocument skel, String endpointUrl) { + if (skel.getCapabilities().getOperationsMetadata() != null) { + String endpointUrlGet = endpointUrl + "?"; + for (Operation op : skel.getCapabilities().getOperationsMetadata().getOperationArray()) { + for (DCP dcp : op.getDCPArray()) { + for (RequestMethodType get : dcp.getHTTP().getGetArray()) { + + get.setHref(endpointUrlGet); + } + for (RequestMethodType post : dcp.getHTTP().getPostArray()) { + post.setHref(endpointUrl); + } + } + } + } + } + + /** + * Force a reload of the capabilities skeleton. + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + public static void reloadSkeleton() throws XmlException, IOException { + getInstance(true); + } + + /** + * Checks if the capabilities document is loaded. + * + * @return if the capabilities are ready. + */ + public static boolean ready() { + lock.lock(); + try { + return capabilitiesDocumentObj != null; + } + finally { + lock.unlock(); + } + } + + /** + * Strategy to load a capabilities skeleton from a URL. + */ + private static class URLLoadingStrategy implements CapabilitiesSkeletonLoadingStrategy { + private final URL url; + + /** + * Creates a new strategy using the specified URL. + * + * @param file + * the file + */ + URLLoadingStrategy(URL url) { + this.url = Preconditions.checkNotNull(url); + } + + @Override + public CapabilitiesDocument loadSkeleton() throws XmlException, IOException { + XmlOptions options = new XmlOptions().setLoadStripComments(); + return CapabilitiesDocument.Factory.parse(getUrl(), options); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof URLLoadingStrategy) { + URLLoadingStrategy that = (URLLoadingStrategy) obj; + return this.getUrl().equals(that.getUrl()); + } + return false; + } + + @Override + public int hashCode() { + return getUrl().hashCode(); + } + + /** + * Gets the URL of this strategy. + * + * @return the URL; + */ + public URL getUrl() { + return url; + } + } + + /** + * Strategy to load a capabilities skeleton from a file. + */ + private static class FileLoadingStrategy extends URLLoadingStrategy { + + /** + * Creates a new strategy using the specified file. + * + * @param file + * the file + */ + FileLoadingStrategy(File file) throws MalformedURLException { + super(file.toURI().toURL()); + } + + /** + * Creates a new strategy using the specified file. + * + * @param file + * the path to the file + */ + FileLoadingStrategy(String file) throws MalformedURLException { + this(new File(Preconditions.checkNotNull(file))); + } + + } + + /** + * Strategy to obtain the capabilities skeleton from an existing instance. + */ + private static class InstanceStrategy implements CapabilitiesSkeletonLoadingStrategy { + private final CapabilitiesDocument instance; + + /** + * Creates a new strategy using the specified instance. + * + * @param instance + * the instance + */ + InstanceStrategy(CapabilitiesDocument instance) { + this.instance = Preconditions.checkNotNull(instance); + } + + @Override + public CapabilitiesDocument loadSkeleton() { + return (CapabilitiesDocument) getInstance().copy(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof InstanceStrategy) { + InstanceStrategy that = (InstanceStrategy) obj; + return this.getInstance().equals(that.getInstance()); + } + return false; + } + + @Override + public int hashCode() { + return getInstance().hashCode(); + } + + /** + * Gets the instance of this strategy. + * + * @return the instance + */ + public CapabilitiesDocument getInstance() { + return instance; + } + } + + /** + * Strategy to load a capabilities skeleton. + */ + private interface CapabilitiesSkeletonLoadingStrategy { + /** + * Loads a CapabilitiesDocument skeleton. Every call to this method should return another instance. + * + * @return the capabilities skeleton + * + * @throws XmlException + * if the Capabilities skeleton is not valid + * @throws IOException + * if an IO error occurs + */ + CapabilitiesDocument loadSkeleton() throws XmlException, IOException; + } +} diff --git a/src/main/java/org/n52/wps/server/CommunicationSizeLogFilter.java b/src/main/java/org/n52/wps/server/CommunicationSizeLogFilter.java new file mode 100644 index 0000000..598870c --- /dev/null +++ b/src/main/java/org/n52/wps/server/CommunicationSizeLogFilter.java @@ -0,0 +1,223 @@ +/** + * 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; + +// import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.math.BigDecimal; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.io.LargeBufferStream; + + +class ResponseSizeInfoStream extends ServletOutputStream { + private OutputStream intStream; + private LargeBufferStream baStream; + private boolean closed = false; + private long streamSize = 0; + + public ResponseSizeInfoStream(OutputStream outStream) { + this.intStream = outStream; + // baStream = new ByteArrayOutputStream(); + } + + public void write(int i) throws java.io.IOException { + this.streamSize++; + this.intStream.write(i); + } + + public void close() throws java.io.IOException { + if (!this.closed) { + this.intStream.close(); + this.closed = true; + } + } + +/* public void flush() throws java.io.IOException { + if (baStream.size() != 0) { + if (! closed) { +// processStream(); // need to synchronize the flush! +// baStream = new ByteArrayOutputStream(); + } + } + } +*/ + public void processStream() throws java.io.IOException { + baStream.close(); + baStream.writeTo(intStream); + this.intStream.flush(); + } + + public byte [] countBytes(byte [] inBytes) { + //streamSize = streamSize + inBytes.length; + return inBytes; + } + + public long getSize() { + if(this.closed) { + return streamSize; + } else + return -1; + } + +} + +class ResponseSizeInfoWrapper extends HttpServletResponseWrapper { + private PrintWriter tpWriter; + private ResponseSizeInfoStream tpStream; + + public ResponseSizeInfoWrapper(ServletResponse inResp) throws java.io.IOException { + super((HttpServletResponse) inResp); + tpStream = new ResponseSizeInfoStream(inResp.getOutputStream()); + tpWriter = new PrintWriter(tpStream); + } + + public ServletOutputStream getOutputStream() throws java.io.IOException { + + return tpStream; + } + public PrintWriter getWriter() throws java.io.IOException { + + return tpWriter; + } +} + +class RequestSizeInfoStream extends ServletInputStream { + // private BufferedInputStream buStream; + private boolean closed = false; + private long streamSize = 0; + private InputStream inputStream; + + public RequestSizeInfoStream(InputStream inStream) { + this.inputStream = inStream; + // buStream = new BufferedInputStream(inStream); + } + + @Override + public int read() throws IOException { + this.streamSize++; + return this.inputStream.read(); + } + + public void close() throws java.io.IOException { + if (!this.closed) { + // processStream(); + this.inputStream.close(); + this.closed = true; + } + } + + /* public void processStream() throws IOException { + byte[] bytes = new byte[8096]; + int length = buStream.read(bytes, 0 , 8096); + while(length != -1) { + length = buStream.read(bytes, 0 , 8096); + streamSize = streamSize + length; + } + + }*/ + + public long getSize() { + if(this.closed) { + return this.streamSize; + } + else + return -1; + } +} +class RequestSizeInfoWrapper extends HttpServletRequestWrapper { + private BufferedReader tpReader; + private RequestSizeInfoStream tpStream; + + public RequestSizeInfoWrapper(ServletRequest req) throws java.io.IOException { + super((HttpServletRequest) req); + this.tpStream = new RequestSizeInfoStream(req.getInputStream()); + this.tpReader = new BufferedReader(new InputStreamReader(this.tpStream)); + } + + public ServletInputStream getInputStream() throws java.io.IOException { + return this.tpStream; + } + + public BufferedReader getReader() throws IOException { + return this.tpReader; + } +} + +/** This class measures the payload of the post data + * + * @author foerster + * + */ +public final class CommunicationSizeLogFilter implements Filter { + private static Logger LOGGER = LoggerFactory.getLogger(CommunicationSizeLogFilter.class); + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) + throws IOException, ServletException { + RequestSizeInfoWrapper myWrappedReq = new RequestSizeInfoWrapper(request); + ResponseSizeInfoWrapper myWrappedResp = new ResponseSizeInfoWrapper(response); + chain.doFilter(myWrappedReq, myWrappedResp); + myWrappedReq.getInputStream().close(); + myWrappedResp.getOutputStream().close(); + long requestSize = ((RequestSizeInfoStream)myWrappedReq.getInputStream()).getSize(); + long responseSize = ((ResponseSizeInfoStream)myWrappedResp.getOutputStream()).getSize(); + if(requestSize == 0) { + return; + } + BigDecimal result = new BigDecimal((double)responseSize/(double)requestSize).setScale(4, BigDecimal.ROUND_HALF_UP); + result = result.movePointRight(2); + LOGGER.info("Simplification ratio " + result); + } + + public void destroy() { + } + + public void init(FilterConfig filterConfig) { + } +} + diff --git a/src/main/java/org/n52/wps/server/ProcessIDRegistry.java b/src/main/java/org/n52/wps/server/ProcessIDRegistry.java new file mode 100644 index 0000000..408c2a2 --- /dev/null +++ b/src/main/java/org/n52/wps/server/ProcessIDRegistry.java @@ -0,0 +1,103 @@ +/** + * 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; + +import java.util.ArrayList; + + +/** + * @author Matthias Mueller, TU Dresden + * + */ +public class ProcessIDRegistry { + + private static ProcessIDRegistry instance = new ProcessIDRegistry(); + private volatile boolean lock = false; + private static ArrayList idList = new ArrayList(); + + private ProcessIDRegistry(){ + //empty private constructor + } + + public static ProcessIDRegistry getInstance(){ + return instance; + } + + public boolean addID(String id){ + while (lock){ + //spin + } + try{ + lock = true; + boolean retval = idList.add(id); + lock = false; + return retval; + } + finally{ + lock = false; + } + } + + public synchronized boolean removeID(String id){ + while (lock){ + //spin + } + try{ + lock = true; + boolean retval = idList.remove(id); + lock = false; + return retval; + } + finally{ + lock = false; + } + } + + public boolean containsID(String id){ + return idList.contains(id); + } + + public String[] getIDs(){ + return idList.toArray(new String[idList.size()]); + } + + protected void clearRegistry(){ + while (lock){ + //spin + } + try{ + lock = true; + idList.clear(); + lock = false; + } + finally{ + lock = false; + } + } +} diff --git a/src/main/java/org/n52/wps/server/RepositoryManager.java b/src/main/java/org/n52/wps/server/RepositoryManager.java new file mode 100644 index 0000000..41f2e0a --- /dev/null +++ b/src/main/java/org/n52/wps/server/RepositoryManager.java @@ -0,0 +1,345 @@ +/** + * 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; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.n52.wps.PropertyDocument.Property; +import org.n52.wps.RepositoryDocument.Repository; +import org.n52.wps.commons.WPSConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Bastian Schaeffer, University of Muenster + * + */ +public class RepositoryManager { + + private static RepositoryManager instance; + private static Logger LOGGER = LoggerFactory.getLogger(RepositoryManager.class); + private List repositories; + private ProcessIDRegistry globalProcessIDs = ProcessIDRegistry.getInstance(); + private UpdateThread updateThread; + + private RepositoryManager(){ + + // clear registry + globalProcessIDs.clearRegistry(); + + // initialize all Repositories + loadAllRepositories(); + + // FvK: added Property Change Listener support + // creates listener and register it to the wpsConfig instance. + WPSConfig.getInstance().addPropertyChangeListener(WPSConfig.WPSCONFIG_PROPERTY_EVENT_NAME, new PropertyChangeListener() { + public void propertyChange( + final PropertyChangeEvent propertyChangeEvent) { + LOGGER.info("Received Property Change Event: {}", + propertyChangeEvent.getPropertyName()); + loadAllRepositories(); + } + }); + + Double updateHours = WPSConfig.getInstance().getWPSConfig().getServer().getRepoReloadInterval(); + + if (updateHours != 0){ + LOGGER.info("Setting repository update period to {} hours.", updateHours); + updateHours = updateHours * 3600 * 1000; // make milliseconds + long updateInterval = updateHours.longValue(); + this.updateThread = new UpdateThread(updateInterval); + updateThread.start(); + } + + + } + + private void loadAllRepositories(){ + repositories = new ArrayList(); + LOGGER.debug("Loading all repositories: {} (doing a gc beforehand...)", repositories); + + System.gc(); + + Repository[] repositoryList = WPSConfig.getInstance().getRegisterdAlgorithmRepositories(); + + for(Repository repository : repositoryList){ + if(repository.getActive()==false){ + continue; + } + String repositoryClassName = repository.getClassName(); + try { + IAlgorithmRepository algorithmRepository = null; + Class repositoryClass = RepositoryManager.class.getClassLoader().loadClass(repositoryClassName); + Constructor[] constructors = repositoryClass.getConstructors(); + for(Constructor constructor : constructors){ + + if(constructor.getParameterTypes().length==1 && constructor.getParameterTypes()[0].equals(String.class)){ + Property[] properties = repository.getPropertyArray(); + Property formatProperty = WPSConfig.getInstance().getPropertyForKey(properties, "supportedFormat"); + String format = formatProperty.getStringValue(); + algorithmRepository = (IAlgorithmRepository) repositoryClass.getConstructor(String.class).newInstance(format); + }else{ + algorithmRepository = (IAlgorithmRepository) repositoryClass.newInstance(); + } + } + + repositories.add(algorithmRepository); + LOGGER.info("Algorithm Repository {} initialized", repositoryClassName); + } catch (InstantiationException e) { + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", repositoryClassName); + } catch (IllegalAccessException e) { + //in case of an singleton +// try { +// +// IAlgorithmRepository algorithmRepository = (IAlgorithmRepository)RepositoryManager.class.getClassLoader().loadClass(repositoryClassName).getMethod("getInstance", new Class[0]).invoke(null, new Object[0]); +// repositories.add(algorithmRepository); +// } catch (IllegalArgumentException e1) { +// LOGGER.warn("An error occured while registering AlgorithmRepository: " + repositoryClassName); +// } catch (SecurityException e1) { +// LOGGER.warn("An error occured while registering AlgorithmRepository: " + repositoryClassName); +// } catch (IllegalAccessException e1) { +// LOGGER.warn("An error occured while registering AlgorithmRepository: " + repositoryClassName); +// } catch (InvocationTargetException e1) { +// LOGGER.warn("An error occured while registering AlgorithmRepository: " + repositoryClassName); +// } catch (NoSuchMethodException e1) { +// LOGGER.warn("An error occured while registering AlgorithmRepository: " + repositoryClassName); +// } catch (ClassNotFoundException e1) { +// LOGGER.warn("An error occured while registering AlgorithmRepository: " + repositoryClassName); +// } + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", repositoryClassName); + + } catch (ClassNotFoundException e) { + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", + repositoryClassName, + e.getMessage()); + } catch (IllegalArgumentException e) { + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", + repositoryClassName, + e.getMessage()); + } catch (SecurityException e) { + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", + repositoryClassName, + e.getMessage()); + } catch (InvocationTargetException e) { + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", + repositoryClassName, + e.getMessage()); + } catch (NoSuchMethodException e) { + LOGGER.warn("An error occured while registering AlgorithmRepository: {}", + repositoryClassName, + e.getMessage()); + } + } + } + + public static RepositoryManager getInstance(){ + if(instance==null){ + instance = new RepositoryManager(); + } + return instance; + } + + /** + * Allows to reInitialize the RepositoryManager... This should not be called to often. + * + */ + public static void reInitialize() { + instance = new RepositoryManager(); + } + + /** + * Allows to reInitialize the Repositories + * + */ + protected void reloadRepositories() { + loadAllRepositories(); + } + + /** + * Methods looks for Algorithm in all Repositories. + * The first match is returned. + * If no match could be found, null is returned + * + * @param className + * @return IAlgorithm or null + * @throws Exception + */ + public IAlgorithm getAlgorithm(String className){ + for(IAlgorithmRepository repository : repositories){ + if(repository.containsAlgorithm(className)){ + return repository.getAlgorithm(className); + } + } + return null; + } + + /** + * + * @return allAlgorithms + */ + public List getAlgorithms(){ + List allAlgorithmNamesCollection = new ArrayList(); + for(IAlgorithmRepository repository : repositories){ + allAlgorithmNamesCollection.addAll(repository.getAlgorithmNames()); + } + return allAlgorithmNamesCollection; + + } + + public boolean containsAlgorithm(String algorithmName) { + for(IAlgorithmRepository repository : repositories){ + if(repository.containsAlgorithm(algorithmName)){ + return true; + } + } + return false; + } + + public IAlgorithmRepository getRepositoryForAlgorithm(String algorithmName){ + for(IAlgorithmRepository repository : repositories){ + if(repository.containsAlgorithm(algorithmName)){ + return repository; + } + } + return null; + } + + public Class getInputDataTypeForAlgorithm(String algorithmIdentifier, String inputIdentifier){ + IAlgorithm algorithm = getAlgorithm(algorithmIdentifier); + return algorithm.getInputDataType(inputIdentifier); + + } + + public Class getOutputDataTypeForAlgorithm(String algorithmIdentifier, String inputIdentifier){ + IAlgorithm algorithm = getAlgorithm(algorithmIdentifier); + return algorithm.getOutputDataType(inputIdentifier); + + } + + public boolean registerAlgorithm(String id, IAlgorithmRepository repository){ + if (globalProcessIDs.addID(id)){ + return true; + } + else return false; + } + + public boolean unregisterAlgorithm(String id){ + if (globalProcessIDs.removeID(id)){ + return true; + } + else return false; + } + + public IAlgorithmRepository getAlgorithmRepository(String name){ + for (IAlgorithmRepository repo : repositories ){ + if(repo.getClass().getName().equals(name)){ + return repo; + } + } + return null; + } + + public IAlgorithmRepository getRepositoryForClassName( + String className) { + for(IAlgorithmRepository repository : repositories){ + if(repository.getClass().getName().equals(className)){ + return repository; + } + } + return null; + } + + public ProcessDescriptionType getProcessDescription(String processClassName){ + for(IAlgorithmRepository repository : repositories){ + if(repository.containsAlgorithm(processClassName)){ + return repository.getProcessDescription(processClassName); + } + } + return null; + } + + static class UpdateThread extends Thread { + + private final long interval; + private boolean firstrun = true; + + public UpdateThread (long interval){ + this.interval = interval; + } + + @Override + public void run() { + LOGGER.debug("UpdateThread started"); + + try { + // never terminate the run method + while (true){ + // do not update on first run! + if (!firstrun){ + LOGGER.info("Reloading repositories - this might take a while ..."); + long timestamp = System.currentTimeMillis(); + RepositoryManager.getInstance().reloadRepositories(); + LOGGER.info("Repositories reloaded - going to sleep. Took {} seconds.", + (System.currentTimeMillis() - timestamp) / 1000); + } else { + firstrun = false; + } + + // sleep for a given INTERVAL + sleep(interval); + } + } catch (InterruptedException e) { + LOGGER.debug("Interrupt received - Terminating the UpdateThread."); + } + } + + } + + // shut down the update thread + public void finalize(){ + if (updateThread != null){ + updateThread.interrupt(); + } + } + + public void shutdown() { + LOGGER.debug("Shutting down all repositories.."); + for (IAlgorithmRepository repo : repositories) { + repo.shutdown(); + } + } + +} diff --git a/src/main/java/org/n52/wps/server/ResponseURLFilter.java b/src/main/java/org/n52/wps/server/ResponseURLFilter.java new file mode 100644 index 0000000..f354367 --- /dev/null +++ b/src/main/java/org/n52/wps/server/ResponseURLFilter.java @@ -0,0 +1,233 @@ +/** + * 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; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import org.n52.wps.PropertyDocument.Property; +import org.n52.wps.ServerDocument.Server; +import org.n52.wps.commons.WPSConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author tkunicki + */ +public class ResponseURLFilter implements Filter { + + private final static Logger LOGGER = LoggerFactory.getLogger(ResponseURLFilter.class); + + public final static String PROP_responseURLFilterEnabled = "responseURLFilterEnabled"; + + private String configURLString; + private boolean enabled; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + Server server = WPSConfig.getInstance().getWPSConfig().getServer(); + + // Build URL from WPS configuration. This is the + // hardcoded URL that we expect to see in reponses and would like to + // replace with the URL from the HTTP request. + configURLString = server.getProtocol() + "://" + + server.getHostname() + ":" + + server.getHostport() + "/" + + server.getWebappPath(); + + // Is filtering enabled in WPS configuration? + Property[] serverProperties = server.getPropertyArray(); + for (Property serverProperty : serverProperties) { + if (/* serverProperty.getActive() && */ PROP_responseURLFilterEnabled.equals(serverProperty.getName())) { + enabled = Boolean.parseBoolean(serverProperty.getStringValue()); + } + } + + if (enabled) { + LOGGER.info("Response URL filtering enabled using base URL of {}", configURLString); + } else { + LOGGER.info("Response URL filtering disabled."); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest requestHTTP = (request instanceof HttpServletRequest) ? + (HttpServletRequest) request : null; + HttpServletResponse responseHTTP = (response instanceof HttpServletResponse) ? + (HttpServletResponse) response : null; + + if (enabled && requestHTTP != null && responseHTTP != null) { + + String requestURLString = extractRequestURLString(requestHTTP); + + // extract servlet path from request URL + String baseURLString = requestURLString.replaceAll("/[^/]*$", ""); + + LOGGER.info("Wrapping response for URL filtering"); + chain.doFilter(request, new BaseURLFilterHttpServletResponse( + responseHTTP, configURLString, baseURLString)); + } else { + LOGGER.warn("Unable to to wrap response for URL filtering"); + chain.doFilter(request, response); + } + } + + @Override + public void destroy() { + // nothing to do yet + } + + protected static String extractRequestURLString(HttpServletRequest request) { + return request.getRequestURL().toString(); + } + + private static class BaseURLFilterHttpServletResponse extends HttpServletResponseWrapper { + + private final String configURLString; + public final String requestURLString; + + public BaseURLFilterHttpServletResponse(HttpServletResponse response, String configURLString, String requestURLString) { + super(response); + this.configURLString = configURLString; + this.requestURLString = requestURLString; + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + String contentType = getResponse().getContentType(); + if (contentType == null || contentType.startsWith("text/xml") || contentType.startsWith("application/xml")) { + LOGGER.info("Content-type: {}, response URL filtering enabled for response to {}", contentType, requestURLString); + return new ServletOutputStreamWrapper( + getResponse().getOutputStream(), + configURLString, + requestURLString); + } else { + LOGGER.info("Content-type: {}, response URL filtering disabled for response to {}", contentType, requestURLString); + return getResponse().getOutputStream(); + } + } + + @Override + public PrintWriter getWriter() throws IOException { + return new PrintWriter(getOutputStream()); + } + } + + private static class ServletOutputStreamWrapper extends ServletOutputStream { + + private final ServletOutputStream outputStream; + + private ByteBuffer find; + private ByteBuffer replace; + private boolean match; + + public ServletOutputStreamWrapper(ServletOutputStream outputStream, String find, String replace) { + this.outputStream = outputStream; + this.find = ByteBuffer.wrap(find.getBytes()); + this.replace = ByteBuffer.wrap(replace.getBytes()); + } + + @Override + public void write(int i) throws IOException { + byte b = (byte)(i & 0xff); + if (match) { + if(find.get() == b) { + if (!find.hasRemaining()) { + // COMPLETE MATCH + // 1) write out replacement buffer + // 2) unset 'match' flag + outputStream.write(replace.array()); + match = false; + } // else { /* POTENTIAL MATCH ongoing, writes deferred */ } + } else { + // FAILED MATCH + // 1) write out portion of 'find' buffer that matched + // 2) write out the current byte that caused mismatch + // 3) unset 'match' flag + outputStream.write(find.array(), 0, find.position() - 1); + outputStream.write(b); + match = false; + } + } else { + if (b == find.get(0)) { + // POTENTIAL MATCH started, write deferred + // - set 'match' flag to true for next write call + // - position 'find' buffer at next byte for next check + match = true; + find.position(1); + } else { + // NO MATCH, just pass byte through to underlying outputstream + outputStream.write(b); + } + } + } + + @Override + public void write(byte[] bytes) throws IOException { + write(bytes, 0, bytes.length); + } + + @Override + public void write(byte[] b, int o, int l) throws IOException { + for (int i = 0; i < l; ++i) { write(b[o + i]); } + } + + @Override + public void close() throws IOException { + if (match) { + // FAILED MATCH, complete deferred writes + outputStream.write(find.array(), 0, find.position()); + match = false; + } + super.close(); + outputStream.close(); + } + + @Override + public void flush() throws IOException { + super.flush(); + outputStream.flush(); + } + } +} diff --git a/src/main/java/org/n52/wps/server/RetrieveResultServlet.java b/src/main/java/org/n52/wps/server/RetrieveResultServlet.java new file mode 100644 index 0000000..e63d26f --- /dev/null +++ b/src/main/java/org/n52/wps/server/RetrieveResultServlet.java @@ -0,0 +1,260 @@ +/** + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.UUID; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.n52.wps.server.database.DatabaseFactory; +import org.n52.wps.server.database.IDatabase; +import org.n52.wps.commons.MIMEUtil; +import org.n52.wps.commons.XMLUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RetrieveResultServlet extends HttpServlet { + + private final static Logger LOGGER = LoggerFactory.getLogger(RetrieveResultServlet.class); + private static final long serialVersionUID = -268198171054599696L; + // This is required for URL generation for response documents. + public final static String SERVLET_PATH = "RetrieveResultServlet"; + // in future parameterize + private final boolean indentXML = false; + + private final int uuid_length = 36; + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // id of result to retrieve. + String id = request.getParameter("id"); + + // optional alternate name for filename (rename the file when retrieving + // if requested) + boolean altName = false; + String alternateFilename = request.getParameter("filename"); + if (!StringUtils.isEmpty(alternateFilename)) { + altName = true; + } + + // return result as attachment (instructs browser to offer user "Save" dialog) + String attachment = request.getParameter("attachment"); + + if (StringUtils.isEmpty(id)) { + errorResponse("id parameter missing", response); + } else { + + if(!isIDValid(id)){ + errorResponse("id parameter not valid", response); + } + + IDatabase db = DatabaseFactory.getDatabase(); + String mimeType = db.getMimeTypeForStoreResponse(id); + long contentLength = db.getContentLengthForStoreResponse(id); + + InputStream inputStream = null; + OutputStream outputStream = null; + try { + inputStream = db.lookupResponse(id); + + if (inputStream == null) { + errorResponse("id " + id + " is unknown to server", response); + } else if (mimeType == null) { + errorResponse("Unable to determine mime-type for id " + id, response); + } else { + String suffix = MIMEUtil.getSuffixFromMIMEType(mimeType).toLowerCase(); + + // if attachment parameter unset, default to false for mime-type of 'xml' and true for everything else. + boolean useAttachment = (StringUtils.isEmpty(attachment) && !"xml".equals(suffix)) || Boolean.parseBoolean(attachment); + if (useAttachment) { + String attachmentName = (new StringBuilder(id)).append('.').append(suffix).toString(); + + if (altName) { + attachmentName = (new StringBuilder(alternateFilename)).append('.').append(suffix).toString(); + } + response.addHeader("Content-Disposition", "attachment; filename=\"" + attachmentName + "\""); + } + + response.setContentType(mimeType); + + if ("xml".equals(suffix)) { + + // NOTE: We don't set "Content-Length" header, xml may be modified + + // need these to work around aggressive IE 8 caching. + response.addHeader("Cache-Control", "no-cache, no-store"); + response.addHeader("Pragma", "no-cache"); + response.addHeader("Expires", "-1"); + + try { + outputStream = response.getOutputStream(); + } catch (IOException e) { + throw new IOException("Error obtaining output stream for response", e); + } + copyResponseAsXML(inputStream, outputStream, useAttachment || indentXML, id); + } else { + + if (contentLength > -1) { + // Can't use response.setContentLength(...) as it accepts an int (max of 2^31 - 1) ?! + // response.setContentLength(contentLength); + response.setHeader("Content-Length", Long.toString(contentLength)); + } else { + LOGGER.warn("Content-Length unknown for response to id {}", id); + } + + try { + outputStream = response.getOutputStream(); + } catch (IOException e) { + throw new IOException("Error obtaining output stream for response", e); + } + copyResponseStream(inputStream, outputStream, id, contentLength); + } + } + } catch (Exception e) { + logException(e); + } finally { + IOUtils.closeQuietly(inputStream); + IOUtils.closeQuietly(outputStream); + } + } + } + + protected void errorResponse(String error, HttpServletResponse response) throws IOException { + response.setContentType("text/html"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + PrintWriter writer = response.getWriter(); + writer.write("Error" + error + ""); + writer.flush(); + LOGGER.warn("Error processing response: " + error); + } + + protected void copyResponseStream( + InputStream inputStream, + OutputStream outputStream, + String id, + long contentLength) throws IOException { + long contentWritten = 0; + try { + byte[] buffer = new byte[8192]; + int bufferRead; + while ((bufferRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bufferRead); + contentWritten += bufferRead; + } + } catch (IOException e) { + String exceptionMessage = contentLength > -1 + ? String.format("Error writing response to output stream for id %s, %d of %d bytes written", id, contentWritten, contentLength) + : String.format("Error writing response to output stream for id %s, %d bytes written", id, contentWritten); + throw new IOException(exceptionMessage, e); + } + LOGGER.info("{} bytes written in response to id {}", contentWritten, id); + } + + protected void copyResponseAsXML( + InputStream inputStream, + OutputStream outputStream, + boolean indent, + String id) throws IOException { + try { + XMLUtil.copyXML(inputStream, outputStream, indent); + } catch (IOException e) { + throw new IOException("Error writing XML response for id " + id, e); + } + } + + private void logException(Exception exception) { + StringBuilder errorBuilder = new StringBuilder(exception.getMessage()); + Throwable cause = getRootCause(exception); + if (cause != exception) { + errorBuilder.append(", exception message: ").append(cause.getMessage()); + } + LOGGER.error(errorBuilder.toString()); + } + + public static Throwable getRootCause(Throwable t) { + return t.getCause() == null ? t : getRootCause(t.getCause()); + } + + public boolean isIDValid(String id){ + + if(id.length() <= uuid_length){ + + try { + UUID checkUUID = UUID.fromString(id); + + if(checkUUID.toString().equals(id)){ + return true; + }else{ + return false; + } + } catch (Exception e) { + return false; + } + + }else { + + String uuidPartOne = id.substring(0, uuid_length); + String uuidPartTwo = id.substring(id.length() - uuid_length, id.length()); + + return isUUIDValid(uuidPartOne) && isUUIDValid(uuidPartTwo); + } + } + + public boolean isUUIDValid(String uuid) { + + // the following can be used to check whether the id is a valid UUID + try { + UUID checkUUID = UUID.fromString(uuid); + + if (checkUUID.toString().equals(uuid)) { + return true; + } else { + return false; + } + } catch (Exception e) { + return false; + } + } +} diff --git a/src/main/java/org/n52/wps/server/WebProcessingService.java b/src/main/java/org/n52/wps/server/WebProcessingService.java new file mode 100644 index 0000000..c036168 --- /dev/null +++ b/src/main/java/org/n52/wps/server/WebProcessingService.java @@ -0,0 +1,393 @@ +/** + * 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; + +// FvK: added Property Change Listener support +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.net.URLDecoder; +import java.util.Map; +import java.util.zip.GZIPOutputStream; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.xmlbeans.XmlException; +import org.n52.wps.GeneratorDocument.Generator; +import org.n52.wps.ParserDocument.Parser; +import org.n52.wps.PropertyDocument.Property; +import org.n52.wps.commons.WPSConfig; +import org.n52.wps.io.GeneratorFactory; +import org.n52.wps.io.ParserFactory; +import org.n52.wps.server.database.DatabaseFactory; +import org.n52.wps.server.handler.RequestHandler; +import org.n52.wps.util.XMLBeansHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This WPS supports HTTP GET for describeProcess and getCapabilities and XML-POST for execute. + * + * @author foerster + * + */ +public class WebProcessingService extends HttpServlet { + + // Universal version identifier for a Serializable class. + // Should be used here, because HttpServlet implements the java.io.Serializable + private static final long serialVersionUID = 8943233273641771839L; + public static String PROPERTY_NAME_WEBAPP_PATH = "webappPath"; + public static String BASE_DIR = null; + public static String WEBAPP_PATH = null; + public static String SERVLET_PATH = "WebProcessingService"; + public static String WPS_NAMESPACE = "http://www.opengis.net/wps/1.0.0"; + public static String DEFAULT_LANGUAGE = "en-US"; + protected static Logger LOGGER = LoggerFactory.getLogger(WebProcessingService.class); + + public final static String PROP_forceGeoToolsXYAxisOrder = "forceGeoToolsXYAxisOrder"; + + /** + * + * Returns a preconfigured OutputStream It takes care of: - caching - content-Encoding + * + * @param hsRequest + * the HttpServletRequest + * @param hsResponse + * the HttpServlerResponse + * @return the preconfigured OutputStream + * @throws IOException + * a task of the tomcat + */ + private static OutputStream getConfiguredOutputStream(HttpServletRequest hsRequest, HttpServletResponse hsResponse) throws IOException { + /* + * Forbids clients to cache the response May solve problems with proxies and bad implementations + */ + hsResponse.setHeader("Expires", "0"); + if (hsRequest.getProtocol().equals("HTTP/1.1")) { + hsResponse.setHeader("Cache-Control", "no-cache"); + } else if (hsRequest.getProtocol().equals("HTTP/1.0")) { + hsResponse.setHeader("Pragma", "no-cache"); + } + + // Enable/disable gzip compression + if (hsRequest.getHeader("Accept-Encoding") != null + && hsRequest.getHeader("Accept-Encoding").indexOf("gzip") >= 0) { + hsResponse.setHeader("Content-Encoding", "gzip"); + LOGGER.info("gzip-Compression for output enabled"); + return new GZIPOutputStream(hsResponse.getOutputStream()); + } // else { + LOGGER.info("gzip-Compression for output disabled"); + return hsResponse.getOutputStream(); + // } + } + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + + LOGGER.info("WebProcessingService initializing..."); + + try { + if (WPSConfig.getInstance(config) == null) { + LOGGER.error("Initialization failed! Please look at the properties file!"); + return; + } + } + catch (Exception e) { + LOGGER.error("Initialization failed! Please look at the properties file!", e); + return; + } + LOGGER.info("Initialization of wps properties successful!"); + + Property[] serverProps = WPSConfig.getInstance().getPropertiesForServer(); + + for (Property property : serverProps) { + if(PROP_forceGeoToolsXYAxisOrder.equals(property.getName())){ + if(Boolean.parseBoolean(property.getStringValue())){ + // this is important to set the lon lat support for correct CRS transformation. + System.setProperty("org.geotools.referencing.forceXY", "true"); + LOGGER.info("Set org.geotools.referencing.forceXY to true."); + } + break; + } + } + + BASE_DIR = this.getServletContext().getRealPath(""); + + Parser[] parsers = WPSConfig.getInstance().getActiveRegisteredParser(); + ParserFactory.initialize(parsers); + + Generator[] generators = WPSConfig.getInstance().getActiveRegisteredGenerator(); + GeneratorFactory.initialize(generators); + + // call RepositoyManager to initialize + RepositoryManager.getInstance(); + LOGGER.info("Algorithms initialized"); + + // String customWebappPath = WPSConfiguration.getInstance().getProperty(PROPERTY_NAME_WEBAPP_PATH); + String customWebappPath = WPSConfig.getInstance().getWPSConfig().getServer().getWebappPath(); + if (customWebappPath != null) { + WEBAPP_PATH = customWebappPath; + } + else { + WEBAPP_PATH = "wps"; + LOGGER.warn("No custom webapp path found, use default wps"); + } + LOGGER.info("webappPath is set to: " + customWebappPath); + + try { + CapabilitiesConfiguration.getInstance(BASE_DIR + File.separator + "config" + + File.separator + "wpsCapabilitiesSkeleton.xml"); + } + catch (IOException e) { + LOGGER.error("error while initializing capabilitiesConfiguration", e); + } + catch (XmlException e) { + LOGGER.error("error while initializing capabilitiesConfiguration", e); + } + + // Get an instance of the database for initialization of the database + DatabaseFactory.getDatabase(); + + LOGGER.info("WPS up and running!"); + + // FvK: added Property Change Listener support + // creates listener and register it to the wpsConfig instance. + // it will listen to changes of the wpsCapabilities + WPSConfig.getInstance().addPropertyChangeListener(org.n52.wps.commons.WPSConfig.WPSCAPABILITIES_SKELETON_PROPERTY_EVENT_NAME, + new PropertyChangeListener() { + @Override + public void propertyChange(final PropertyChangeEvent propertyChangeEvent) { + LOGGER.info(this.getClass().getName() + + ": Received Property Change Event: " + + propertyChangeEvent.getPropertyName()); + try { + CapabilitiesConfiguration.reloadSkeleton(); + } + catch (IOException e) { + LOGGER.error("error while initializing capabilitiesConfiguration", + e); + } + catch (XmlException e) { + LOGGER.error("error while initializing capabilitiesConfiguration", + e); + } + } + }); + + // FvK: added Property Change Listener support + // creates listener and register it to the wpsConfig instance. + // it will listen to changes of the wpsConfiguration + WPSConfig.getInstance().addPropertyChangeListener(org.n52.wps.commons.WPSConfig.WPSCONFIG_PROPERTY_EVENT_NAME, + new PropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent propertyChangeEvent) { + LOGGER.info(this.getClass().getName() + + ": Received Property Change Event: " + + propertyChangeEvent.getPropertyName()); + try { + CapabilitiesConfiguration.reloadSkeleton(); + } + catch (IOException e) { + LOGGER.error("error while initializing capabilitiesConfiguration", + e); + } + catch (XmlException e) { + LOGGER.error("error while initializing capabilitiesConfiguration", + e); + } + } + }); + + } + + protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { + try { + @SuppressWarnings("resource") + OutputStream out = res.getOutputStream(); // closed by res.flushBuffer(); + RequestHandler handler = new RequestHandler((Map) req.getParameterMap(), out); + String mimeType = handler.getResponseMimeType(); + res.setContentType(mimeType); + handler.handle(); + + res.setStatus(HttpServletResponse.SC_OK); + } + catch (ExceptionReport e) { + handleException(e, res); + } + catch (RuntimeException e) { + ExceptionReport er = new ExceptionReport("Error handing request: " + e.getMessage(), + ExceptionReport.NO_APPLICABLE_CODE, + e); + handleException(er, res); + } + finally { + if (res != null) { + res.flushBuffer(); + } + // out.flush(); + // out.close(); + } + } + + public final static int MAXIMUM_REQUEST_SIZE = 128 << 20; + public final static String SPECIAL_XML_POST_VARIABLE = "request"; + private static final String XML_CONTENT_TYPE = "text/xml"; + + protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { + BufferedReader reader = null; + + try { + String contentType = req.getContentType(); + String characterEncoding = req.getCharacterEncoding(); + if (characterEncoding == null || characterEncoding.length() == 0) { + characterEncoding = "UTF-8"; // default character encoding if unspecified + } + + int contentLength = req.getContentLength(); + if (contentLength > MAXIMUM_REQUEST_SIZE) { + LOGGER.warn("POST request rejected, request size of " + contentLength + " too large."); + ExceptionReport er = new ExceptionReport("Request body too large, limited to " + MAXIMUM_REQUEST_SIZE + + " bytes", ExceptionReport.NO_APPLICABLE_CODE); + handleException(er, res); + } + + LOGGER.debug("Received POST: Content-Type = " + contentType + ", Character-Encoding = " + characterEncoding + + ", Content-Length = " + contentLength); + + int requestSize = 0; + + StringWriter writer = contentLength > 0 ? new StringWriter(contentLength) : new StringWriter(); + reader = req.getReader(); + char[] buffer = new char[8192]; + int read; + while ( (read = reader.read(buffer)) != -1 && requestSize < MAXIMUM_REQUEST_SIZE) { + writer.write(buffer, 0, read); + requestSize += read; + } + + LOGGER.debug("POST request contained " + requestSize + " characters"); + + // Protect against denial of service attacks. + if (requestSize >= MAXIMUM_REQUEST_SIZE && reader.read() > -1) { + LOGGER.warn("POST request rejected, request size of " + requestSize + " too large."); + ExceptionReport er = new ExceptionReport("Request body too large, limited to " + MAXIMUM_REQUEST_SIZE + + " bytes", ExceptionReport.NO_APPLICABLE_CODE); + handleException(er, res); + } + + String documentString = writer.toString(); + + // Perform URL decoding, if necessary + // if ("application/x-www-form-urlencoded".equals(contentType)) { + if ( (contentType).startsWith("application/x-www-form-urlencoded")) { + if (documentString.startsWith(SPECIAL_XML_POST_VARIABLE + "=")) { + // This is a hack to permit xml to be easily submitted via a form POST. + // By convention, we are allowing users to post xml if they name it + // with a POST parameter "request" although this is not + // valid per the specification. + documentString = documentString.substring(SPECIAL_XML_POST_VARIABLE.length() + 1); + LOGGER.debug("POST request form variable removed"); + } + documentString = URLDecoder.decode(documentString, characterEncoding); + LOGGER.debug("Decoded of POST:\n" + documentString + "\n"); + } + + RequestHandler handler = new RequestHandler(new ByteArrayInputStream(documentString.getBytes("UTF-8")), + res.getOutputStream()); + String mimeType = handler.getResponseMimeType(); + res.setContentType(mimeType); + + handler.handle(); + + res.setStatus(HttpServletResponse.SC_OK); + } + catch (ExceptionReport e) { + handleException(e, res); + } + catch (Exception e) { + ExceptionReport er = new ExceptionReport("Error handing request: " + e.getMessage(), ExceptionReport.NO_APPLICABLE_CODE, e); + handleException(er, res); + } + finally { + if (res != null) { + res.flushBuffer(); + } + + if (reader != null) { + reader.close(); + } + } + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { + if (SERVLET_PATH == null) { + req.getContextPath(); + } + super.service(req, res); + } + + private static void handleException(ExceptionReport exception, HttpServletResponse res) { + res.setContentType(XML_CONTENT_TYPE); + try { + LOGGER.debug(exception.toString()); + // DO NOT MIX getWriter and getOuputStream! + exception.getExceptionDocument().save(res.getOutputStream(), + XMLBeansHelper.getXmlOptions()); + + res.setStatus(exception.getHTTPStatusCode()); + } + catch (IOException e) { + LOGGER.warn("exception occured while writing ExceptionReport to stream"); + try { + res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "error occured, while writing OWS Exception output"); + } + catch (IOException ex) { + LOGGER.error("error while writing error code to client!"); + res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + } + + @Override + public void destroy() { + super.destroy(); + DatabaseFactory.getDatabase().shutdown(); + } +} \ No newline at end of file diff --git a/src/main/java/org/n52/wps/server/handler/DataInputInterceptors.java b/src/main/java/org/n52/wps/server/handler/DataInputInterceptors.java new file mode 100644 index 0000000..e8c93ce --- /dev/null +++ b/src/main/java/org/n52/wps/server/handler/DataInputInterceptors.java @@ -0,0 +1,85 @@ +/** + * 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.handler; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Map; + +import org.n52.wps.io.data.IData; + +import net.opengis.wps.x100.InputType; + +/** + * For some algorithms it is needed to intercept + * the DataInputs before being processed. An algorithm + * should provide implementations of these through this + * interface and the corresponding annotation. + * + * @author matthes rieke + * + */ +public interface DataInputInterceptors { + + + /** + * @return a map where input identifiers are keys + */ + public Map getInterceptors(); + + + public static interface InterceptorInstance { + + /** + * applies the actual interception + * @param input the input as provided in the Execute request + * + * @return true if processed, this triggers a skip of parsing within the InputHandler + */ + public List applyInterception(InputType input); + + } + + /** + * Decorate your Algorithm implementation with this + * annotation. the value must be the fully qualified + * class name of the {@link DataInputInterceptors} implementation. + * + * @author matthes rieke + * + */ + @Retention(RetentionPolicy.RUNTIME) + public static @interface DataInputInterceptorImplementations { + + String value(); + + } + +} diff --git a/src/main/java/org/n52/wps/server/handler/RequestExecutor.java b/src/main/java/org/n52/wps/server/handler/RequestExecutor.java new file mode 100644 index 0000000..9ed1248 --- /dev/null +++ b/src/main/java/org/n52/wps/server/handler/RequestExecutor.java @@ -0,0 +1,62 @@ +/** + * 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.handler; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.n52.wps.ServerDocument.Server; +import org.n52.wps.commons.WPSConfig; + +/** + * After the client Request is accepted, it should be executed. To prevent + * resource-exhaustion, this ThreadPoolExecutor stores the Requests in a queue, + * and handles only a couple of them at a time. To tune the performance one can + * alter the parameters of this pool. + * + * Proper pool size estimation: N = Number of processors WT = Average waiting + * time of a task ST = Average service time of a task #Threads = N * (1 + WT/ST) + * + * @author Timon ter Braak + */ +public class RequestExecutor extends ThreadPoolExecutor { + + public static Server serverConfig = WPSConfig.getInstance().getWPSConfig().getServer(); + + /** + * Create a RequestExecutor. + */ + public RequestExecutor() { + super(serverConfig.getMinPoolSize().intValue(), serverConfig.getMaxPoolSize().intValue(), serverConfig.getKeepAliveSeconds().intValue(), + TimeUnit.SECONDS, new ArrayBlockingQueue( + serverConfig.getMaxQueuedTasks().intValue())); + } + +} \ No newline at end of file diff --git a/src/main/java/org/n52/wps/server/handler/RequestHandler.java b/src/main/java/org/n52/wps/server/handler/RequestHandler.java new file mode 100644 index 0000000..260e42b --- /dev/null +++ b/src/main/java/org/n52/wps/server/handler/RequestHandler.java @@ -0,0 +1,398 @@ +/** + * 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.handler; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.collections.map.CaseInsensitiveMap; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.WebProcessingService; +import org.n52.wps.server.request.CapabilitiesRequest; +import org.n52.wps.server.request.DescribeProcessRequest; +import org.n52.wps.server.request.ExecuteRequest; +import org.n52.wps.server.request.Request; +import org.n52.wps.server.request.RetrieveResultRequest; +import org.n52.wps.server.response.ExecuteResponse; +import org.n52.wps.server.response.Response; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +/** + * This class accepts client requests, determines its type and then schedules + * the {@link ExecuteRequest}'s for execution. The request is executed for a + * short time, within the client will be served with an immediate result. If the + * time runs out, the client will be served with a reference to the future + * result. The client can come back later to retrieve the result. Uses + * "computation_timeout_seconds" from wps.properties + * + * @author Timon ter Braak + */ +public class RequestHandler { + + public static final String VERSION_ATTRIBUTE_NAME = "version"; + + /** Computation timeout in seconds */ + protected static RequestExecutor pool = new RequestExecutor(); + + protected OutputStream os; + + private static Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class); + + protected String responseMimeType; + + protected Request req; + + // Empty constructor due to classes which extend the RequestHandler + protected RequestHandler() { + + } + + /** + * Handles requests of type HTTP_GET (currently capabilities and + * describeProcess). A Map is used to represent the client input. + * + * @param params + * The client input + * @param os + * The OutputStream to write the response to. + * @throws ExceptionReport + * If the requested operation is not supported + */ + public RequestHandler(Map params, OutputStream os) + throws ExceptionReport { + this.os = os; + //sleepingTime is 0, by default. + /*if(WPSConfiguration.getInstance().exists(PROPERTY_NAME_COMPUTATION_TIMEOUT)) { + this.sleepingTime = Integer.parseInt(WPSConfiguration.getInstance().getProperty(PROPERTY_NAME_COMPUTATION_TIMEOUT)); + } + String sleepTime = WPSConfig.getInstance().getWPSConfig().getServer().getComputationTimeoutMilliSeconds(); + */ + + + Request req; + CaseInsensitiveMap ciMap = new CaseInsensitiveMap(params); + + /* + * check if service parameter is present and equals "WPS" + * otherwise an ExceptionReport will be thrown + */ + String serviceType = Request.getMapValue("service", ciMap, true); + + if(!serviceType.equalsIgnoreCase("WPS")){ + throw new ExceptionReport("Parameter is not correct, expected: WPS, got: " + serviceType, + ExceptionReport.INVALID_PARAMETER_VALUE, "service"); + } + + /* + * check language. if not supported, return ExceptionReport + * Fix for https://bugzilla.52north.org/show_bug.cgi?id=905 + */ + String language = Request.getMapValue("language", ciMap, false); + + if(language != null){ + Request.checkLanguageSupported(language); + } + + // get the request type + String requestType = Request.getMapValue("request", ciMap, true); + + if (requestType.equalsIgnoreCase("GetCapabilities")) { + req = new CapabilitiesRequest(ciMap); + } + else if (requestType.equalsIgnoreCase("DescribeProcess")) { + req = new DescribeProcessRequest(ciMap); + } + else if (requestType.equalsIgnoreCase("Execute")) { + req = new ExecuteRequest(ciMap); + setResponseMimeType((ExecuteRequest)req); + } + else if (requestType.equalsIgnoreCase("RetrieveResult")) { + req = new RetrieveResultRequest(ciMap); + } + else { + throw new ExceptionReport( + "The requested Operation is not supported or not applicable to the specification: " + + requestType, + ExceptionReport.OPERATION_NOT_SUPPORTED, requestType); + } + + this.req = req; + } + + /** + * Handles requests of type HTTP_POST (currently executeProcess). A Document + * is used to represent the client input. This Document must first be parsed + * from an InputStream. + * + * @param is + * The client input + * @param os + * The OutputStream to write the response to. + * @throws ExceptionReport + */ + public RequestHandler(InputStream is, OutputStream os) + throws ExceptionReport { + String nodeName, localName, nodeURI, version = null; + Document doc; + this.os = os; + + boolean isCapabilitiesNode = false; + + try { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); + + DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance(); + fac.setNamespaceAware(true); + + // parse the InputStream to create a Document + doc = fac.newDocumentBuilder().parse(is); + + // Get the first non-comment child. + Node child = doc.getFirstChild(); + while(child.getNodeName().compareTo("#comment")==0) { + child = child.getNextSibling(); + } + nodeName = child.getNodeName(); + localName = child.getLocalName(); + nodeURI = child.getNamespaceURI(); + Node versionNode = child.getAttributes().getNamedItem("version"); + + /* + * check for service parameter. this has to be present for all requests + */ + Node serviceNode = child.getAttributes().getNamedItem("service"); + + if(serviceNode == null){ + throw new ExceptionReport("Parameter not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, "service"); + }else{ + if(!serviceNode.getNodeValue().equalsIgnoreCase("WPS")){ + throw new ExceptionReport("Parameter not specified.", ExceptionReport.INVALID_PARAMETER_VALUE, "service"); + } + } + + isCapabilitiesNode = nodeName.toLowerCase().contains("capabilities"); + if(versionNode == null && !isCapabilitiesNode) { + throw new ExceptionReport("Parameter not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, "version"); + } + //TODO: I think this can be removed, as capabilities requests do not have a version parameter (BenjaminPross) + if(!isCapabilitiesNode){ +// version = child.getFirstChild().getTextContent();//.getNextSibling().getFirstChild().getNextSibling().getFirstChild().getNodeValue(); + version = child.getAttributes().getNamedItem("version").getNodeValue(); + } + /* + * check language, if not supported, return ExceptionReport + * Fix for https://bugzilla.52north.org/show_bug.cgi?id=905 + */ + Node languageNode = child.getAttributes().getNamedItem("language"); + if(languageNode != null){ + String language = languageNode.getNodeValue(); + Request.checkLanguageSupported(language); + } + } catch (SAXException e) { + throw new ExceptionReport( + "There went something wrong with parsing the POST data: " + + e.getMessage(), + ExceptionReport.NO_APPLICABLE_CODE, e); + } catch (IOException e) { + throw new ExceptionReport( + "There went something wrong with the network connection.", + ExceptionReport.NO_APPLICABLE_CODE, e); + } catch (ParserConfigurationException e) { + throw new ExceptionReport( + "There is a internal parser configuration error", + ExceptionReport.NO_APPLICABLE_CODE, e); + } + //Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904 + if(!isCapabilitiesNode && version == null) { + throw new ExceptionReport("Parameter not specified." , ExceptionReport.MISSING_PARAMETER_VALUE, "version"); + } + if(!isCapabilitiesNode && !version.equals(Request.SUPPORTED_VERSION)) { + throw new ExceptionReport("Version not supported." , ExceptionReport.INVALID_PARAMETER_VALUE, "version"); + } + // get the request type + if (nodeURI.equals(WebProcessingService.WPS_NAMESPACE) && localName.equals("Execute")) { + req = new ExecuteRequest(doc); + setResponseMimeType((ExecuteRequest)req); + }else if (nodeURI.equals(WebProcessingService.WPS_NAMESPACE) && localName.equals("GetCapabilities")){ + req = new CapabilitiesRequest(doc); + this.responseMimeType = "text/xml"; + } else if (nodeURI.equals(WebProcessingService.WPS_NAMESPACE) && localName.equals("DescribeProcess")) { + req = new DescribeProcessRequest(doc); + this.responseMimeType = "text/xml"; + + } else if(!localName.equals("Execute")){ + throw new ExceptionReport("The requested Operation not supported or not applicable to the specification: " + + nodeName, ExceptionReport.OPERATION_NOT_SUPPORTED, localName); + } + else if(nodeURI.equals(WebProcessingService.WPS_NAMESPACE)) { + throw new ExceptionReport("specified namespace is not supported: " + + nodeURI, ExceptionReport.INVALID_PARAMETER_VALUE); + } + } + + /** + * Handle a request after its type is determined. The request is scheduled + * for execution. If the server has enough free resources, the client will + * be served immediately. If time runs out, the client will be asked to come + * back later with a reference to the result. + * + * @param req The request of the client. + * @throws ExceptionReport + */ + public void handle() throws ExceptionReport { + Response resp = null; + if(req ==null){ + throw new ExceptionReport("Internal Error",""); + } + if (req instanceof ExecuteRequest) { + // cast the request to an executerequest + ExecuteRequest execReq = (ExecuteRequest) req; + + execReq.updateStatusAccepted(); + + ExceptionReport exceptionReport = null; + try { + if (execReq.isStoreResponse()) { + resp = new ExecuteResponse(execReq); + InputStream is = resp.getAsStream(); + IOUtils.copy(is, os); + is.close(); + pool.submit(execReq); + return; + } + try { + // retrieve status with timeout enabled + try { + resp = pool.submit(execReq).get(); + } + catch (ExecutionException ee) { + LOGGER.warn("exception while handling ExecuteRequest."); + // the computation threw an error + // probably the client input is not valid + if (ee.getCause() instanceof ExceptionReport) { + exceptionReport = (ExceptionReport) ee + .getCause(); + } else { + exceptionReport = new ExceptionReport( + "An error occurred in the computation: " + + ee.getMessage(), + ExceptionReport.NO_APPLICABLE_CODE); + } + } catch (InterruptedException ie) { + LOGGER.warn("interrupted while handling ExecuteRequest."); + // interrupted while waiting in the queue + exceptionReport = new ExceptionReport( + "The computation in the process was interrupted.", + ExceptionReport.NO_APPLICABLE_CODE); + } + } finally { + if (exceptionReport != null) { + LOGGER.debug("ExceptionReport not null: " + exceptionReport.getMessage()); + // NOT SURE, if this exceptionReport is also written to the DB, if required... test please! + throw exceptionReport; + } + // send the result to the outputstream of the client. + /* if(((ExecuteRequest) req).isQuickStatus()) { + resp = new ExecuteResponse(execReq); + }*/ + else if(resp == null) { + LOGGER.warn("null response handling ExecuteRequest."); + throw new ExceptionReport("Problem with handling threads in RequestHandler", ExceptionReport.NO_APPLICABLE_CODE); + } + if(!execReq.isStoreResponse()) { + InputStream is = resp.getAsStream(); + IOUtils.copy(is, os); + is.close(); + LOGGER.info("Served ExecuteRequest."); + } + } + } catch (RejectedExecutionException ree) { + LOGGER.warn("exception handling ExecuteRequest.", ree); + // server too busy? + throw new ExceptionReport( + "The requested process was rejected. Maybe the server is flooded with requests.", + ExceptionReport.SERVER_BUSY); + } catch (Exception e) { + LOGGER.error("exception handling ExecuteRequest.", e); + if (e instanceof ExceptionReport) { + throw (ExceptionReport)e; + } + throw new ExceptionReport("Could not read from response stream.", ExceptionReport.NO_APPLICABLE_CODE); + } + } else { + // for GetCapabilities and DescribeProcess: + resp = req.call(); + try { + InputStream is = resp.getAsStream(); + IOUtils.copy(is, os); + is.close(); + } catch (IOException e) { + throw new ExceptionReport("Could not read from response stream.", ExceptionReport.NO_APPLICABLE_CODE); + } + + } + } + + protected void setResponseMimeType(ExecuteRequest req) { + if(req.isRawData()){ + responseMimeType = req.getExecuteResponseBuilder().getMimeType(); + }else{ + responseMimeType = "text/xml"; + } + + + } + + + + public String getResponseMimeType(){ + if(responseMimeType == null){ + return "text/xml"; + } + return responseMimeType.toLowerCase(); + } + + +} + + + diff --git a/src/main/java/org/n52/wps/server/request/CapabilitiesRequest.java b/src/main/java/org/n52/wps/server/request/CapabilitiesRequest.java new file mode 100644 index 0000000..2108656 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/CapabilitiesRequest.java @@ -0,0 +1,159 @@ +/** + * 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.util.ArrayList; + +import org.apache.commons.collections.map.CaseInsensitiveMap; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.handler.RequestHandler; +import org.n52.wps.server.response.CapabilitiesResponse; +import org.n52.wps.server.response.Response; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Handles a CapabilitesRequest + */ +public class CapabilitiesRequest extends Request { + + private static final String ACCEPT_VERSIONS_ELEMENT_NAME = "AcceptVersions"; + private static final String PARAM_SERVICE = "service"; + private static final String PARAM_VERSION = "version"; + private static final Object REQUEST_DOC = "document"; + + /** + * Creates a CapabilitesRequest based on a Map (HTTP_GET) + * + * @param ciMap + * The client input + * @throws ExceptionReport + */ + public CapabilitiesRequest(CaseInsensitiveMap ciMap) throws ExceptionReport { + super(ciMap); + //Fix for https://bugzilla.52north.org/show_bug.cgi?id=907 + String providedAcceptVersionsString = Request.getMapValue("acceptversions", ciMap, false); + + if (providedAcceptVersionsString != null) { + + String[] providedAcceptVersions = providedAcceptVersionsString.split(","); + + if (providedAcceptVersions != null) { + map.put("version", providedAcceptVersions); + } + } + } + + public CapabilitiesRequest(Document doc) throws ExceptionReport { + super(doc); + this.map = new CaseInsensitiveMap(); + + Node fc = this.doc.getFirstChild(); + String name = fc.getNodeName(); + this.map.put(REQUEST_DOC, name); + + Node serviceItem = fc.getAttributes().getNamedItem("service"); + if (serviceItem != null) { + String service = serviceItem.getNodeValue(); + String[] serviceArray = {service}; + + this.map.put(PARAM_SERVICE, serviceArray); + } + + NodeList nList = doc.getFirstChild().getChildNodes(); + ArrayList versionList = new ArrayList(); + + for (int i = 0; i < nList.getLength(); i++) { + Node n = nList.item(i); + if (n.getLocalName() != null) { + if (n.getLocalName().equalsIgnoreCase(ACCEPT_VERSIONS_ELEMENT_NAME)) { + + NodeList nList2 = n.getChildNodes(); + + for (int j = 0; j < nList2.getLength(); j++) { + Node n2 = nList2.item(j); + + if (n2.getLocalName() != null + && n2.getLocalName().equalsIgnoreCase(RequestHandler.VERSION_ATTRIBUTE_NAME)) { + versionList.add(n2.getTextContent()); + } + } + break; + } + } + } + + if ( !versionList.isEmpty()) { + this.map.put(PARAM_VERSION, versionList.toArray(new String[versionList.size()])); + } + + } + + /** + * Validates the client input + * + * @throws ExceptionReport + * @return True if the input is valid, False otherwise + */ + public boolean validate() throws ExceptionReport { + String services = getMapValue(PARAM_SERVICE, true); + if ( !services.equalsIgnoreCase("wps")) { + throw new ExceptionReport("Parameter is not correct, expected: WPS , got: " + services, + ExceptionReport.INVALID_PARAMETER_VALUE, "service"); + } + + String[] versions = getMapArray(PARAM_VERSION, false); + if ( !requireVersion(SUPPORTED_VERSION, false)) { + throw new ExceptionReport("Requested versions are not supported, you requested: " + + Request.accumulateString(versions), ExceptionReport.VERSION_NEGOTIATION_FAILED, "version"); + } + + return true; + } + + /** + * Actually serves the Request. + * + * @throws ExceptionReport + * @return Response The result of the computation + */ + public Response call() throws ExceptionReport { + validate(); + LOGGER.info("Handled GetCapabilitiesRequest successfully!"); + return new CapabilitiesResponse(this); + } + + /** + * Not used in this class. Returns null; + */ + public Object getAttachedResult() { + return null; + } +} diff --git a/src/main/java/org/n52/wps/server/request/DescribeProcessRequest.java b/src/main/java/org/n52/wps/server/request/DescribeProcessRequest.java new file mode 100644 index 0000000..faf2249 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/DescribeProcessRequest.java @@ -0,0 +1,185 @@ +/** + * 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.util.List; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; + +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionsDocument; + +import org.apache.commons.collections.map.CaseInsensitiveMap; +import org.apache.xmlbeans.XmlCursor; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.RepositoryManager; +import org.n52.wps.server.WebProcessingService; +import org.n52.wps.server.response.DescribeProcessResponse; +import org.n52.wps.server.response.Response; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Handles a DescribeProcessRequest + * @see Request + */ +public class DescribeProcessRequest extends Request { + + private ProcessDescriptionsDocument document; + + /** + * Creates a DescribeProcessRequest based on a Map (HTTP_GET) + * @param ciMap The client input + * @throws ExceptionReport + */ + public DescribeProcessRequest(CaseInsensitiveMap ciMap) throws ExceptionReport{ + super(ciMap); + } + + /** + * Creates a DescribeProcessRequest based on a Document (SOAP?) + * @param doc The client input + * @throws ExceptionReport + */ + public DescribeProcessRequest(Document doc) throws ExceptionReport{ + super(doc); + + //put the respective elements of the document in the map + NamedNodeMap nnm = doc.getFirstChild().getAttributes(); + + map = new CaseInsensitiveMap(); + + for (int i = 0; i < nnm.getLength(); i++) { + + Node n = nnm.item(i); + if(n.getLocalName().equalsIgnoreCase("service")){ + map.put(n.getLocalName(), new String[]{n.getNodeValue()}); + }else if(n.getLocalName().equalsIgnoreCase("version")){ + map.put(n.getLocalName(), new String[]{n.getNodeValue()}); + } + } + //get identifier + String identifierList = ""; + + NodeList nList = doc.getFirstChild().getChildNodes(); + + boolean identifierParameterExists = false; + + for (int i = 0; i < nList.getLength(); i++) { + Node n = nList.item(i); + if(n.getLocalName() != null && n.getLocalName().equalsIgnoreCase("identifier")){ + identifierParameterExists = true; + String s = n.getTextContent(); + if(s != null && !s.isEmpty()){ + identifierList = identifierList.concat(s + ","); + } + } + } + if(identifierParameterExists){ + map.put("identifier", new String[]{identifierList}); + } + } + + + /** + * Validates the client input + * @throws ExceptionReport + * @return True if the input is valid, False otherwise + */ + public boolean validate() throws ExceptionReport{ + getMapValue("version", true, new String[]{"1.0.0"}); // required + getMapValue("identifier", true); // required! + return true; + } + + public Object getAttachedResult(){ + return document; + } + + /** + * Actually serves the Request. + * @throws ExceptionReport + * @return Response The result of the computation + */ + public Response call() throws ExceptionReport { + validate(); + + document = ProcessDescriptionsDocument.Factory.newInstance(); + document.addNewProcessDescriptions(); + XmlCursor c = document.newCursor(); + c.toFirstChild(); + c.toLastAttribute(); + c.setAttributeText(new QName(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation"), "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_response.xsd"); + + String[] identifiers = getMapValue("identifier", true).split(","); + document.getProcessDescriptions().setLang(WebProcessingService.DEFAULT_LANGUAGE); + document.getProcessDescriptions().setService("WPS"); + document.getProcessDescriptions().setVersion(Request.SUPPORTED_VERSION); + + if(identifiers.length==1 && identifiers[0].equalsIgnoreCase("all")){ + List identifierList = RepositoryManager.getInstance().getAlgorithms(); + identifiers = new String[identifierList.size()]; + for(int i = 0;i returnResults; + private ExecuteResponseBuilder execRespType; + + + + /** + * Creates an ExecuteRequest based on a Document (HTTP_POST) + * + * @param doc + * The clients submission + * @throws ExceptionReport + */ + public ExecuteRequest(Document doc) throws ExceptionReport { + super(doc); + try { + XmlOptions option = new XmlOptions(); + option.setLoadTrimTextBuffer(); + this.execDom = ExecuteDocument.Factory.parse(doc, option); + if (this.execDom == null) { + LOGGER.error("ExecuteDocument is null"); + throw new ExceptionReport("Error while parsing post data", + ExceptionReport.MISSING_PARAMETER_VALUE); + } + } catch (XmlException e) { + throw new ExceptionReport("Error while parsing post data", + ExceptionReport.MISSING_PARAMETER_VALUE, e); + } + + // validate the client input + validate(); + + // create an initial response + execRespType = new ExecuteResponseBuilder(this); + + storeRequest(execDom); + } + + /* + * Creates an ExecuteRequest based on a Map (HTTP_GET). NOTE: Parameters are + * treated as non case sensitive. @param ciMap The client input @throws + * ExceptionReport + */ + public ExecuteRequest(CaseInsensitiveMap ciMap) throws ExceptionReport { + super(ciMap); + initForGET(ciMap); + // validate the client input + validate(); + + // create an initial response + execRespType = new ExecuteResponseBuilder(this); + + storeRequest(ciMap); + } + + public void getKVPDataInputs(){ + + } + + /** + * @param ciMap + */ + private void initForGET(CaseInsensitiveMap ciMap) throws ExceptionReport { + String version = getMapValue("version", ciMap, true); + if (!version.equals(Request.SUPPORTED_VERSION)) { + throw new ExceptionReport("request version is not supported: " + + version, ExceptionReport.VERSION_NEGOTIATION_FAILED); + } + this.execDom = ExecuteDocument.Factory.newInstance(); + Execute execute = execDom.addNewExecute(); + String processID = getMapValue("Identifier", true); + if (!RepositoryManager.getInstance().containsAlgorithm(processID)) { + throw new ExceptionReport("Process does not exist", + ExceptionReport.INVALID_PARAMETER_VALUE); + } + execute.addNewIdentifier().setStringValue(processID); + DataInputsType dataInputs = execute.addNewDataInputs(); + String dataInputString = getMapValue("DataInputs", true); + dataInputString = dataInputString.replace("&","&"); + String[] inputs = dataInputString.split(";"); + + // Handle data inputs + for (String inputString : inputs) { + int position = inputString.indexOf("="); + if (position == -1) { + throw new ExceptionReport("No \"=\" supplied for attribute: " + + inputString, ExceptionReport.MISSING_PARAMETER_VALUE); + } + //get name + String key = inputString.substring(0, position); + String value = null; + if (key.length() + 1 < inputString.length()) { + // BS int valueDelimiter = inputString.indexOf("@"); + int valueDelimiter = inputString.indexOf("@"); + if (valueDelimiter != -1 && position + 1 < valueDelimiter) { + value = inputString.substring(position + 1, valueDelimiter); + } else { + value = inputString.substring(position + 1); + } + } + ProcessDescriptionType description = RepositoryManager.getInstance().getProcessDescription(processID); + + if (description == null) { + throw new ExceptionReport("Data Identifier not supported: " + + key, ExceptionReport.MISSING_PARAMETER_VALUE); + } + InputDescriptionType inputDesc = XMLBeansHelper.findInputByID(key, + description.getDataInputs()); + if (inputDesc == null) { + throw new ExceptionReport("Data Identifier not supported: " + + key, ExceptionReport.MISSING_PARAMETER_VALUE); + } + InputType input = dataInputs.addNewInput(); + input.addNewIdentifier().setStringValue(key); + // prepare attributes + String encodingAttribute = null; + String mimeTypeAttribute = null; + String schemaAttribute = null; + String hrefAttribute = null; + String uom = null; + String dataType = null; + String[] inputItemstemp = inputString.split("@"); + String[] inputItems = null; + if (inputItemstemp.length == 2) { + inputItems = inputItemstemp[1].split("@"); + } else { + inputItems = inputString.split("@"); + } + if (inputItemstemp.length > 1) { + for (int i = 0; i < inputItems.length; i++) { + int attributePos = inputItems[i].indexOf("="); + if (attributePos == -1 + || attributePos + 1 >= inputItems[i].length()) { + continue; + } + String attributeName = inputItems[i].substring(0, + attributePos); + String attributeValue = inputItems[i] + .substring(attributePos + 1); + //attribute is input name + if(attributeName.equals(key)){ + continue; + } + try { + attributeValue = URLDecoder.decode(attributeValue, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new ExceptionReport("Something went wrong while trying to decode value of " + attributeName, ExceptionReport.NO_APPLICABLE_CODE, e); + } + if (attributeName.equalsIgnoreCase("encoding")) { + encodingAttribute = attributeValue; + } else if (attributeName.equalsIgnoreCase("mimeType")) { + mimeTypeAttribute = attributeValue; + } else if (attributeName.equalsIgnoreCase("schema")) { + schemaAttribute = attributeValue; + } else if (attributeName.equalsIgnoreCase("href") | attributeName.equalsIgnoreCase("xlink:href")) { + hrefAttribute = attributeValue; + } else if (attributeName.equalsIgnoreCase("uom")) { + uom = attributeValue; + } else if (attributeName.equalsIgnoreCase("datatype")) { + dataType = attributeValue; + } else { + throw new ExceptionReport( + "Attribute is not supported: " + attributeName, + ExceptionReport.INVALID_PARAMETER_VALUE); + } + + } + } + if (inputDesc.isSetComplexData()) { + // TODO: check for different attributes + // handling ComplexReference + if (!(hrefAttribute == null) && !hrefAttribute.equals("")) { + InputReferenceType reference = input.addNewReference(); + reference.setHref(hrefAttribute); + if (schemaAttribute != null) { + reference.setSchema(schemaAttribute); + } + if (mimeTypeAttribute != null) { + reference.setMimeType(mimeTypeAttribute); + } + if (encodingAttribute != null) { + reference.setEncoding(encodingAttribute); + } + + } + // Handling ComplexData + else { + ComplexDataType data = input.addNewData().addNewComplexData(); + + InputStream stream = new ByteArrayInputStream(value.getBytes()); + + try { + data.set(XmlObject.Factory.parse(stream)); + } catch (Exception e) { + LOGGER.warn("Could not parse value: " + value + " as XMLObject. Trying to create text node."); + try { + Node textNode = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().createTextNode(value); + data.set(XmlObject.Factory.parse(textNode)); + } catch (Exception e1) { + throw new ExceptionReport("Exception while trying to parse value: " + value, + ExceptionReport.NO_APPLICABLE_CODE, e1); + } + } + + if (schemaAttribute != null) { + data.setSchema(schemaAttribute); + } + if (mimeTypeAttribute != null) { + data.setMimeType(mimeTypeAttribute); + } + if (encodingAttribute != null) { + data.setEncoding(encodingAttribute); + } + } + + } else if (inputDesc.isSetLiteralData()) { + LiteralDataType data = input.addNewData().addNewLiteralData(); + if (value == null) { + throw new ExceptionReport("No value provided for literal: " + + inputDesc.getIdentifier().getStringValue(), + ExceptionReport.MISSING_PARAMETER_VALUE); + } + data.setStringValue(value); + if(uom != null){ + data.setUom(uom); + } + if(dataType != null){ + data.setDataType(dataType); + } + } else if (inputDesc.isSetBoundingBoxData()) { + BoundingBoxType data = input.addNewData().addNewBoundingBoxData(); + String[] values = value.split(","); + + if(values.length<4){ + throw new ExceptionReport("Invalid Number of BBOX Values: " + + inputDesc.getIdentifier().getStringValue(), + ExceptionReport.MISSING_PARAMETER_VALUE); + } + List lowerCorner = new ArrayList(); + lowerCorner.add(values[0]); + lowerCorner.add(values[1]); + data.setLowerCorner(lowerCorner); + + List upperCorner = new ArrayList(); + upperCorner.add(values[2]); + upperCorner.add(values[3]); + data.setUpperCorner(upperCorner); + + if(values.length>4){ + data.setCrs(values[4]); + } + + if(values.length>5){ + data.setDimensions(BigInteger.valueOf(Long.valueOf(values[5]))); + } + } + + } + // retrieve status + boolean status = false; + String statusString = getMapValue("status", false); + if (statusString != null) { + status = Boolean.parseBoolean(statusString); + } + boolean store = false; + String storeString = getMapValue("storeExecuteResponse", false); + if (storeString != null) { + store = Boolean.parseBoolean(storeString); + } + // Handle ResponseDocument option + String responseDocument = getMapValue("ResponseDocument", false); + if (responseDocument != null) { + String[] outputs = responseDocument.split(";"); + ResponseDocumentType responseDoc = execute.addNewResponseForm() + .addNewResponseDocument(); + responseDoc.setStatus(status); + responseDoc.setStoreExecuteResponse(store); + for (String outputID : outputs) { + String[] outputDataparameters = outputID.split("@"); + String outputDataInput = ""; + if (outputDataparameters.length > 0) { + outputDataInput = outputDataparameters[0]; + } else { + outputDataInput = outputID; + } + outputDataInput = outputDataInput.replace("=", ""); + ProcessDescriptionType description = RepositoryManager.getInstance().getProcessDescription(processID); + OutputDescriptionType outputDesc = XMLBeansHelper + .findOutputByID(outputDataInput, description.getProcessOutputs() + .getOutputArray()); + if (outputDesc == null) { + throw new ExceptionReport( + "Data output Identifier not supported: " + + outputDataInput, + ExceptionReport.MISSING_PARAMETER_VALUE); + } + DocumentOutputDefinitionType output = responseDoc + .addNewOutput(); + output.addNewIdentifier().setStringValue(outputDataInput); + + for (int i = 1; i < outputDataparameters.length; i++) { + int attributePos = outputDataparameters[i].indexOf("="); + if (attributePos == -1 + || attributePos + 1 >= outputDataparameters[i] + .length()) { + continue; + } + String attributeName = outputDataparameters[i].substring(0, + attributePos); + String attributeValue = outputDataparameters[i] + .substring(attributePos + 1); + try{ + attributeValue = URLDecoder.decode(attributeValue, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new ExceptionReport("Something went wrong while trying to decode value of " + attributeName, ExceptionReport.NO_APPLICABLE_CODE, e); + } + if (attributeName.equalsIgnoreCase("mimeType")) { + output.setMimeType(attributeValue); + } else if (attributeName.equalsIgnoreCase("schema")) { + output.setSchema(attributeValue); + } else if (attributeName.equalsIgnoreCase("encoding")) { + output.setEncoding(attributeValue); + + } + } + } + } + String rawData = getMapValue("RawDataOutput", false); + if (rawData != null) { + String[] rawDataparameters = rawData.split("@"); + String rawDataInput = ""; + if (rawDataparameters.length > 0) { + rawDataInput = rawDataparameters[0]; + } else { + rawDataInput = rawData; + } + ProcessDescriptionType description = RepositoryManager.getInstance().getProcessDescription(processID); + OutputDescriptionType outputDesc = XMLBeansHelper.findOutputByID( + rawDataInput, + description.getProcessOutputs().getOutputArray()); + if (outputDesc == null) { + throw new ExceptionReport( + "Data output Identifier not supported: " + rawData, + ExceptionReport.MISSING_PARAMETER_VALUE); + } + ResponseFormType responseForm = execute.addNewResponseForm(); + OutputDefinitionType output = responseForm.addNewRawDataOutput(); + output.addNewIdentifier().setStringValue( + outputDesc.getIdentifier().getStringValue()); + + if (rawDataparameters.length > 0) { + for (int i = 0; i < rawDataparameters.length; i++) { + int attributePos = rawDataparameters[i].indexOf("="); + if (attributePos == -1 + || attributePos + 1 >= rawDataparameters[i] + .length()) { + continue; + } + String attributeName = rawDataparameters[i].substring(0, + attributePos); + String attributeValue = rawDataparameters[i] + .substring(attributePos + 1); + try{ + attributeValue = URLDecoder.decode(attributeValue, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new ExceptionReport("Something went wrong while trying to decode value of " + attributeName, ExceptionReport.NO_APPLICABLE_CODE, e); + } + if (attributeName.equalsIgnoreCase("mimeType")) { + output.setMimeType(attributeValue); + } else if (attributeName.equalsIgnoreCase("schema")) { + output.setSchema(attributeValue); + } else if (attributeName.equalsIgnoreCase("encoding")) { + output.setEncoding(attributeValue); + + } else { + throw new ExceptionReport( + "Attribute is not supported: " + attributeName, + ExceptionReport.INVALID_PARAMETER_VALUE); + } + + } + } + + } + + } + + /** + * Validates the client request + * + * @return True if the input is valid, False otherwise + */ + public boolean validate() throws ExceptionReport { + // Identifier must be specified. + /* + * Only for HTTP_GET: String identifier = getMapValue("identifier"); + * + * try{ // Specifies if all complex valued output(s) of this process + * should be stored by process // as web-accessible resources store = + * getMapValue("store").equals("true"); + * // Specifies if Execute operation response shall be returned quickly + * with status information status = + * getMapValue("status").equals("true"); }catch(ExceptionReport e){ // + * if parameters "store" or "status" are not included, they default to + * false; } + * // just testing if the number of arguments is even... String[] + * diArray = getMapValue("DataInputs").split(","); if(diArray.length % 2 != + * 0) { throw new ExceptionReport("Incorrect number of arguments for + * parameter dataInputs, please only a even number of parameter values", + * ExceptionReport.INVALID_PARAMETER_VALUE); } + * + */ + if (!execDom.getExecute().getVersion().equals(SUPPORTED_VERSION)) { + throw new ExceptionReport("Specified version is not supported.", + ExceptionReport.INVALID_PARAMETER_VALUE, "version=" + + getExecute().getVersion()); + } + + //Fix for bug https://bugzilla.52north.org/show_bug.cgi?id=906 + String identifier = getAlgorithmIdentifier(); + + if(identifier == null){ + throw new ExceptionReport( + "No process identifier supplied.", + ExceptionReport.MISSING_PARAMETER_VALUE, "identifier"); + } + + // check if the algorithm is in our repository + if (!RepositoryManager.getInstance().containsAlgorithm( + identifier)) { + throw new ExceptionReport( + "Specified process identifier does not exist", + ExceptionReport.INVALID_PARAMETER_VALUE, + "identifier=" + identifier); + } + + // validate if the process can be executed + ProcessDescriptionType desc = RepositoryManager.getInstance().getProcessDescription(getAlgorithmIdentifier()); + // We need a description of the inputs for the algorithm + if (desc == null) { + LOGGER.warn("desc == null"); + return false; + } + + // Get the inputdescriptions of the algorithm + + if(desc.getDataInputs()!=null){ + InputDescriptionType[] inputDescs = desc.getDataInputs().getInputArray(); + + //prevent NullPointerException for zero input values in execute request (if only default values are used) + InputType[] inputs; + if(getExecute().getDataInputs()==null) + inputs=new InputType[0]; + else + inputs = getExecute().getDataInputs().getInputArray(); + + // For each input supplied by the client + for (InputType input : inputs) { + boolean identifierMatched = false; + // Try to match the input with one of the descriptions + for (InputDescriptionType inputDesc : inputDescs) { + // If found, then process: + if (inputDesc.getIdentifier().getStringValue().equals( + input.getIdentifier().getStringValue())) { + identifierMatched = true; + // If it is a literal value, + if (input.getData() != null + && input.getData().getLiteralData() != null) { + // then check if the desription is also of type literal + if (inputDesc.getLiteralData() == null) { + throw new ExceptionReport( + "Inputtype LiteralData is not supported", + ExceptionReport.INVALID_PARAMETER_VALUE); + } + // literalValue.getDataType ist optional + if (input.getData().getLiteralData().getDataType() != null) { + if (inputDesc.getLiteralData() != null) + if (inputDesc.getLiteralData().getDataType() != null) + if (inputDesc.getLiteralData() + .getDataType().getReference() != null) + if (!input + .getData() + .getLiteralData() + .getDataType() + .equals( + inputDesc + .getLiteralData() + .getDataType() + .getReference())) { + throw new ExceptionReport( + "Specified dataType is not supported " + + input + .getData() + .getLiteralData() + .getDataType() + + " for input " + + input + .getIdentifier() + .getStringValue(), + ExceptionReport.INVALID_PARAMETER_VALUE); + } + } + } + // Excluded, because ProcessDescription validation should be + // done on startup! + // else if (input.getComplexValue() != null) { + // if(ParserFactory.getInstance().getParser(input.getComplexValue().getSchema()) + // == null) { + // LOGGER.warn("Request validation message: schema attribute + // null, so the simple one will be used!"); + // } + // } + // else if (input.getComplexValueReference() != null) { + // // we found a complexvalue input, try to get the parser. + // if(ParserFactory.getInstance().getParser(input.getComplexValueReference().getSchema()) + // == null) { + // LOGGER.warn("Request validation message: schema attribute + // null, so the simple one will be used!"); + // } + // } + break; + } + } + // if the identifier did not match one of the descriptions, it is + // invalid + if (!identifierMatched) { + throw new ExceptionReport("Input Identifier is not valid: " + + input.getIdentifier().getStringValue(), + ExceptionReport.INVALID_PARAMETER_VALUE, + "input identifier"); + } + } + } + return true; + } + + /** + * Actually serves the Request. + * + * @throws ExceptionReport + */ + public Response call() throws ExceptionReport { + IAlgorithm algorithm = null; + Map> inputMap = null; + try { + ExecutionContext context; + if (getExecute().isSetResponseForm()) { + context = getExecute().getResponseForm().isSetRawDataOutput() ? + new ExecutionContext(getExecute().getResponseForm().getRawDataOutput()) : + new ExecutionContext(Arrays.asList(getExecute().getResponseForm().getResponseDocument().getOutputArray())); + } + else { + context = new ExecutionContext(); + } + + // register so that any function that calls ExecuteContextFactory.getContext() gets the instance registered with this thread + ExecutionContextFactory.registerContext(context); + + LOGGER.debug("started with execution"); + + updateStatusStarted(); + + // parse the input + InputType[] inputs = new InputType[0]; + if( getExecute().getDataInputs()!=null){ + inputs = getExecute().getDataInputs().getInputArray(); + } + InputHandler parser = new InputHandler.Builder(inputs, getAlgorithmIdentifier()).build(); + + // we got so far: + // get the algorithm, and run it with the clients input + + /* + * IAlgorithm algorithm = + * RepositoryManager.getInstance().getAlgorithm(getAlgorithmIdentifier()); + * returnResults = algorithm.run((Map)parser.getParsedInputLayers(), + * (Map)parser.getParsedInputParameters()); + */ + algorithm = RepositoryManager.getInstance().getAlgorithm(getAlgorithmIdentifier()); + + if(algorithm instanceof ISubject){ + ISubject subject = (ISubject) algorithm; + subject.addObserver(this); + + } + + if(algorithm instanceof AbstractTransactionalAlgorithm){ + returnResults = ((AbstractTransactionalAlgorithm)algorithm).run(execDom); + } else { + inputMap = parser.getParsedInputData(); + returnResults = algorithm.run(inputMap); + } + + List errorList = algorithm.getErrors(); + if (errorList != null && !errorList.isEmpty()) { + String errorMessage = errorList.get(0); + LOGGER.error("Error reported while handling ExecuteRequest for " + getAlgorithmIdentifier() + ": " + errorMessage); + updateStatusError(errorMessage); + } else { + updateStatusSuccess(); + } + } catch(Throwable e) { + String errorMessage = null; + if (algorithm != null && algorithm.getErrors() != null && !algorithm.getErrors().isEmpty()) { + errorMessage = algorithm.getErrors().get(0); + } + if (errorMessage == null) { + errorMessage = e.toString(); + } + if (errorMessage == null) { + errorMessage = "UNKNOWN ERROR"; + } + LOGGER.error("Exception/Error while executing ExecuteRequest for " + getAlgorithmIdentifier() + ": " + errorMessage); + updateStatusError(errorMessage); + if (e instanceof Error) { + // This is required when catching Error + throw (Error)e; + } + if (e instanceof ExceptionReport) { + throw (ExceptionReport)e; + } else { + throw new ExceptionReport("Error while executing the embedded process for: " + getAlgorithmIdentifier(), ExceptionReport.NO_APPLICABLE_CODE, e); + } + } finally { + // you ***MUST*** call this or else you will have a PermGen ClassLoader memory leak due to ThreadLocal use + ExecutionContextFactory.unregisterContext(); + if (algorithm instanceof ISubject) { + ((ISubject)algorithm).removeObserver(this); + } + if (inputMap != null) { + for(List l : inputMap.values()) { + for (IData d : l) { + if (d instanceof IComplexData) { + ((IComplexData)d).dispose(); + } + } + } + } + if (returnResults != null) { + for (IData d : returnResults.values()) { + if (d instanceof IComplexData) { + ((IComplexData)d).dispose(); + } + } + } + } + + ExecuteResponse response = new ExecuteResponse(this); + return response; + } + + + /** + * Gets the identifier of the algorithm the client requested + * + * @return An identifier + */ + public String getAlgorithmIdentifier() { + //Fix for bug https://bugzilla.52north.org/show_bug.cgi?id=906 + if(getExecute().getIdentifier() != null){ + return getExecute().getIdentifier().getStringValue(); + } + return null; + } + + /** + * Gets the Execute that is associated with this Request + * + * @return The Execute + */ + public Execute getExecute() { + return execDom.getExecute(); + } + + public Map getAttachedResult() { + return returnResults; + } + + public boolean isStoreResponse() { + if (execDom.getExecute().getResponseForm() == null) { + return false; + } + if (execDom.getExecute().getResponseForm().getRawDataOutput() != null) { + return false; + } + return execDom.getExecute().getResponseForm().getResponseDocument() + .getStoreExecuteResponse(); + } + + public boolean isQuickStatus() { + if (execDom.getExecute().getResponseForm() == null) { + return false; + } + if (execDom.getExecute().getResponseForm().getRawDataOutput() != null) { + return false; + } + return execDom.getExecute().getResponseForm().getResponseDocument() + .getStatus(); + } + + public ExecuteResponseBuilder getExecuteResponseBuilder() { + return this.execRespType; + } + + public boolean isRawData() { + if (execDom.getExecute().getResponseForm() == null) { + return false; + } + if (execDom.getExecute().getResponseForm().getRawDataOutput() != null) { + return true; + } else { + return false; + } + } + + + public void update(ISubject subject) { + Object state = subject.getState(); + LOGGER.info("Update received from Subject, state changed to : " + state); + StatusType status = StatusType.Factory.newInstance(); + + int percentage = 0; + if (state instanceof Integer) { + percentage = (Integer) state; + status.addNewProcessStarted().setPercentCompleted(percentage); + }else if(state instanceof String){ + status.addNewProcessStarted().setStringValue((String)state); + } + updateStatus(status); + } + + public void updateStatusAccepted() { + StatusType status = StatusType.Factory.newInstance(); + status.setProcessAccepted("Process Accepted"); + updateStatus(status); + } + + public void updateStatusStarted() { + StatusType status = StatusType.Factory.newInstance(); + status.addNewProcessStarted().setPercentCompleted(0); + updateStatus(status); + } + + public void updateStatusSuccess() { + StatusType status = StatusType.Factory.newInstance(); + status.setProcessSucceeded("Process successful"); + updateStatus(status); + } + + public void updateStatusError(String errorMessage) { + StatusType status = StatusType.Factory.newInstance(); + net.opengis.ows.x11.ExceptionReportDocument.ExceptionReport excRep = status + .addNewProcessFailed().addNewExceptionReport(); + excRep.setVersion("1.0.0"); + ExceptionType excType = excRep.addNewException(); + excType.addNewExceptionText().setStringValue(errorMessage); + excType.setExceptionCode(ExceptionReport.NO_APPLICABLE_CODE); + updateStatus(status); + } + + private void updateStatus(StatusType status) { + getExecuteResponseBuilder().setStatus(status); + try { + getExecuteResponseBuilder().update(); + if (isStoreResponse()) { + ExecuteResponse executeResponse = new ExecuteResponse(this); + InputStream is = null; + try { + is = executeResponse.getAsStream(); + DatabaseFactory.getDatabase().storeResponse( + getUniqueId().toString(), is); + } finally { + IOUtils.closeQuietly(is); + } + } + } catch (ExceptionReport e) { + LOGGER.error("Update of process status failed.", e); + throw new RuntimeException(e); + } + } + + private void storeRequest(ExecuteDocument executeDocument) { + InputStream is = null; + try { + is = executeDocument.newInputStream(); + DatabaseFactory.getDatabase().insertRequest( + getUniqueId().toString(), is, true); + } catch (Exception e) { + LOGGER.error("Exception storing ExecuteRequest", e); + } finally { + IOUtils.closeQuietly(is); + } + } + + private void storeRequest(CaseInsensitiveMap map) { + + BufferedWriter w = null; + ByteArrayOutputStream os = null; + ByteArrayInputStream is = null; + try { + os = new ByteArrayOutputStream(); + w = new BufferedWriter(new OutputStreamWriter(os)); + for (Object key : map.keySet()) { + Object value = map.get(key); + String valueString = ""; + if(value instanceof String[]){ + valueString = ((String[])value)[0]; + }else{ + valueString = value.toString(); + } + w.append(key.toString()).append('=').append(valueString); + w.newLine(); + } + w.flush(); + is = new ByteArrayInputStream(os.toByteArray()); + DatabaseFactory.getDatabase().insertRequest( + getUniqueId().toString(), is, false); + } catch (Exception e) { + LOGGER.error("Exception storing ExecuteRequest", e); + } finally { + IOUtils.closeQuietly(w); + IOUtils.closeQuietly(os); + IOUtils.closeQuietly(is); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/n52/wps/server/request/InputHandler.java b/src/main/java/org/n52/wps/server/request/InputHandler.java new file mode 100644 index 0000000..f031659 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/InputHandler.java @@ -0,0 +1,1329 @@ +/** + * 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> inputData = new HashMap>(); + 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 inputInterceptors = resolveInputInterceptors(algorithmIdentifier); + + for (InputType input : builder.inputs) { + String inputId = input.getIdentifier().getStringValue().trim(); + if (inputInterceptors.containsKey(inputId)) { + InterceptorInstance interceptor = inputInterceptors.get(inputId); + List 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 resolveInputInterceptors(String algorithmClassName) { + Map result = new HashMap(); + 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 foundEncodingList = new ArrayList(); + 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 foundSchemaList = new ArrayList(); + 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 list = new ArrayList(); + 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(""); + complexValueCopy = complexValueCopy.substring(startIndex + 1); + + int endIndex = complexValueCopy.indexOf(" list = inputData.get(inputID); + list.add(parameterObj); + } + else { + List list = new ArrayList(); + 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("&", ""); + 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 foundEncodingList = new ArrayList(); + 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 foundSchemaList = new ArrayList(); + 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("&", "&"); + 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 list = inputData.get(inputID); + list.add(parsedInputData); + inputData.put(inputID, list); + } + else { + List list = new ArrayList(); + 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 resultList = inputData.get(input.getIdentifier() + .getStringValue()); + if (resultList == null) { + inputData.put(input.getIdentifier().getStringValue(), resultList + = new ArrayList(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 coordinate = new ArrayList(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> 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(); + // } + // } +} diff --git a/src/main/java/org/n52/wps/server/request/Request.java b/src/main/java/org/n52/wps/server/request/Request.java new file mode 100644 index 0000000..484d2f2 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/Request.java @@ -0,0 +1,279 @@ +/** + * 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.util.UUID; +import java.util.concurrent.Callable; + +import org.apache.commons.collections.map.CaseInsensitiveMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.response.Response; +import org.w3c.dom.Document; + +/** + * The client requests some operation from the server. + * The request generates a unique reference based on the client, time and a count. + * Not secure! Upcoming references are easily guessed or altered. + * @see java.rmi.server.UID + */ +abstract public class Request implements Callable { + + protected CaseInsensitiveMap map = null; + protected Document doc = null; + protected static Logger LOGGER = LoggerFactory.getLogger(Request.class); + protected UUID id = null; + public static final String SUPPORTED_VERSION = "1.0.0"; + public static final String[] SUPPORTED_LANGUAGES = new String[]{"en-US"}; + + /** + * Create a Request based on a CaseInsensitiveMap as input (HTTP GET) + * @param map The Map which holds the client input. + */ + public Request(CaseInsensitiveMap map) throws ExceptionReport{ + super(); + this.map = map; + } + + /** + * Create a Request based on a Document as input (HTTP POST) + * @param doc The Document which holds the client input. + */ + public Request(Document doc) throws ExceptionReport{ + super(); + this.doc = doc; + } + + /** + * Returns the user input in Document form + * @return Document || null if Request(Map, outputstream) was used + */ + public Document getDocument(){ + return doc; + } + + /** + * Returns the user input in Map form + * @return Map || null if Request(Document, OutputStream) was used + */ + public CaseInsensitiveMap getMap(){ + return map; + } + + /** + * Retrieve a value from an input-map with a lookup-key + * @param key The lookup-key + * @param map The input-map to look in + * @param required If the key-value pair must be in the map. + * @return The value of the key-value pair + */ + public static String getMapValue(String key, CaseInsensitiveMap map, boolean required) throws ExceptionReport{ + if(map.containsKey(key)){ + return ((String[]) map.get(key))[0]; + }else if(!required){ + LOGGER.warn("Parameter <" + key + "> not found."); + return null; + }else{ + //Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904 + throw new ExceptionReport("Parameter <" + key + "> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, key); + } + } + + /** + * Retrieve a value from an input-map with a lookup-key + * @param key The lookup-key + * @param map The input-map to look in + * @param required If the key-value pair must be in the map. + * @return The value of the key-value pair + */ + public static String getMapValue(String key, CaseInsensitiveMap map, boolean required, String[] supportedValues) throws ExceptionReport{ + if(map.containsKey(key)){ + + String value = ((String[]) map.get(key))[0]; + + for (String string : supportedValues) { + if(string.equalsIgnoreCase(value)){ + return value; + } + } + throw new ExceptionReport("Invalid value for parameter <" + key + ">.", ExceptionReport.INVALID_PARAMETER_VALUE, key); + }else if(!required){ + LOGGER.warn("Parameter <" + key + "> not found."); + return null; + }else{ + //Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904 + throw new ExceptionReport("Parameter <" + key + "> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, key); + } + } + + /** + * Retrieve an array of values from an input-map with a lookup-key + * @param key The lookup-key + * @param map The input-map to look in + * @param required If the key-value pair must be in the map. + * @return The array of values of the key-value pair + */ + public static String[] getMapArray(String key, CaseInsensitiveMap map, boolean required) throws ExceptionReport{ + if(map.containsKey(key)){ + return (String[]) map.get(key); + }else if(!required){ + LOGGER.warn("Parameter <" + key + "> not found."); + return null; + }else{ + //Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904 + throw new ExceptionReport("Parameter <" + key + "> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, key); + } + } + + /** + * Retrieve a value from the client-input-map with a lookup-key + * @param The lookup-key + * @return The value of the key-value pair + */ + protected String getMapValue(String key, boolean required) throws ExceptionReport{ + return Request.getMapValue(key, this.map, required); + } + + /** + * Retrieve a value from the client-input-map with a lookup-key + * @param The lookup-key + * @return The value of the key-value pair + */ + protected String getMapValue(String key, boolean required, String[] supportedValues) throws ExceptionReport{ + return Request.getMapValue(key, this.map, required, supportedValues); + } + + /** + * Retrieve an array of values from the client-input-map with a lookup-key + * @param The lookup-key + * @return The array of values of the key-value pair + */ + protected String[] getMapArray(String key, boolean required) throws ExceptionReport{ + return Request.getMapArray(key, this.map, required); + } + + /** + * Returns the version that the client requested. + * @return An array of versions that are compatible with the client + */ + protected String[] getRequestedVersions(boolean mandatory) throws ExceptionReport{ + return getMapArray("version", mandatory); + + } + + /** + * The process (request) on the server could require a specific version on the client + * @param version The version that is required on the client + * @return True if the required version matches, False otherwise. + */ + public boolean requireVersion(String version, boolean mandatory) throws ExceptionReport{ + String[] versions = getRequestedVersions(mandatory); + if(mandatory && versions == null) { + //Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904 + throw new ExceptionReport("Parameter not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, "version"); + } + else if(versions == null && ! mandatory) { + return true; + } + for(String v : versions) { + //remove possible blanks + if(v.trim().equals(version)) { + return true; + } + } + return false; + } + + /** + * Accumulates the Strings in an array, separated by ", " (without quotes). + * @param strings The array to accumulate + * @return The accumulated String + */ + public static String accumulateString(String[] strings) { + StringBuffer sb = new StringBuffer(); + for(int i = 0; i < strings.length; i++) { + String s = strings[i]; + if(!(i == strings.length-1)){ + sb.append(s + ", "); + }else{ + sb.append(s); + } + } + return sb.toString(); + } + + public UUID getUniqueId(){ + if (id == null) { + this.id = UUID.randomUUID(); + } + return id; + } + + /** + * Checks, if the language is supported by the WPS. + * The language parameter is optional, however, if a wrong language is requested, + * an ExceptionReport has to be returned to the client. + * + * See https://bugzilla.52north.org/show_bug.cgi?id=905. + * + * @param language The language to be checked. + * @throws ExceptionReport If a wrong language is requested, this ExceptionReport will be returned to the client. + */ + public static void checkLanguageSupported(String language) throws ExceptionReport { + + for (String supportedLanguage : SUPPORTED_LANGUAGES) { + if(supportedLanguage.equals(language)){ + return; + } + } + throw new ExceptionReport( + "The requested language " + language + " is not supported", + ExceptionReport.INVALID_PARAMETER_VALUE, "language"); + } + + abstract public Object getAttachedResult(); + + /** + * After creation a Request is handled. This is done by calling this method. + * This handling could contain a lot of computations. These computations should + * be called from within this method. + * @return A Response to the client Request + * @see java.util.concurrent.Callable#call() + */ + abstract public Response call() throws ExceptionReport; + + /** + * There should be some validation required on the (input of the) clients Request. + * @return True if the clients Request can be handled without problems, False otherwise + */ + abstract public boolean validate() throws ExceptionReport; + +} + \ No newline at end of file diff --git a/src/main/java/org/n52/wps/server/request/RetrieveResultRequest.java b/src/main/java/org/n52/wps/server/request/RetrieveResultRequest.java new file mode 100644 index 0000000..d2db53c --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/RetrieveResultRequest.java @@ -0,0 +1,94 @@ +/** + * 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.InputStream; + +import org.apache.commons.collections.map.CaseInsensitiveMap; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.database.IDatabase; +import org.n52.wps.server.database.DatabaseFactory; +import org.n52.wps.server.response.Response; +import org.n52.wps.server.response.RetrieveResultResponse; + +/** + * If the server is too busy or the computation is too long, + * the client can retrieve the result at a later time. + * This class represents a Request to obtain the result. + * The respons might be the result, be also could contain a + * message that the result is not available yet. + */ +public class RetrieveResultRequest extends Request { + + private InputStream storedResponse = null; + + /** + * Create a Request based on a CaseInsensitiveMap as input (HTTP GET) + * @param ciMap The Map which holds the client input. + */ + public RetrieveResultRequest(CaseInsensitiveMap ciMap) throws ExceptionReport{ + super(ciMap); + } + + /** + * Actually serves the Request. + * @throws ExceptionReport + */ + public Response call() throws ExceptionReport { + if(validate()){ + return new RetrieveResultResponse(this); + } + return null; + } + + /** + * Validates the client input + * @return True if the input is valid, False otherwise + */ + public boolean validate() throws ExceptionReport { + String req_id = getMapValue("request_id", true); + if(req_id.length() == 0){ + throw new ExceptionReport("The value of parameter is not valid.", ExceptionReport.INVALID_PARAMETER_VALUE); + } + try{ + }catch(NumberFormatException e){ + throw new ExceptionReport("The value of parameter is not an integer identifier", ExceptionReport.INVALID_PARAMETER_VALUE); + } + IDatabase db = DatabaseFactory.getDatabase(); + this.storedResponse = db.lookupResponse(req_id); + return (this.storedResponse != null); + } + + public Object getAttachedResult() throws NullPointerException { + if(this.storedResponse == null) + throw new NullPointerException("No stored responses were found!"); + return this.storedResponse; + } + +} diff --git a/src/main/java/org/n52/wps/server/request/strategy/DefaultReferenceStrategy.java b/src/main/java/org/n52/wps/server/request/strategy/DefaultReferenceStrategy.java new file mode 100644 index 0000000..951f061 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/strategy/DefaultReferenceStrategy.java @@ -0,0 +1,183 @@ +/** + * 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.strategy; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.MalformedURLException; + +import net.opengis.wps.x100.InputType; + +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DecompressingHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.server.ExceptionReport; + +/** + * + * @author Matthias Mueller + * + * Basic methods to retrieve input data using HTTP/GET, HTTP/POST or HTTP/POST with href'd body + * + */ +public class DefaultReferenceStrategy implements IReferenceStrategy{ + + // TODO: follow HTTP redirects with LaxRedirectStrategy + + Logger logger = LoggerFactory.getLogger(DefaultReferenceStrategy.class); + + //TODO: get proxy from config + //static final HttpHost proxy = new HttpHost("127.0.0.1", 8080, "http"); + static final HttpHost proxy = null; + + @Override + public boolean isApplicable(InputType input) { + // TODO Auto-generated method stub + return true; + } + + // TODO: follow references, e..g + + @Override + public ReferenceInputStream fetchData(InputType input) throws ExceptionReport { + + String href = input.getReference().getHref(); + String mimeType = input.getReference().getMimeType(); + + try { + // Handling POST with referenced document + if(input.getReference().isSetBodyReference()) { + + String bodyHref = input.getReference().getBodyReference().getHref(); + + // but Body reference into a String + StringWriter writer = new StringWriter(); + IOUtils.copy(httpGet(bodyHref, null), writer); + String body = writer.toString(); + + // trigger POST request + return httpPost(href, body, mimeType); + + } + + // Handle POST with inline message + else if (input.getReference().isSetBody()) { + String body = input.getReference().getBody().toString(); + return httpPost(href, body, mimeType); + } + + // Handle get request + else { + return httpGet(href, mimeType); + } + + + } + catch(RuntimeException e) { + throw new ExceptionReport("Error occured while parsing XML", + ExceptionReport.NO_APPLICABLE_CODE, e); + } + catch(MalformedURLException e) { + String inputID = input.getIdentifier().getStringValue(); + throw new ExceptionReport("The inputURL of the execute is wrong: inputID: " + inputID + " | dataURL: " + href, + ExceptionReport.INVALID_PARAMETER_VALUE ); + } + catch(IOException e) { + String inputID = input.getIdentifier().getStringValue(); + throw new ExceptionReport("Error occured while receiving the complexReferenceURL: inputID: " + inputID + " | dataURL: " + href, + ExceptionReport.INVALID_PARAMETER_VALUE ); + } + } + + /** + * Make a GET request using mimeType and href + * + * TODO: add support for autoretry, proxy + */ + private ReferenceInputStream httpGet(final String dataURLString, final String mimeType) throws IOException { + HttpClient backend = new DefaultHttpClient(); + DecompressingHttpClient httpclient = new DecompressingHttpClient(backend); + + HttpGet httpget = new HttpGet(dataURLString); + + if (mimeType != null){ + httpget.addHeader(new BasicHeader("Content-type", mimeType)); + } + + return processResponse(httpclient.execute(httpget)); + } + + /** + * Make a POST request using mimeType and href + * + * TODO: add support for autoretry, proxy + */ + private ReferenceInputStream httpPost(final String dataURLString, final String body, final String mimeType) throws IOException { + HttpClient backend = new DefaultHttpClient(); + + DecompressingHttpClient httpclient = new DecompressingHttpClient(backend); + + HttpPost httppost = new HttpPost(dataURLString); + + if (mimeType != null){ + httppost.addHeader(new BasicHeader("Content-type", mimeType)); + } + + // set body entity + HttpEntity postEntity = new StringEntity(body); + httppost.setEntity(postEntity); + + return processResponse(httpclient.execute(httppost)); + } + + private ReferenceInputStream processResponse(HttpResponse response) throws IOException { + + HttpEntity entity = response.getEntity(); + Header header; + + header = entity.getContentType(); + String mimeType = header == null ? null : header.getValue(); + + header = entity.getContentEncoding(); + String encoding = header == null ? null : header.getValue(); + + return new ReferenceInputStream(entity.getContent(), mimeType, encoding); + } +} diff --git a/src/main/java/org/n52/wps/server/request/strategy/IReferenceStrategy.java b/src/main/java/org/n52/wps/server/request/strategy/IReferenceStrategy.java new file mode 100644 index 0000000..0ad8ad0 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/strategy/IReferenceStrategy.java @@ -0,0 +1,39 @@ +/** + * 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.strategy; + + +import org.n52.wps.server.ExceptionReport; + +import net.opengis.wps.x100.InputType; + +public interface IReferenceStrategy { + public boolean isApplicable(InputType input); + public ReferenceInputStream fetchData(InputType input) throws ExceptionReport; +} diff --git a/src/main/java/org/n52/wps/server/request/strategy/LazyHttpInputStream.java b/src/main/java/org/n52/wps/server/request/strategy/LazyHttpInputStream.java new file mode 100644 index 0000000..dd25cdc --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/strategy/LazyHttpInputStream.java @@ -0,0 +1,212 @@ +/** + * 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.strategy; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DecompressingHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicHeader; +import org.n52.wps.server.request.InputHandler; + +/** + * An extension of an Input Stream with HTTP Connection abilities. + * Uses an {@link InputStream} internally. HTTP connection is established + * in a lazy fashion, i.e. on first read attempt. + * + * This class shall prevent timeout issues with I/O streaming in the WPS framework. + * + * @deprecated alternative implementation now used, featuring {@link ReferenceInputStream} and + * corresponding adjustments in {@link InputHandler} + * + * @author Matthias Mueller, TU Dresden + * + */ +@Deprecated +public class LazyHttpInputStream extends InputStream { + + private InputStream is; + private boolean initDone = false; + + // connection parameters + final boolean useHttpGet; + final String dataURLString; + final String body; + final String mimeType; + + /** + * Constructor for HTTP/POST + * + * @param dataURLString + * @param body + * @param mimeType + */ + public LazyHttpInputStream (final String dataURLString, final String body, final String mimeType){ + this.dataURLString = dataURLString; + this.body = body; + this.mimeType = mimeType; + useHttpGet = false; + } + + /** + * Constructor for HTTP/GET + * + * @param dataURLString + * @param body + * @param mimeType + */ + public LazyHttpInputStream (final String dataURLString, final String mimeType){ + this.dataURLString = dataURLString; + this.body = null; + this.mimeType = mimeType; + useHttpGet = true; + } + + /** + * Private init method that makes HTTP connections. + * + * @throws IOException + */ + private final void init() throws IOException{ + if (useHttpGet){ + is = httpGet(dataURLString, mimeType); + } else { + is = httpPost(dataURLString, body, mimeType); + } + + initDone = true; + } + + + @Override + public int read() throws IOException { + if (!initDone){ + init(); + } + + return is.read(); + } + + @Override + public int available() throws IOException { + if (!initDone){ + init(); + } + return is.available(); + } + + @Override + public void close() throws IOException { + if (!initDone){ + init(); + } + is.close(); + } + + @Override + public synchronized void mark(int readlimit) { + if (!initDone){ + try { + init(); + } catch (IOException e) { + // silent catch + } + } + is.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + if (!initDone){ + init(); + } + is.reset(); + } + + @Override + public boolean markSupported() { + if (!initDone){ + // silent catch + } + return is.markSupported(); + } + + /** + * Make a GET request using mimeType and href + * + * TODO: add support for autoretry, proxy + */ + private static InputStream httpGet(final String dataURLString, final String mimeType) throws IOException { + HttpClient backend = new DefaultHttpClient(); + DecompressingHttpClient httpclient = new DecompressingHttpClient(backend); + + HttpGet httpget = new HttpGet(dataURLString); + + if (mimeType != null){ + httpget.addHeader(new BasicHeader("Content-type", mimeType)); + } + + HttpResponse response = httpclient.execute(httpget); + HttpEntity entity = response.getEntity(); + return entity.getContent(); + } + + /** + * Make a POST request using mimeType and href + * + * TODO: add support for autoretry, proxy + */ + private static InputStream httpPost(final String dataURLString, final String body, final String mimeType) throws IOException { + HttpClient backend = new DefaultHttpClient(); + + DecompressingHttpClient httpclient = new DecompressingHttpClient(backend); + + HttpPost httppost = new HttpPost(dataURLString); + + if (mimeType != null){ + httppost.addHeader(new BasicHeader("Content-type", mimeType)); + } + + // set body entity + HttpEntity postEntity = new StringEntity(body); + httppost.setEntity(postEntity); + + HttpResponse response = httpclient.execute(httppost); + HttpEntity resultEntity = response.getEntity(); + return resultEntity.getContent(); + } + +} diff --git a/src/main/java/org/n52/wps/server/request/strategy/ReferenceInputStream.java b/src/main/java/org/n52/wps/server/request/strategy/ReferenceInputStream.java new file mode 100644 index 0000000..6198c26 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/strategy/ReferenceInputStream.java @@ -0,0 +1,56 @@ +/** + * 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.strategy; + +import java.io.FilterInputStream; +import java.io.InputStream; + +/** + * + * @author tkunicki + */ +public class ReferenceInputStream extends FilterInputStream { + + private final String mimeType; + private final String encoding; + + public ReferenceInputStream(InputStream inputStream, String mimeType, String encoding) { + super(inputStream); + this.mimeType = mimeType; + this.encoding = encoding; + } + + public String getMimeType() { + return mimeType; + } + + public String getEncoding() { + return encoding; + } +} diff --git a/src/main/java/org/n52/wps/server/request/strategy/ReferenceStrategyRegister.java b/src/main/java/org/n52/wps/server/request/strategy/ReferenceStrategyRegister.java new file mode 100644 index 0000000..b00b570 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/strategy/ReferenceStrategyRegister.java @@ -0,0 +1,71 @@ +/** + * 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.strategy; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.n52.wps.server.ExceptionReport; + +import net.opengis.wps.x100.InputType; + +public class ReferenceStrategyRegister { + + protected List registeredStrategies; + private static ReferenceStrategyRegister instance; + + + public synchronized static ReferenceStrategyRegister getInstance(){ + if(instance==null){ + instance = new ReferenceStrategyRegister(); + } + return instance; + } + + private ReferenceStrategyRegister(){ + registeredStrategies = new ArrayList(); + registeredStrategies.add(new WCS111XMLEmbeddedBase64OutputReferenceStrategy()); + } + + protected void registerStrategy(IReferenceStrategy strategy){ + registeredStrategies.add(strategy); + } + + public ReferenceInputStream resolveReference(InputType input) throws ExceptionReport{ + IReferenceStrategy foundStrategy = new DefaultReferenceStrategy(); + for(IReferenceStrategy strategy : registeredStrategies){ + if(strategy.isApplicable(input)){ + foundStrategy = strategy; + break; + } + } + return foundStrategy.fetchData(input); + } +} diff --git a/src/main/java/org/n52/wps/server/request/strategy/WCS111XMLEmbeddedBase64OutputReferenceStrategy.java b/src/main/java/org/n52/wps/server/request/strategy/WCS111XMLEmbeddedBase64OutputReferenceStrategy.java new file mode 100644 index 0000000..7d20e46 --- /dev/null +++ b/src/main/java/org/n52/wps/server/request/strategy/WCS111XMLEmbeddedBase64OutputReferenceStrategy.java @@ -0,0 +1,184 @@ +/** + * 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.strategy; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.zip.GZIPInputStream; + +import net.opengis.wps.x100.InputType; + +import org.apache.commons.codec.binary.Base64InputStream; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.xmlbeans.XmlObject; +import org.n52.wps.server.ExceptionReport; + +public class WCS111XMLEmbeddedBase64OutputReferenceStrategy implements IReferenceStrategy{ + + private String fetchedMimeType; + private String fetchedEncoding; + + @Override + public boolean isApplicable(InputType input) { + + if(input.getReference().isSetBody()) { + XmlObject xo = input.getReference().getBody(); + return xo.toString().contains("http://www.opengis.net/wcs/1.1.1"); + }else{ + String dataURLString = input.getReference().getHref(); + return (dataURLString.contains("=GetCoverage") && dataURLString.contains("=1.1.1")); + } + } + + @Override + public ReferenceInputStream fetchData(InputType input) throws ExceptionReport { + + String dataURLString = input.getReference().getHref(); + + String schema = input.getReference().getSchema(); + String encoding = input.getReference().getEncoding(); + String mimeType = input.getReference().getMimeType(); + + try { + URL dataURL = new URL(dataURLString); + // Do not give a direct inputstream. + // The XML handlers cannot handle slow connections + URLConnection conn = dataURL.openConnection(); + conn.setRequestProperty("Accept-Encoding", "gzip"); + conn.setRequestProperty("Content-type", "multipart/mixed"); + //Handling POST with referenced document + if(input.getReference().isSetBodyReference()) { + String bodyReference = input.getReference().getBodyReference().getHref(); + URL bodyReferenceURL = new URL (bodyReference); + URLConnection bodyReferenceConn = bodyReferenceURL.openConnection(); + bodyReferenceConn.setRequestProperty("Accept-Encoding", "gzip"); + InputStream referenceInputStream = retrievingZippedContent(bodyReferenceConn); + IOUtils.copy(referenceInputStream, conn.getOutputStream()); + } + //Handling POST with inline message + else if (input.getReference().isSetBody()) { + conn.setDoOutput(true); + + input.getReference().getBody().save(conn.getOutputStream()); + } + InputStream inputStream = retrievingZippedContent(conn); + + BufferedReader bRead = new BufferedReader(new InputStreamReader(inputStream)); + + String line = ""; + + //boundary between different content types + String boundary = ""; + + boolean boundaryFound = false; + + boolean encodedImagepart = false; + + String encodedImage = ""; + + //e.g. base64 + String contentTransferEncoding = ""; + + String imageContentType = ""; + + int boundaryCount = 0; + + while((line = bRead.readLine()) != null){ + + if(line.contains("boundary")){ + boundary = line.substring(line.indexOf("\"") + 1, line.lastIndexOf("\"")); + boundaryFound = true; + continue; + } + if(boundaryFound){ + if(line.contains(boundary)){ + boundaryCount++; + continue; + } + } + + if(encodedImagepart){ + encodedImage = encodedImage.concat(line); + } + //is the image always the third part?! + else if(boundaryCount == 2){ + if(line.contains("Content-Type")){ + imageContentType = line.substring(line.indexOf(":") +1).trim(); + }else if(line.contains("Content-Transfer-Encoding")){ + contentTransferEncoding = line.substring(line.indexOf(":") +1).trim(); + }else if(line.contains("Content-ID")){ + /* just move further one line (which is hopefully empty) + * and start parsing the encoded image + */ + line = bRead.readLine(); + encodedImagepart = true; + } + } + + } + + return new ReferenceInputStream( + new Base64InputStream(new ByteArrayInputStream(encodedImage.getBytes())), + imageContentType, + null); // encoding is null since encoding was removed + } + catch(RuntimeException e) { + throw new ExceptionReport("Error occured while parsing XML", + ExceptionReport.NO_APPLICABLE_CODE, e); + } + catch(MalformedURLException e) { + String inputID = input.getIdentifier().getStringValue(); + throw new ExceptionReport("The inputURL of the execute is wrong: inputID: " + inputID + " | dataURL: " + dataURLString, + ExceptionReport.INVALID_PARAMETER_VALUE ); + } + catch(IOException e) { + String inputID = input.getIdentifier().getStringValue(); + throw new ExceptionReport("Error occured while receiving the complexReferenceURL: inputID: " + inputID + " | dataURL: " + dataURLString, + ExceptionReport.INVALID_PARAMETER_VALUE ); + } + } + + 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(); + } + } +} diff --git a/src/main/java/org/n52/wps/server/response/CapabilitiesResponse.java b/src/main/java/org/n52/wps/server/response/CapabilitiesResponse.java new file mode 100644 index 0000000..c609c58 --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/CapabilitiesResponse.java @@ -0,0 +1,68 @@ +/** + * 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.response; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.xmlbeans.XmlException; +import org.n52.wps.server.CapabilitiesConfiguration; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.request.CapabilitiesRequest; +import org.n52.wps.util.XMLBeansHelper; + +/** + * A Response to the Request "GetCapabilities" + * + */ +public class CapabilitiesResponse extends Response { + + /** + * Serves the Request with a Response + * @param request The GetCapabilities request + */ + public CapabilitiesResponse(CapabilitiesRequest request){ + super(request); + } + + /** + * Save this Response to an OutputStream + * @param os The OutputStream to save this Response to + * @throws ExceptionReport + */ + public InputStream getAsStream() throws ExceptionReport{ + try { + return CapabilitiesConfiguration.getInstance().newInputStream(XMLBeansHelper.getXmlOptions()); + } catch (IOException e) { + throw new ExceptionReport("Exception occured while generating response", ExceptionReport.NO_APPLICABLE_CODE, e); + } catch (XmlException e) { + throw new ExceptionReport("Exception occured while generating response", ExceptionReport.NO_APPLICABLE_CODE, e); + } + } +} diff --git a/src/main/java/org/n52/wps/server/response/DescribeProcessResponse.java b/src/main/java/org/n52/wps/server/response/DescribeProcessResponse.java new file mode 100644 index 0000000..37d4740 --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/DescribeProcessResponse.java @@ -0,0 +1,55 @@ +/** + * 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.response; + +import java.io.InputStream; + +import net.opengis.wps.x100.ProcessDescriptionsDocument; + +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.request.DescribeProcessRequest; +import org.n52.wps.util.XMLBeansHelper; + + +public class DescribeProcessResponse extends Response{ + + public DescribeProcessResponse(DescribeProcessRequest request){ + super(request); + } + + @Override + public InputStream getAsStream() throws ExceptionReport{ + try { + return ((ProcessDescriptionsDocument)request.getAttachedResult()).newInputStream(XMLBeansHelper.getXmlOptions()); + } + catch(Exception e) { + throw new ExceptionReport("Exception occured while writing response document", ExceptionReport.NO_APPLICABLE_CODE, e); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/n52/wps/server/response/ExecuteResponse.java b/src/main/java/org/n52/wps/server/response/ExecuteResponse.java new file mode 100644 index 0000000..010610f --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/ExecuteResponse.java @@ -0,0 +1,56 @@ +/** + * 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.response; + +import java.io.InputStream; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.request.ExecuteRequest; + +public class ExecuteResponse extends Response { + + private ExecuteResponseBuilder builder; + + public ExecuteResponse(ExecuteRequest request) throws ExceptionReport{ + super(request); + this.builder = ((ExecuteRequest)this.request).getExecuteResponseBuilder(); + } + + @Override + public InputStream getAsStream() throws ExceptionReport{ + return this.builder.getAsStream(); + } + + public ExecuteResponseBuilder getExecuteResponseBuilder(){ + return builder; + } + + public String getMimeType(){ + return builder.getMimeType(); + } +} \ No newline at end of file diff --git a/src/main/java/org/n52/wps/server/response/ExecuteResponseBuilder.java b/src/main/java/org/n52/wps/server/response/ExecuteResponseBuilder.java new file mode 100644 index 0000000..23dc030 --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/ExecuteResponseBuilder.java @@ -0,0 +1,381 @@ +/** + * 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.response; + +import java.io.InputStream; +import java.util.Calendar; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; + +import net.opengis.ows.x11.DomainMetadataType; +import net.opengis.ows.x11.LanguageStringType; +import net.opengis.wps.x100.DataInputsType; +import net.opengis.wps.x100.DocumentOutputDefinitionType; +import net.opengis.wps.x100.ExecuteResponseDocument; +import net.opengis.wps.x100.ExecuteResponseDocument.ExecuteResponse; +import net.opengis.wps.x100.OutputDefinitionType; +import net.opengis.wps.x100.OutputDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.StatusType; + +import org.apache.xmlbeans.XmlCursor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.n52.wps.commons.WPSConfig; +import org.n52.wps.io.data.IBBOXData; +import org.n52.wps.io.data.IData; +import org.n52.wps.server.CapabilitiesConfiguration; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.RepositoryManager; +import org.n52.wps.server.WebProcessingService; +import org.n52.wps.server.database.DatabaseFactory; +import org.n52.wps.server.request.ExecuteRequest; +import org.n52.wps.server.request.Request; +import org.n52.wps.util.XMLBeansHelper; + +/** + * WPS Execute operation response. By default, this XML document is delivered to the client in response to an Execute request. If "status" is "false" in the Execute operation request, this document is normally returned when process execution has been completed. + * If "status" in the Execute request is "true", this response shall be returned as soon as the Execute request has been accepted for processing. In this case, the same XML document is also made available as a web-accessible resource from the URL identified in the statusLocation, and the WPS server shall repopulate it once the process has completed. It may repopulate it on an ongoing basis while the process is executing. + * However, the response to an Execute request will not include this element in the special case where the output is a single complex value result and the Execute request indicates that "store" is "false". + * Instead, the server shall return the complex result (e.g., GIF image or GML) directly, without encoding it in the ExecuteResponse. If processing fails in this special case, the normal ExecuteResponse shall be sent, with the error condition indicated. This option is provided to simplify the programming required for simple clients and for service chaining. + * @author Timon ter Braak + * + */ +public class ExecuteResponseBuilder { + + private String identifier; + private DataInputsType dataInputs; + //private DocumentOutputDefinitionType[] outputDefs; + private ExecuteRequest request; + private ExecuteResponseDocument doc; + private RawData rawDataHandler = null; + private ProcessDescriptionType description; + private static Logger LOGGER = LoggerFactory.getLogger(ExecuteResponseBuilder.class); + private Calendar creationTime; + + public ExecuteResponseBuilder(ExecuteRequest request) throws ExceptionReport{ + this.request = request; + doc = ExecuteResponseDocument.Factory.newInstance(); + doc.addNewExecuteResponse(); + XmlCursor c = doc.newCursor(); + c.toFirstChild(); + c.toLastAttribute(); + c.setAttributeText(new QName(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation"), "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd"); + doc.getExecuteResponse().setServiceInstance(CapabilitiesConfiguration.ENDPOINT_URL+"?REQUEST=GetCapabilities&SERVICE=WPS"); + doc.getExecuteResponse().setLang(WebProcessingService.DEFAULT_LANGUAGE); + doc.getExecuteResponse().setService("WPS"); + doc.getExecuteResponse().setVersion(Request.SUPPORTED_VERSION); + this.identifier = request.getExecute().getIdentifier().getStringValue().trim(); + ExecuteResponse responseElem = doc.getExecuteResponse(); + responseElem.addNewProcess().addNewIdentifier().setStringValue(identifier); + description = RepositoryManager.getInstance().getProcessDescription(this.identifier); + if(description==null){ + throw new RuntimeException("Error while accessing the process description for "+ identifier); + } + + responseElem.getProcess().setTitle(description.getTitle()); + responseElem.getProcess().setProcessVersion(description.getProcessVersion()); + creationTime = Calendar.getInstance(); + } + + public void update() throws ExceptionReport { + // copying the request parameters to the response + ExecuteResponse responseElem = doc.getExecuteResponse(); + + // if status succeeded, update reponse with result + if (responseElem.getStatus().isSetProcessSucceeded()) { + // the response only include dataInputs, if the property is set to true; + //if(Boolean.getBoolean(WPSConfiguration.getInstance().getProperty(WebProcessingService.PROPERTY_NAME_INCLUDE_DATAINPUTS_IN_RESPONSE))) { + if(new Boolean(WPSConfig.getInstance().getWPSConfig().getServer().getIncludeDataInputsInResponse())){ + dataInputs = request.getExecute().getDataInputs(); + responseElem.setDataInputs(dataInputs); + } + responseElem.addNewProcessOutputs(); + // has the client specified the outputs? + if (request.getExecute().isSetResponseForm()) { + // Get the outputdescriptions from the algorithm + + OutputDescriptionType[] outputDescs = description.getProcessOutputs().getOutputArray(); + if(request.isRawData()) { + OutputDefinitionType rawDataOutput = request.getExecute().getResponseForm().getRawDataOutput(); + String id = rawDataOutput.getIdentifier().getStringValue(); + OutputDescriptionType desc = XMLBeansHelper.findOutputByID(id, outputDescs); + if(desc.isSetComplexOutput()) { + String encoding = ExecuteResponseBuilder.getEncoding(desc, rawDataOutput); + String schema = ExecuteResponseBuilder.getSchema(desc, rawDataOutput); + String responseMimeType = getMimeType(rawDataOutput); + generateComplexDataOutput(id, false, true, schema, responseMimeType, encoding, null); + } + + else if (desc.isSetLiteralOutput()) { + String mimeType = null; + String schema = null; + String encoding = null; + DomainMetadataType dataType = desc.getLiteralOutput().getDataType(); + String reference = dataType != null ? dataType.getReference() : null; + generateLiteralDataOutput(id, doc, true, reference, schema, mimeType, encoding, desc.getTitle()); + } + else if (desc.isSetBoundingBoxOutput()) { + generateBBOXOutput(id, doc, true, desc.getTitle()); + } + return; + } + // Get the outputdefinitions from the clients request + // For each request of output + for(int i = 0; i"); + builder.append("\n\t"); + builder.append(""); + SPACE_JOINER.appendTo(builder, Doubles.asList(bbox.getLowerCorner())); + builder.append(""); + builder.append("\n\t"); + builder.append(""); + SPACE_JOINER.appendTo(builder, Doubles.asList(bbox.getUpperCorner())); + builder.append(""); + builder.append("\n"); + builder.append(""); + return new ByteArrayInputStream(builder.toString().getBytes(Charsets.UTF_8)); + } + //complexdata + if(encoding == null || "".equals(encoding) || encoding.equalsIgnoreCase(IOHandler.DEFAULT_ENCODING)){ + return generator.generateStream(obj, mimeType, schema); + } + else if(encoding.equalsIgnoreCase(IOHandler.ENCODING_BASE64)){ + return generator.generateBase64Stream(obj, mimeType, schema); + + } + } catch (IOException e) { + throw new ExceptionReport("Error while generating Complex Data out of the process result", ExceptionReport.NO_APPLICABLE_CODE, e); + } + throw new ExceptionReport("Could not determine encoding. Use default (=not set) or base64", ExceptionReport.NO_APPLICABLE_CODE); + } + + private StringBuilder appendAttr(StringBuilder builder, String key, Object value) { + return builder.append(' ').append(key).append('=') + .append('"').append(value).append('"'); + } + + private static String escape(String s) { + return s.replaceAll("&", "&") + .replaceAll("\"", """) + .replaceAll("'", "'") + .replaceAll("<", "<") + .replaceAll(">", ">"); + } +} diff --git a/src/main/java/org/n52/wps/server/response/Response.java b/src/main/java/org/n52/wps/server/response/Response.java new file mode 100644 index 0000000..f37e0c4 --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/Response.java @@ -0,0 +1,61 @@ +/** + * 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.response; + +import java.io.InputStream; +import java.util.UUID; + +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.request.Request; + + +abstract public class Response { + + protected Request request; + + public Response(Request request){ + this.request = request; + } + + public UUID getUniqueId(){ + return this.request.getUniqueId(); + } + + public String getType() { + return this.getClass().getName(); + } + + public abstract InputStream getAsStream() throws ExceptionReport; + + public Request getRequest(){ + return request; + } + + +} diff --git a/src/main/java/org/n52/wps/server/response/ResponseData.java b/src/main/java/org/n52/wps/server/response/ResponseData.java new file mode 100644 index 0000000..619e719 --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/ResponseData.java @@ -0,0 +1,359 @@ +/** + * 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.response; + +import java.util.ArrayList; +import java.util.List; + +import net.opengis.wps.x100.ComplexDataDescriptionType; +import net.opengis.wps.x100.OutputDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.io.GeneratorFactory; +import org.n52.wps.io.IGenerator; +import org.n52.wps.io.data.IData; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.RepositoryManager; + +/* + * @author foerster + * This and the inheriting classes in charge of populating the ExecuteResponseDocument. + */ +public abstract class ResponseData { + + private static Logger LOGGER = LoggerFactory.getLogger(ResponseData.class); + + protected IData obj = null; + protected String id; + protected String schema; + protected String encoding; + protected String mimeType; + protected IGenerator generator = null; + protected String algorithmIdentifier = null; + protected ProcessDescriptionType description = null; + + + public ResponseData(IData obj, String id, String schema, String encoding, + String mimeType, String algorithmIdentifier, ProcessDescriptionType description) throws ExceptionReport { + + this.obj = obj; + this.id = id; + this.algorithmIdentifier = algorithmIdentifier; + this.description = description; + this.encoding = encoding; + + OutputDescriptionType outputType =null; + + OutputDescriptionType[] describeProcessOutput = description.getProcessOutputs().getOutputArray(); + for(OutputDescriptionType tempOutputType : describeProcessOutput){ + if(tempOutputType.getIdentifier().getStringValue().equalsIgnoreCase(id)){ + outputType = tempOutputType; + } + } + + + + //select generator + + //0. complex output set? --> no: skip + //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 + + + String finalSchema = null; + String finalMimeType = null; + String finalEncoding = null; + + if (outputType.isSetComplexOutput()){ + if (mimeType != null){ + //mime type in request + ComplexDataDescriptionType format = null; + + String defaultMimeType = outputType.getComplexOutput().getDefault().getFormat().getMimeType(); + + boolean canUseDefault = false; + if(defaultMimeType.equalsIgnoreCase(mimeType)){ + ComplexDataDescriptionType potenitalFormat = outputType.getComplexOutput().getDefault().getFormat(); + if(schema != null && encoding == null){ + if(schema.equalsIgnoreCase(potenitalFormat.getSchema())){ + canUseDefault = true; + format = potenitalFormat; + } + } + if(schema == null && encoding != null){ + if(encoding.equalsIgnoreCase(potenitalFormat.getEncoding())){ + canUseDefault = true; + format = potenitalFormat; + } + + } + if(schema != null && encoding != null){ + if(schema.equalsIgnoreCase(potenitalFormat.getSchema()) && encoding.equalsIgnoreCase(potenitalFormat.getEncoding())){ + canUseDefault = true; + format = potenitalFormat; + } + + } + if(schema == null && encoding == null){ + canUseDefault = true; + format = potenitalFormat; + } + + } + if(!canUseDefault){ + ComplexDataDescriptionType[] formats =outputType.getComplexOutput().getSupported().getFormatArray(); + for(ComplexDataDescriptionType potenitalFormat : formats){ + if(potenitalFormat.getMimeType().equalsIgnoreCase(mimeType)){ + if(schema != null && encoding == null){ + if(schema.equalsIgnoreCase(potenitalFormat.getSchema())){ + format = potenitalFormat; + } + } + if(schema == null && encoding != null){ + if(encoding.equalsIgnoreCase(potenitalFormat.getEncoding()) || potenitalFormat.getEncoding() == null){ + format = potenitalFormat; + } + + } + if(schema != null && encoding != null){ + if(schema.equalsIgnoreCase(potenitalFormat.getSchema()) && ((encoding.equalsIgnoreCase(potenitalFormat.getEncoding()) || potenitalFormat.getEncoding() == null) )){ + format = potenitalFormat; + } + + } + if(schema == null && encoding == null){ + format = potenitalFormat; + } + } + } + } + if(format == null){ + throw new ExceptionReport("Could not determine output format", ExceptionReport.INVALID_PARAMETER_VALUE); + } + + finalMimeType = format.getMimeType(); + + if(format.isSetEncoding()){ + //no encoding provided--> select default one for mimeType + finalEncoding = format.getEncoding(); + } + + if(format.isSetSchema()){ + //no encoding provided--> select default one for mimeType + finalSchema = format.getSchema(); + } + + }else{ + + //mimeType not in request + if(mimeType==null && encoding==null && schema == null){ + //nothing set, use default values + finalSchema = outputType.getComplexOutput().getDefault().getFormat().getSchema(); + finalMimeType = outputType.getComplexOutput().getDefault().getFormat().getMimeType(); + finalEncoding = outputType.getComplexOutput().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(encoding!=null && schema==null){ + //encoding set only + ComplexDataDescriptionType encodingFormat = null; + String defaultEncoding = outputType.getComplexOutput().getDefault().getFormat().getEncoding(); + int found = 0; + String foundEncoding = null; + if(defaultEncoding.equalsIgnoreCase(encoding)){ + foundEncoding = outputType.getComplexOutput().getDefault().getFormat().getEncoding(); + encodingFormat = outputType.getComplexOutput().getDefault().getFormat(); + found = found +1; + }else{ + ComplexDataDescriptionType[] formats = outputType.getComplexOutput().getSupported().getFormatArray(); + for(ComplexDataDescriptionType tempFormat : formats){ + if(tempFormat.getEncoding().equalsIgnoreCase(encoding)){ + foundEncoding = tempFormat.getEncoding(); + encodingFormat = tempFormat; + found = found +1; + } + } + } + + if(found == 1){ + finalEncoding = foundEncoding; + finalMimeType = encodingFormat.getMimeType(); + if(encodingFormat.isSetSchema()){ + finalSchema = 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(schema != null && encoding==null){ + //schema set only + ComplexDataDescriptionType schemaFormat = null; + String defaultSchema = outputType.getComplexOutput().getDefault().getFormat().getSchema(); + int found = 0; + String foundSchema = null; + if(defaultSchema.equalsIgnoreCase(schema)){ + foundSchema = outputType.getComplexOutput().getDefault().getFormat().getSchema(); + schemaFormat = outputType.getComplexOutput().getDefault().getFormat(); + found = found +1; + }else{ + ComplexDataDescriptionType[] formats = outputType.getComplexOutput().getSupported().getFormatArray(); + for(ComplexDataDescriptionType tempFormat : formats){ + if(tempFormat.getEncoding().equalsIgnoreCase(schema)){ + foundSchema = tempFormat.getSchema(); + schemaFormat =tempFormat; + found = found +1; + } + } + } + + if(found == 1){ + finalSchema = foundSchema; + finalMimeType = schemaFormat.getMimeType(); + if(schemaFormat.isSetEncoding()){ + finalEncoding = 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(encoding!=null && schema!=null){ + //schema and encoding set + + + //encoding + String defaultEncoding = outputType.getComplexOutput().getDefault().getFormat().getEncoding(); + + List foundEncodingList = new ArrayList(); + if(defaultEncoding.equalsIgnoreCase(encoding)){ + foundEncodingList.add(outputType.getComplexOutput().getDefault().getFormat()); + + + }else{ + ComplexDataDescriptionType[] formats = outputType.getComplexOutput().getSupported().getFormatArray(); + for(ComplexDataDescriptionType tempFormat : formats){ + if(tempFormat.getEncoding().equalsIgnoreCase(encoding)){ + foundEncodingList.add(tempFormat); + } + } + + + + + //schema + List foundSchemaList = new ArrayList(); + String defaultSchema = outputType.getComplexOutput().getDefault().getFormat().getSchema(); + if(defaultSchema.equalsIgnoreCase(schema)){ + foundSchemaList.add(outputType.getComplexOutput().getDefault().getFormat()); + }else{ + formats = outputType.getComplexOutput().getSupported().getFormatArray(); + for(ComplexDataDescriptionType tempFormat : formats){ + if(tempFormat.getEncoding().equalsIgnoreCase(schema)){ + 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()){ + finalEncoding = foundCommonFormat.getEncoding(); + } + if(foundCommonFormat.isSetSchema()){ + finalSchema = 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); + } + + } + + } + + } + } + + } + } + + this.schema = finalSchema; + if(this.encoding==null){ + this.encoding = finalEncoding; + } + this.mimeType = finalMimeType; + + + + + } + + protected void prepareGenerator() throws ExceptionReport { + Class algorithmOutput = RepositoryManager.getInstance().getOutputDataTypeForAlgorithm(this.algorithmIdentifier, id); + + LOGGER.debug("Looking for matching Generator: schema: {}, mimeType {}, encoding: {}", schema, mimeType, encoding); + + GeneratorFactory factory = GeneratorFactory.getInstance(); + this.generator = factory.getGenerator(this.schema, this.mimeType, this.encoding, algorithmOutput); + + if(this.generator != null){ + LOGGER.info("Using generator " + generator.getClass().getName() + " for Schema: " + schema); + } + if(this.generator == null) { + throw new ExceptionReport("Could not find an appropriate generator based on given mimetype/schema/encoding for output", ExceptionReport.NO_APPLICABLE_CODE); + } + } + +} + + diff --git a/src/main/java/org/n52/wps/server/response/RetrieveResultResponse.java b/src/main/java/org/n52/wps/server/response/RetrieveResultResponse.java new file mode 100644 index 0000000..d12480e --- /dev/null +++ b/src/main/java/org/n52/wps/server/response/RetrieveResultResponse.java @@ -0,0 +1,48 @@ +/** + * 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.response; + +import java.io.InputStream; + +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.request.RetrieveResultRequest; + +public class RetrieveResultResponse extends Response { + + public RetrieveResultResponse(RetrieveResultRequest request) { + super(request); + } + + public InputStream getAsStream() throws ExceptionReport { + return (InputStream)request.getAttachedResult(); + } + + + +} diff --git a/src/main/java/org/n52/wps/util/XMLBeansHelper.java b/src/main/java/org/n52/wps/util/XMLBeansHelper.java new file mode 100644 index 0000000..934def2 --- /dev/null +++ b/src/main/java/org/n52/wps/util/XMLBeansHelper.java @@ -0,0 +1,130 @@ +/** + * 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.util; + +import java.util.concurrent.ConcurrentMap; + +import net.opengis.wps.x100.InputDescriptionType; +import net.opengis.wps.x100.OutputDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType.DataInputs; + +import org.apache.xmlbeans.XmlOptions; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.Maps; + +/* + * + * Some conveniant methods, to access some XMLBean objects. + * @author foerster + * + */ +public class XMLBeansHelper { + public static OutputDescriptionType findOutputByID(String outputID, OutputDescriptionType[] outputDescs) { + for(OutputDescriptionType desc : outputDescs) { + if(desc.getIdentifier().getStringValue().equals(outputID)) { + return desc; + } + } + return null; + } + + public static InputDescriptionType findInputByID(String outputID, DataInputs inputs) { + for(InputDescriptionType desc : inputs.getInputArray()) { + if(desc.getIdentifier().getStringValue().equals(outputID)) { + return desc; + } + } + return null; + } + + /** + * @return the default XmlOptions used in responses + */ + public static XmlOptions getXmlOptions() { + return new XmlOptions() + .setSaveNamespacesFirst() + .setSaveSuggestedPrefixes(PREFIXES) + .setSaveAggressiveNamespaces() + .setSavePrettyPrint(); + } + + /** + * Registers a prefix for a namespace to be used in responses. + * + * @param namespace the XML namespace + * @param prefix the prefix + */ + public static void registerPrefix(String namespace, String prefix) { + PREFIXES.put(Preconditions.checkNotNull(Strings.emptyToNull(namespace)), + Preconditions.checkNotNull(Strings.emptyToNull(prefix))); + } + + /** + * The namespace for WPS 1.0.0: {@value}. + */ + public static final String NS_WPS_1_0_0 = "http://www.opengis.net/wps/1.0.0"; + + /** + * The prefix for WPS 1.0.0: {@value}. + */ + public static final String NS_WPS_PREFIX = "wps"; + + /** + * The namespace for WPS 1.0.0: {@value}. + */ + public static final String NS_OWS_1_1 = "http://www.opengis.net/ows/1.1"; + + /** + * The prefix for WPS 1.0.0: {@value}. + */ + public static final String NS_OWS_PREFIX = "ows"; + + /** + * The namespace for XSI: {@value}. + */ + public static final String NS_XSI + = "http://www.w3.org/2001/XMLSchema-instance"; + + /** + * The prefix for XSI: {@value}. + */ + public static final String NS_XSI_PREFIX = "xsi"; + + private static final ConcurrentMap PREFIXES; + + static { + PREFIXES = Maps.newConcurrentMap(); + PREFIXES.put(NS_XSI, NS_XSI_PREFIX); + PREFIXES.put(NS_WPS_1_0_0, NS_WPS_PREFIX); + PREFIXES.put(NS_OWS_1_1, NS_OWS_PREFIX); + } + +} diff --git a/src/test/java/org/n52/wps/server/request/DummyTestClassAlgorithmInputHandlerTest.java b/src/test/java/org/n52/wps/server/request/DummyTestClassAlgorithmInputHandlerTest.java new file mode 100644 index 0000000..08b96d0 --- /dev/null +++ b/src/test/java/org/n52/wps/server/request/DummyTestClassAlgorithmInputHandlerTest.java @@ -0,0 +1,148 @@ +/** + * 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 static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.io.IOException; + +import net.opengis.wps.x100.ExecuteDocument; +import net.opengis.wps.x100.InputType; + +import org.apache.xmlbeans.XmlException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.n52.wps.io.data.binding.bbox.BoundingBoxData; +import org.n52.wps.server.ExceptionReport; + +import com.google.common.primitives.Doubles; + +/** + * + * @author isuftin + */ +public class DummyTestClassAlgorithmInputHandlerTest { + + private static String sampleFileName = null; + private static File sampleFile = null; + private static ExecuteDocument execDoc = null; + private static InputType[] inputArray = null; + private static File projectRoot = null; + + @BeforeClass + public static void setupClass() { + sampleFileName = "src/test/resources/DummyTestClass.xml"; + sampleFile = new File(sampleFileName); + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() throws XmlException, IOException { + WPSConfigTestUtil.generateMockConfig(getClass(), "/org/n52/wps/io/test/inputhandler/generator/wps_config.xml"); + + execDoc = ExecuteDocument.Factory.parse(sampleFile); + inputArray = execDoc.getExecute().getDataInputs().getInputArray(); + + File f = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile()); + projectRoot = new File(f.getParentFile().getParentFile().getParent()); + } + + @After + public void tearDown() { + } + + @Test(expected = ExceptionReport.class) + public void testInputHandlerInitializationWithIncorrectAlgorithmName() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, "this.algorithm.name.does.not.exist").build(); + } + + @Test(expected = ExceptionReport.class) + public void testInputHandlerInitializationWithNullAlgorithmName() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, null).build(); + } + + @Test(expected = NullPointerException.class) + public void testInputHandlerInitializationWithNullInputsArray() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(null, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + } + + @Test + public void testInputHandlerInitializationWithEmptyInputsArray() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(new InputType[]{}, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + + assertThat(instance, not(nullValue())); + assertThat(instance.getParsedInputData().isEmpty(), is(true)); + } + + @Test + public void testInputHandlerInitialization() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + + assertThat(instance, not(nullValue())); + } + + @Test + public void testGetParsedInputDataWithCorrectInput() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + + assertThat(instance.getParsedInputData().isEmpty(), is(false)); + assertThat(instance.getParsedInputData().size(), equalTo(1)); + assertThat(instance.getParsedInputData().size(), equalTo(1)); + assertThat(instance.getParsedInputData().get("BBOXInputData"), is(notNullValue())); + assertThat(instance.getParsedInputData().get("BBOXInputData").size(), equalTo(1)); + assertThat(instance.getParsedInputData().get("BBOXInputData").get(0), is(notNullValue())); + assertThat((BoundingBoxData)instance.getParsedInputData().get("BBOXInputData").get(0).getPayload(), is(notNullValue())); + + BoundingBoxData test = (BoundingBoxData)instance.getParsedInputData().get("BBOXInputData").get(0).getPayload(); + assertThat(Doubles.asList(test.getLowerCorner()), contains(46.75, 13.05)); + assertThat(Doubles.asList(test.getUpperCorner()), contains(46.85, 13.25)); + assertThat(test.getCRS(), is(nullValue())); + assertThat(test.getDimension(), is(2)); + } +} diff --git a/src/test/java/org/n52/wps/server/request/ExecuteRequestTest.java b/src/test/java/org/n52/wps/server/request/ExecuteRequestTest.java new file mode 100644 index 0000000..c78a850 --- /dev/null +++ b/src/test/java/org/n52/wps/server/request/ExecuteRequestTest.java @@ -0,0 +1,121 @@ +/** + * 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 static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import net.opengis.wps.x100.ExecuteResponseDocument; +import net.opengis.wps.x100.StatusType; + +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.apache.xmlbeans.XmlValidationError; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import org.n52.wps.commons.WPSConfig; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.database.DatabaseFactory; + +/** + * + * @author bpross-52n + */ +public class ExecuteRequestTest { + + private DocumentBuilderFactory fac; + + @BeforeClass + public static void setUpClass() + throws XmlException, IOException { + WPSConfig.forceInitialization("src/test/resources/org/n52/wps/io/test/inputhandler/generator/wps_config.xml"); + } + + @Before + public void setUp(){ + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); + + fac = DocumentBuilderFactory.newInstance(); + fac.setNamespaceAware(true); + } + + @Test + public void testUpdateStatusError() throws ExceptionReport, XmlException, IOException, SAXException, ParserConfigurationException { + + FileInputStream fis = new FileInputStream(new File("src/test/resources/LRDTCCorruptInputResponseDocStatusTrue.xml")); + + // parse the InputStream to create a Document + Document doc = fac.newDocumentBuilder().parse(fis); + + ExecuteRequest request = new ExecuteRequest(doc); + + String exceptionText = "TestError"; + + request.updateStatusError(exceptionText); + + File response = DatabaseFactory.getDatabase().lookupResponseAsFile(request.getUniqueId().toString()); + + ExecuteResponseDocument responseDoc = ExecuteResponseDocument.Factory.parse(response); + + StatusType statusType = responseDoc.getExecuteResponse().getStatus(); + + assertTrue(validateExecuteResponse(responseDoc)); + assertTrue(statusType.isSetProcessFailed()); + assertTrue(statusType.getProcessFailed().getExceptionReport().getExceptionArray(0).getExceptionTextArray(0).equals(exceptionText)); + + } + + private boolean validateExecuteResponse(ExecuteResponseDocument responseDoc) { + XmlOptions xmlOptions = new XmlOptions(); + List xmlValidationErrorList = new ArrayList(); + xmlOptions.setErrorListener(xmlValidationErrorList); + boolean valid = responseDoc.validate(xmlOptions); + if (!valid) { + System.err.println("Error validating process description for " + getClass().getCanonicalName()); + for (XmlValidationError xmlValidationError : xmlValidationErrorList) { + System.err.println("\tMessage: " + xmlValidationError.getMessage()); + System.err.println("\tLocation of invalid XML: " + + xmlValidationError.getCursorLocation().xmlText()); + } + } + return valid; + } +} diff --git a/src/test/java/org/n52/wps/server/request/InputHandlerTest.java b/src/test/java/org/n52/wps/server/request/InputHandlerTest.java new file mode 100644 index 0000000..83b758b --- /dev/null +++ b/src/test/java/org/n52/wps/server/request/InputHandlerTest.java @@ -0,0 +1,160 @@ +/** + * 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.File; +import java.io.IOException; +import java.util.Map; +import net.opengis.wps.x100.ComplexDataDescriptionType; +import net.opengis.wps.x100.ExecuteDocument; +import net.opengis.wps.x100.InputDescriptionType; +import net.opengis.wps.x100.InputType; +import org.apache.xmlbeans.XmlException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.handler.DataInputInterceptors.InterceptorInstance; + +/** + * + * @author isuftin + */ +public class InputHandlerTest { + + private static File simpleBufferAlgorithmFile = null; + private static File dummyTestClassAlgorithmFile = null; + private static ExecuteDocument simpleBufferAlgorithmExecDoc = null; + private static ExecuteDocument dummyTestClassAlgorithmExecDoc = null; + private static InputType[] simpleBufferAlgorithmInputArray = null; + private static InputType[] dummyTestClassAlgorithmInputArray = null; + + @BeforeClass + public static void setupClass() throws XmlException, IOException { + WPSConfigTestUtil.generateMockConfig(InputHandlerTest.class, "/org/n52/wps/io/test/inputhandler/generator/wps_config.xml"); + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() throws XmlException, IOException { + simpleBufferAlgorithmFile = new File("src/test/resources/SimpleBufferAlgorithm.xml"); + simpleBufferAlgorithmExecDoc = ExecuteDocument.Factory.parse(simpleBufferAlgorithmFile); + simpleBufferAlgorithmInputArray = simpleBufferAlgorithmExecDoc.getExecute().getDataInputs().getInputArray(); + + dummyTestClassAlgorithmFile = new File("src/test/resources/DummyTestClass.xml"); + dummyTestClassAlgorithmExecDoc = ExecuteDocument.Factory.parse(dummyTestClassAlgorithmFile); + dummyTestClassAlgorithmInputArray = dummyTestClassAlgorithmExecDoc.getExecute().getDataInputs().getInputArray(); + } + + @After + public void tearDown() { + } + + @Test + public void testInputHandlerInitialization() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(simpleBufferAlgorithmInputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + + assertThat(instance, not(nullValue())); + } + + @Test + public void testInputHandlerResolveInputInterceptors() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerResolveInputInterceptors..."); + InputHandler instance = new InputHandler.Builder(simpleBufferAlgorithmInputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + + Map resolveInputInterceptors = instance.resolveInputInterceptors("org.n52.wps.server.algorithm.SimpleBufferAlgorithm"); + assertThat(resolveInputInterceptors.size(), equalTo(0)); + + instance = new InputHandler.Builder(dummyTestClassAlgorithmInputArray, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + resolveInputInterceptors = instance.resolveInputInterceptors("org.n52.wps.server.algorithm.SimpleBufferAlgorithm"); + assertThat(resolveInputInterceptors.size(), equalTo(0)); + } + + @Test + public void testInputHandlerResolveInputDescriptionTypes() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerResolveInputDescriptionTypes..."); + + InputHandler instance = new InputHandler.Builder(simpleBufferAlgorithmInputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + InputDescriptionType idt = instance.getInputReferenceDescriptionType("data"); + assertThat(idt, is(notNullValue())); + assertThat(idt.getMaxOccurs().intValue(), equalTo(1)); + assertThat(idt.getMinOccurs().intValue(), equalTo(1)); + + instance = new InputHandler.Builder(dummyTestClassAlgorithmInputArray, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + idt = instance.getInputReferenceDescriptionType("BBOXInputData"); + assertThat(idt, is(notNullValue())); + assertThat(idt.getMaxOccurs().intValue(), equalTo(1)); + assertThat(idt.getMinOccurs().intValue(), equalTo(0)); + } + + @Test + public void testInputHandlerGetNonDefaultFormat() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerGetNonDefaultFormat..."); + + InputHandler instance = new InputHandler.Builder(simpleBufferAlgorithmInputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + InputDescriptionType idt = instance.getInputReferenceDescriptionType("data"); + String dataMimeType = "text/xml; subtype=gml/3.1.0"; + String dataSchema = "http://schemas.opengis.net/gml/3.1.0/base/feature.xsd"; + String dataEncoding = null; + ComplexDataDescriptionType cddt = instance.getNonDefaultFormat(idt, dataMimeType, dataSchema, dataEncoding); + assertThat(cddt, is(notNullValue())); + assertThat(cddt.getEncoding(), is(nullValue())); + assertThat(cddt.getMimeType(), is(equalTo("text/xml; subtype=gml/3.1.0"))); + assertThat(cddt.getSchema(), is(equalTo("http://schemas.opengis.net/gml/3.1.0/base/feature.xsd"))); + + instance = new InputHandler.Builder(dummyTestClassAlgorithmInputArray, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + idt = instance.getInputReferenceDescriptionType("BBOXInputData"); + cddt = instance.getNonDefaultFormat(idt, dataMimeType, dataSchema, dataEncoding); + assertThat(cddt, is(nullValue())); + } + + @Test//TODO check test, why is method getComplexValueNodeString used for bbox data? + public void testInputHandlerGetComplexValueNodeString() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerGetComplexValueNodeString..."); + + InputHandler instance = new InputHandler.Builder(simpleBufferAlgorithmInputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + String result = instance.getComplexValueNodeString(simpleBufferAlgorithmInputArray[0].getData().getComplexData().getDomNode()); + assertThat(result, not(isEmptyOrNullString())); + assertThat(result, containsString("147.25674400000003 -42.778393 147.22018400000002 -42.824776 147.179596 -42.82143 147.11132800000001 -42.795731 147.057098 -42.741581 147.00347900000003 -42.704803 146.91909800000002 -42.622734 146.91053799999997 -42.610928 146.88998400000003 -42.585396 146.83844 -42.572792 146.78569 -42.539352 146.724335 -42.485966 146.695023 -42.469582 146.64987200000002 -42.450371 146.604965 -42.432274 146.578781 -42.408531 146.539307 -42.364208 146.525055 -42.30883 146.558044 -42.275948 146.57624800000002 -42.23777 146.58146699999998 -42.203426 146.490005 -42.180222 146.3797 -42.146332 146.33406100000002 -42.138741 146.270966 -42.165703 146.197296 -42.224072 146.167908 -42.244835 146.16493200000002 -42.245171 146.111023 -42.265202 146.03747600000003 -42.239738 145.981628 -42.187851 145.85391199999998 -42.133492 145.819611 -42.129154 145.72052000000002 -42.104084 145.61857600000002 -42.056023 145.541718 -42.027241 145.48628200000002 -41.983326 145.452744 -41.926544 145.494034 -41.896477 145.59173600000003 -41.860214 145.64211999999998 -41.838398 145.669449 -41.830734 145.680923 -41.795753 145.68296800000002 -41.743221 145.67515600000002 -41.710377 145.680115 -41.688908 145.70106500000003 -41.648228 145.71479799999997 -41.609509 145.62919599999998 -41.462051 145.64889499999998 -41.470337 145.633423 -41.420902 145.631866 -41.36528 145.640854 -41.301533 145.700424 -41.242611 145.77242999999999 -41.193897 145.80233800000002 -41.161488 145.856018 -41.08007")); + + instance = new InputHandler.Builder(dummyTestClassAlgorithmInputArray, "org.n52.wps.server.algorithm.test.DummyTestClass").build(); + result = instance.getComplexValueNodeString(dummyTestClassAlgorithmInputArray[0].getData().getBoundingBoxData().getDomNode()); + assertThat(result, not(isEmptyOrNullString())); + assertThat(result, containsString("46.75 13.05")); + + } +} diff --git a/src/test/java/org/n52/wps/server/request/SimpleBufferAlgorithmInputHandlerTest.java b/src/test/java/org/n52/wps/server/request/SimpleBufferAlgorithmInputHandlerTest.java new file mode 100644 index 0000000..89bb02e --- /dev/null +++ b/src/test/java/org/n52/wps/server/request/SimpleBufferAlgorithmInputHandlerTest.java @@ -0,0 +1,160 @@ +/** + * 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 static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.io.IOException; + +import net.opengis.wps.x100.ExecuteDocument; +import net.opengis.wps.x100.InputType; + +import org.apache.xmlbeans.XmlException; +import org.geotools.feature.DefaultFeatureCollection; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.n52.wps.io.data.IData; +import org.n52.wps.server.ExceptionReport; + +/** + * + * @author isuftin + */ +public class SimpleBufferAlgorithmInputHandlerTest { + + private static String sampleFileName = null; + private static File sampleFile = null; + private static ExecuteDocument execDoc = null; + private static InputType[] inputArray = null; + private static File projectRoot = null; + + @BeforeClass + public static void setupClass() throws XmlException, IOException { + sampleFileName = "src/test/resources/SimpleBufferAlgorithm.xml"; + sampleFile = new File(sampleFileName); + WPSConfigTestUtil.generateMockConfig(SimpleBufferAlgorithmInputHandlerTest.class, "/org/n52/wps/io/test/inputhandler/generator/wps_config.xml"); + + execDoc = ExecuteDocument.Factory.parse(sampleFile); + inputArray = execDoc.getExecute().getDataInputs().getInputArray(); + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() throws XmlException, IOException { + File f = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile()); + projectRoot = new File(f.getParentFile().getParentFile().getParent()); + } + + @After + public void tearDown() { + } + + @Test(expected = ExceptionReport.class) + public void testInputHandlerInitializationWithIncorrectAlgorithmName() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, "this.algorithm.name.does.not.exist").build(); + } + + @Test(expected = ExceptionReport.class) + public void testInputHandlerInitializationWithNullAlgorithmName() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, null).build(); + } + + @Test(expected = NullPointerException.class) + public void testInputHandlerInitializationWithNullInputsArray() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(null, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + } + + @Test + public void testInputHandlerInitializationWithEmptyInputsArray() throws ExceptionReport { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(new InputType[]{}, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + + assertThat(instance, not(nullValue())); + assertThat(instance.getParsedInputData().isEmpty(), is(true)); + } + + @Test + public void testInputHandlerInitialization() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + + assertThat(instance, not(nullValue())); + } + + @Test + public void testGetParsedInputDataWithCorrectInput() throws ExceptionReport, XmlException, IOException { + System.out.println("Testing testInputHandlerInitialization..."); + InputHandler instance = new InputHandler.Builder(inputArray, "org.n52.wps.server.algorithm.SimpleBufferAlgorithm").build(); + + assertThat(instance.getParsedInputData().isEmpty(), is(false)); + assertThat(instance.getParsedInputData().size(), equalTo(2)); + assertThat(instance.getParsedInputData().keySet().size(), equalTo(2)); + assertThat(instance.getParsedInputData().keySet(), containsInAnyOrder("width", "data")); + assertThat(instance.getParsedInputData().get("data").size(), equalTo(1)); + assertThat(instance.getParsedInputData().get("width").size(), equalTo(1)); + + IData width = instance.getParsedInputData().get("width").get(0); + IData data = instance.getParsedInputData().get("data").get(0); + + assertThat(data, is(notNullValue())); + assertThat(data.getSupportedClass().getName(), is(equalToIgnoringCase("org.geotools.feature.FeatureCollection"))); + assertThat(data.getPayload(), is(notNullValue())); + assertThat(((DefaultFeatureCollection) data.getPayload()).getID(), is(equalToIgnoringCase("featureCollection"))); + assertThat(((DefaultFeatureCollection) data.getPayload()).getSchema().getTypeName(), is(equalToIgnoringCase("tasmania_roads"))); + assertThat(((DefaultFeatureCollection) data.getPayload()).getSchema().getAttributeCount(), equalTo(7)); + assertThat(((DefaultFeatureCollection) data.getPayload()).fids().size(), equalTo(14)); + assertThat(((DefaultFeatureCollection) data.getPayload()).fids().toArray()[0].toString(), is(equalToIgnoringCase("tasmania_roads.1"))); + assertThat(((DefaultFeatureCollection) data.getPayload()).fids().toArray()[13].toString(), is(equalToIgnoringCase("tasmania_roads.9"))); + assertThat(((DefaultFeatureCollection) data.getPayload()).getBounds().toString(), is(equalToIgnoringCase("ReferencedEnvelope[145.19754 : 148.27298000000002, -43.423512 : -40.852802]"))); + assertThat(((DefaultFeatureCollection) data.getPayload()).getBounds().getArea(), equalTo(7.906064362400054d)); + assertThat(((DefaultFeatureCollection) data.getPayload()).getBounds().getDimension(), equalTo(2)); + + assertThat(width, is(notNullValue())); + assertThat(((Double)width.getPayload()), equalTo(20.0d)); + + } +} diff --git a/src/test/java/org/n52/wps/server/request/WPSConfigTestUtil.java b/src/test/java/org/n52/wps/server/request/WPSConfigTestUtil.java new file mode 100644 index 0000000..ce68744 --- /dev/null +++ b/src/test/java/org/n52/wps/server/request/WPSConfigTestUtil.java @@ -0,0 +1,61 @@ +/** + * 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.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.xmlbeans.XmlException; +import org.n52.wps.commons.WPSConfig; + +/** + * + * @author tkunicki + */ +public class WPSConfigTestUtil { + + public static void generateMockConfig(String path) throws XmlException, IOException { + generateMockConfig(WPSConfigTestUtil.class, path); + } + + public static void generateMockConfig(Class clazz, String path) throws XmlException, IOException { + + InputStream configInputStream = null; + try { + configInputStream = new BufferedInputStream(clazz.getResourceAsStream(path)); + WPSConfig.forceInitialization(configInputStream); + + } finally { + if (configInputStream != null) { + try { configInputStream.close(); } catch (IOException ignore) {} + } + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/n52/wps/server/response/ExecuteResponseBuilderTest.java b/src/test/java/org/n52/wps/server/response/ExecuteResponseBuilderTest.java new file mode 100644 index 0000000..8a9daf3 --- /dev/null +++ b/src/test/java/org/n52/wps/server/response/ExecuteResponseBuilderTest.java @@ -0,0 +1,379 @@ +/** + * 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.response; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilderFactory; + +import net.opengis.wps.x100.DocumentOutputDefinitionType; +import net.opengis.wps.x100.OutputDefinitionType; + +import org.apache.xmlbeans.XmlException; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.w3c.dom.Document; + +import org.n52.wps.server.request.ExecuteRequest; +import org.n52.wps.server.request.InputHandlerTest; +import org.n52.wps.server.request.WPSConfigTestUtil; + +/** + * This class tests the getMimeType method of the ExecuteResponseBuilder class. + * TODO: Enhance with multiple in-/output tests + * + * @author Benjamin Pross(bpross-52n) + * + */ +public class ExecuteResponseBuilderTest { + + private ExecuteRequest executeRequest; + private DocumentBuilderFactory fac; + + @BeforeClass + public static void setupClass() throws XmlException, IOException { + WPSConfigTestUtil.generateMockConfig(InputHandlerTest.class, "/org/n52/wps/io/test/inputhandler/generator/wps_config.xml"); + } + + @Before + public void setUp() throws Exception { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", + "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); + + fac = DocumentBuilderFactory.newInstance(); + fac.setNamespaceAware(true); + } + + @Test + public void testGetMimeTypeLiteralOutputResponseDoc() { + + try { + String sampleFileName = "src/test/resources/DTCExecuteLiteralOutputResponseDoc.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + /* + * only one output here + */ + OutputDefinitionType definition = executeRequest.getExecute().getResponseForm().getResponseDocument().getOutputArray(0); + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(definition); + + /* + * this should be text/plain as LiteralData was requested + */ + assertTrue(mimeType.equals("text/plain")); + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeLiteralOutputRawData() { + + try { + String sampleFileName = "src/test/resources/DTCExecuteLiteralOutputRawData.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + /* + * only one output here + */ + OutputDefinitionType definition = executeRequest.getExecute().getResponseForm().getRawDataOutput(); + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(definition); + + /* + * this should be text/plain as LiteralData was requested + */ + assertTrue(mimeType.equals("text/plain")); + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeComplexOutputRawData() { + + try { + String sampleFileName = "src/test/resources/DTCExecuteComplexOutputRawDataMimeTiff.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is; + is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc; + doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + /* + * only one output here + */ + OutputDefinitionType definition = executeRequest.getExecute().getResponseForm().getRawDataOutput(); + String originalMimeType = definition.getMimeType(); + + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(definition); + + /* + * this should be the same mime type as requested + */ + assertTrue(mimeType.equals(originalMimeType)); + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeComplexOutputResponseDoc() { + + try { + String sampleFileName = "src/test/resources/DTCExecuteComplexOutputResponseDocMimeTiff.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is; + is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc; + doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + /* + * only one output here + */ + OutputDefinitionType definition = executeRequest.getExecute().getResponseForm().getResponseDocument().getOutputArray(0); + String originalMimeType = definition.getMimeType(); + + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(definition); + + /* + * this should be the same mime type as requested + */ + assertTrue(mimeType.equals(originalMimeType)); + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeMultipleComplexOutputsResponseDocPerm1() { + + try { + String sampleFileName = "src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm1.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is; + is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc; + doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + DocumentOutputDefinitionType[] outputs = executeRequest.getExecute().getResponseForm().getResponseDocument().getOutputArray(); + + for (DocumentOutputDefinitionType documentOutputDefinitionType : outputs) { + + String identifier = documentOutputDefinitionType.getIdentifier().getStringValue(); + + String originalMimeType = documentOutputDefinitionType.getMimeType(); + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(documentOutputDefinitionType); + + if(identifier.contains("Complex")){ + assertTrue(mimeType.equals(originalMimeType)); + }else{ + assertTrue(mimeType.equals("text/plain") || mimeType.equals("text/xml")); + } + + } + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeMultipleComplexOutputsResponseDocPerm2() { + + try { + String sampleFileName = "src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm2.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is; + is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc; + doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + DocumentOutputDefinitionType[] outputs = executeRequest.getExecute().getResponseForm().getResponseDocument().getOutputArray(); + + for (DocumentOutputDefinitionType documentOutputDefinitionType : outputs) { + + String identifier = documentOutputDefinitionType.getIdentifier().getStringValue(); + + String originalMimeType = documentOutputDefinitionType.getMimeType(); + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(documentOutputDefinitionType); + + if(identifier.contains("Complex")){ + assertTrue(mimeType.equals(originalMimeType)); + }else{ + assertTrue(mimeType.equals("text/plain") || mimeType.equals("text/xml")); + } + + } + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeBBOXOutputResponseDoc() { + + try { + String sampleFileName = "src/test/resources/DTCExecuteBBOXOutputResponseDoc.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is; + is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc; + doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + /* + * only one output here + */ + OutputDefinitionType definition = executeRequest.getExecute().getResponseForm().getResponseDocument().getOutputArray(0); + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(definition); + + /* + * this should be text/xml as BBOXData was requested + */ + assertTrue(mimeType.equals("text/xml")); + + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testGetMimeTypeBBOXOutputRawData() { + + try { + String sampleFileName = "src/test/resources/DTCExecuteBBOXOutputRawData.xml"; + File sampleFile = new File(sampleFileName); + + FileInputStream is; + is = new FileInputStream(sampleFile); + + // parse the InputStream to create a Document + Document doc; + doc = fac.newDocumentBuilder().parse(is); + + is.close(); + + executeRequest = new ExecuteRequest(doc); + + /* + * only one output here + */ + OutputDefinitionType definition = executeRequest.getExecute().getResponseForm().getRawDataOutput(); + + String mimeType = executeRequest.getExecuteResponseBuilder() + .getMimeType(definition); + + /* + * this should be text/xml as BBOXData was requested + */ + assertTrue(mimeType.equals("text/xml")); + + } catch (Exception e) { + fail(e.getMessage()); + } + } + +} diff --git a/src/test/java/org/n52/wps/server/response/OutputDataItemTest.java b/src/test/java/org/n52/wps/server/response/OutputDataItemTest.java new file mode 100644 index 0000000..111fdfb --- /dev/null +++ b/src/test/java/org/n52/wps/server/response/OutputDataItemTest.java @@ -0,0 +1,256 @@ +/** + * 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.response; + +import static org.junit.Assert.assertTrue; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; + +import net.opengis.ows.x11.LanguageStringType; +import net.opengis.wps.x100.ExecuteResponseDocument; +import net.opengis.wps.x100.ExecuteResponseDocument.ExecuteResponse; +import net.opengis.wps.x100.LiteralOutputType; +import net.opengis.wps.x100.OutputDescriptionType; +import net.opengis.wps.x100.ProcessBriefType; +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType.ProcessOutputs; +import net.opengis.wps.x100.ProcessDescriptionsDocument; + +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlOptions; +import org.apache.xmlbeans.XmlValidationError; +import org.junit.Before; +import org.junit.Test; +import org.n52.wps.io.BasicXMLTypeFactory; +import org.n52.wps.io.data.ILiteralData; +import org.n52.wps.io.data.binding.literal.LiteralAnyURIBinding; +import org.n52.wps.io.data.binding.literal.LiteralBase64BinaryBinding; +import org.n52.wps.io.data.binding.literal.LiteralBooleanBinding; +import org.n52.wps.io.data.binding.literal.LiteralByteBinding; +import org.n52.wps.io.data.binding.literal.LiteralDateTimeBinding; +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.io.data.binding.literal.LiteralStringBinding; +import org.n52.wps.server.CapabilitiesConfiguration; +import org.n52.wps.server.WebProcessingService; +import org.n52.wps.server.request.Request; + +/** + * @author BenjaminPross(bpross-52n) + * + * This class is for testing the updateResponseForLiteralData() method of the class OutputDataItem.java. + * + */ +public class OutputDataItemTest { + + private ProcessDescriptionType descriptionsType; + private String processID = "org.n52.wps.server.response.OutputDataItemTest"; + private ExecuteResponseDocument mockupResponseDocument; + private LanguageStringType outputTitle = LanguageStringType.Factory + .newInstance(); + private LanguageStringType processTitle = LanguageStringType.Factory + .newInstance(); + private Random random = new Random(); + private List literalDataList; + + @Before + public void setUp() { + + literalDataList = new ArrayList(); + + String url = ""; + try { + url = "http://52north.org"; + literalDataList.add(new LiteralAnyURIBinding(new URL(url).toURI())); + } catch (Exception e1) { + System.out.println(url + " caused " + e1); + } + + String uuid = UUID.randomUUID().toString(); + + literalDataList.add(new LiteralBase64BinaryBinding(uuid.getBytes())); + literalDataList.add(new LiteralBooleanBinding(true)); + literalDataList.add(new LiteralByteBinding((byte) 127)); + literalDataList.add(new LiteralDateTimeBinding(Calendar.getInstance() + .getTime())); + literalDataList.add(new LiteralDoubleBinding(random.nextDouble())); + literalDataList.add(new LiteralFloatBinding(random.nextFloat())); + literalDataList.add(new LiteralIntBinding(random.nextInt())); + literalDataList.add(new LiteralLongBinding(random.nextLong())); + literalDataList.add(new LiteralShortBinding((short) random.nextInt(Short.MAX_VALUE + 1))); + literalDataList.add(new LiteralStringBinding(uuid)); + + outputTitle.setStringValue("output title"); + processTitle.setStringValue("process title"); + + ProcessDescriptionsDocument descriptionsDocument = ProcessDescriptionsDocument.Factory + .newInstance(); + descriptionsType = descriptionsDocument.addNewProcessDescriptions() + .addNewProcessDescription(); + + descriptionsType.addNewIdentifier().setStringValue(processID); + + mockupResponseDocument = createExecuteResponseDocument(); + } + + private ExecuteResponseDocument createExecuteResponseDocument() { + + ExecuteResponseDocument doc = ExecuteResponseDocument.Factory + .newInstance(); + ExecuteResponse responseElem = doc.addNewExecuteResponse(); + XmlCursor c = doc.newCursor(); + c.toFirstChild(); + c.toLastAttribute(); + c.setAttributeText( + new QName(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, + "schemaLocation"), + "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd"); + responseElem.setServiceInstance(CapabilitiesConfiguration.ENDPOINT_URL + + "?REQUEST=GetCapabilities&SERVICE=WPS"); + responseElem.setLang(WebProcessingService.DEFAULT_LANGUAGE); + responseElem.setService("WPS"); + responseElem.setVersion(Request.SUPPORTED_VERSION); + ProcessBriefType process = responseElem.addNewProcess(); + process.addNewIdentifier().setStringValue(processID); + process.setProcessVersion("1.0.0"); + process.setTitle(processTitle); + responseElem.addNewStatus().setProcessSucceeded("Process successful"); + responseElem.getStatus().setCreationTime(Calendar.getInstance()); + + responseElem.addNewProcessOutputs(); + + return doc; + + } + + @Test + public void testUpdateResponseForLiteralData() { + + for (ILiteralData literalData : literalDataList) { + + try { + testLiteralOutput(literalData); + } catch (Exception e) { + System.out.println("Test failed for " + literalData.getClass() + + " " + e); + } + + mockupResponseDocument.getExecuteResponse().getProcessOutputs() + .removeOutput(0); + } + } + + private void testLiteralOutput(ILiteralData literalDataBinding) + throws Exception { + + String startText = "Testing " + literalDataBinding.getClass() + + " and value "; + + String endText = "ResponseDocument valid for " + + literalDataBinding.getClass() + " and value "; + + if (literalDataBinding.getPayload() instanceof byte[]) { + + byte[] bytes = (byte[]) literalDataBinding.getPayload(); + + String bytesAsIntegerValues = "["; + + for (int i = 0; i < bytes.length; i++) { + if(i < bytes.length -1){ + bytesAsIntegerValues = bytesAsIntegerValues + .concat((int) bytes[i] + ", "); + }else{ + bytesAsIntegerValues = bytesAsIntegerValues + .concat((int) bytes[i] + "]"); + } + } + startText = startText.concat("" + bytesAsIntegerValues); + endText = endText.concat("" + bytesAsIntegerValues); + } else { + startText = startText.concat("" + literalDataBinding.getPayload()); + endText = endText.concat("" + literalDataBinding.getPayload()); + } + + System.out.println(startText); + + ProcessOutputs processOutputs = descriptionsType.addNewProcessOutputs(); + OutputDescriptionType outputDescType = processOutputs.addNewOutput(); + outputDescType.addNewIdentifier().setStringValue("output"); + LiteralOutputType outputType = outputDescType.addNewLiteralOutput(); + + String dataTypeAsString = BasicXMLTypeFactory + .getXMLDataTypeforBinding(literalDataBinding.getClass()); + + outputType.addNewDataType().setStringValue(dataTypeAsString); + + OutputDataItem ouDI = new OutputDataItem(literalDataBinding, "output", + null, null, null, outputTitle, processID, descriptionsType); + + ouDI.updateResponseForLiteralData(mockupResponseDocument, + dataTypeAsString); + + assertTrue(validateResponseDocument(mockupResponseDocument)); + + System.out.println(endText); + System.out.println(); + } + + private boolean validateResponseDocument(ExecuteResponseDocument doc) { + XmlOptions xmlOptions = new XmlOptions(); + List xmlValidationErrorList = new ArrayList(); + xmlOptions.setErrorListener(xmlValidationErrorList); + boolean valid = doc.validate(xmlOptions); + if (!valid) { + System.err + .println("Error validating ExecuteResponseDocument for data type" + + doc.getExecuteResponse().getProcessOutputs() + .getOutputArray(0).getData() + .getLiteralData().getDataType()); + for (XmlValidationError xmlValidationError : xmlValidationErrorList) { + System.err.println("\tMessage: " + + xmlValidationError.getMessage()); + System.err.println("\tLocation of invalid XML: " + + xmlValidationError.getCursorLocation().xmlText()); + } + } + return valid; + } + +} diff --git a/src/test/java/org/n52/wps/server/response/RawDataTest.java b/src/test/java/org/n52/wps/server/response/RawDataTest.java new file mode 100644 index 0000000..956e2ff --- /dev/null +++ b/src/test/java/org/n52/wps/server/response/RawDataTest.java @@ -0,0 +1,129 @@ +/** + * 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.response; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; + +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.n52.wps.commons.WPSConfig; +import org.n52.wps.io.data.IData; +import org.n52.wps.io.data.binding.bbox.BoundingBoxData; +import org.n52.wps.server.IAlgorithm; +import org.n52.wps.server.algorithm.test.DummyTestClass; + +/** + * This class is for testing RawData output. + * + * @author Benjamin Pross (bpross-52n) + * + */ +public class RawDataTest { + + IAlgorithm algorithm; + ProcessDescriptionType processDescription; + String identifier; + + @BeforeClass + public static void setUpClass() { + try { + WPSConfig.forceInitialization("../52n-wps-webapp/src/main/webapp/config/wps_config.xml"); + } catch (XmlException ex) { + System.out.println(ex.getMessage()); + } catch (IOException ex) { + System.out.println(ex.getMessage()); + } + } + + @Before + public void setUp(){ + algorithm = new DummyTestClass(); + processDescription = algorithm.getDescription(); + identifier = algorithm.getWellKnownName(); + } + + @Test + public void testBBoxRawDataOutputCRS(){ + + IData envelope = new BoundingBoxData( + new double[] { 46, 102 }, + new double[] { 47, 103 }, "EPSG:4326"); + + InputStream is; + + try { + RawData bboxRawData = new RawData(envelope, "BBOXOutputData", null, null, null, identifier, processDescription); + + is = bboxRawData.getAsStream(); + + XmlObject bboxXMLObject = XmlObject.Factory.parse(is); + + assertTrue(bboxXMLObject != null); + + assertTrue(bboxXMLObject.getDomNode().getFirstChild().getNodeName().equals("wps:BoundingBoxData")); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void testBBoxRawDataOutput(){ + + IData envelope = new BoundingBoxData( + new double[] { 46, 102 }, + new double[] { 47, 103 }, null); + + InputStream is; + + try { + RawData bboxRawData = new RawData(envelope, "BBOXOutputData", null, null, null, identifier, processDescription); + + is = bboxRawData.getAsStream(); + + XmlObject bboxXMLObject = XmlObject.Factory.parse(is); + + assertTrue(bboxXMLObject != null); + + assertTrue(bboxXMLObject.getDomNode().getFirstChild().getNodeName().equals("wps:BoundingBoxData")); + } catch (Exception e) { + fail(e.getMessage()); + } + } + +} diff --git a/src/test/java/org/n52/wps/server/test/HttpInputTest.java b/src/test/java/org/n52/wps/server/test/HttpInputTest.java new file mode 100644 index 0000000..6a421ad --- /dev/null +++ b/src/test/java/org/n52/wps/server/test/HttpInputTest.java @@ -0,0 +1,84 @@ +/** + * 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.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import net.opengis.wps.x100.ExecuteDocument; +import net.opengis.wps.x100.InputType; + +import org.apache.xmlbeans.XmlException; +import org.junit.Assert; +import org.junit.Test; +import org.n52.wps.server.ExceptionReport; +import org.n52.wps.server.request.strategy.DefaultReferenceStrategy; +import org.n52.wps.server.request.strategy.IReferenceStrategy; + +public class HttpInputTest { + + static final String sampleFileName = "src/test/resources/InputTestExecuteSample.xml"; + + @Test + public void testHttpInput() throws XmlException, IOException{ + + + // Arrange + File sample = new File(sampleFileName); + ExecuteDocument execDoc = ExecuteDocument.Factory.parse(sample); + InputType[] inputArray = execDoc.getExecute().getDataInputs().getInputArray(); + + // Act & Assert + for (InputType currentInput : inputArray){ + System.out.println("Testing input " + currentInput.getIdentifier().getStringValue()); + + IReferenceStrategy strategy = new DefaultReferenceStrategy(); + try { + InputStream is = strategy.fetchData(currentInput); + Assert.assertNotNull(is); + printStream(is); + + } catch (ExceptionReport e) { + e.printStackTrace(); + } + } + + } + + private static void printStream (InputStream is) throws IOException{ + BufferedReader in = new BufferedReader(new InputStreamReader(is)); + String line = null; + while((line = in.readLine()) != null) { + System.out.println(line); + } + } +} diff --git a/src/test/java/org/n52/wps/server/test/UUIDTest.java b/src/test/java/org/n52/wps/server/test/UUIDTest.java new file mode 100644 index 0000000..010a84e --- /dev/null +++ b/src/test/java/org/n52/wps/server/test/UUIDTest.java @@ -0,0 +1,72 @@ +/** + * 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.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.n52.wps.server.RetrieveResultServlet; + +public class UUIDTest { + + RetrieveResultServlet resultServlet; + + @Before + public void setup(){ + resultServlet = new RetrieveResultServlet(); + } + + @Test + public void testUUIDValidationValidStatusID(){ + + String id1 = "615c4b53-13a6-4228-9bd2-5bc4a0b09e95"; + + assertTrue(resultServlet.isIDValid(id1)); + } + + @Test + public void testUUIDValidationValidResultID(){ + + String id2 = "615c4b53-13a6-4228-9bd2-5bc4a0b09e95result.49b52cb6-5fe6-4812-8faf-ac7bf338ee4d"; + + assertTrue(resultServlet.isIDValid(id2)); + } + + @Test + public void testUUIDValidationInvalidStatusID(){ + + String id3 = "1143eb2c-b93e-4340-8769-1437b621bd5e%3Cscript%3Ealert%28%27oh%20noes%27%29%3B%3C/script%3E"; + + assertFalse((resultServlet.isIDValid(id3))); + } + + +} diff --git a/src/test/resources/DTCExecuteBBOXOutputRawData.xml b/src/test/resources/DTCExecuteBBOXOutputRawData.xml new file mode 100644 index 0000000..7dea8fe --- /dev/null +++ b/src/test/resources/DTCExecuteBBOXOutputRawData.xml @@ -0,0 +1,24 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + BBOXInputData + Distance which people will walk to get to a playground. + + + 46.75 13.05 + 46.85 13.25 + + + + + + + BBOXOutputData + + + \ No newline at end of file diff --git a/src/test/resources/DTCExecuteBBOXOutputResponseDoc.xml b/src/test/resources/DTCExecuteBBOXOutputResponseDoc.xml new file mode 100644 index 0000000..c7bd83e --- /dev/null +++ b/src/test/resources/DTCExecuteBBOXOutputResponseDoc.xml @@ -0,0 +1,23 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + BBOXInputData + Distance which people will walk to get to a playground. + + + 46.75 13.05 + 46.85 13.25 + + + + + + + + BBOXOutputData + + + + \ No newline at end of file diff --git a/src/test/resources/DTCExecuteComplexOutputRawDataMimeTiff.xml b/src/test/resources/DTCExecuteComplexOutputRawDataMimeTiff.xml new file mode 100644 index 0000000..d1e71fe --- /dev/null +++ b/src/test/resources/DTCExecuteComplexOutputRawDataMimeTiff.xml @@ -0,0 +1,19 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + ComplexInputData + + + + + + ComplexOutputData + + + \ No newline at end of file diff --git a/src/test/resources/DTCExecuteComplexOutputResponseDocMimeTiff.xml b/src/test/resources/DTCExecuteComplexOutputResponseDocMimeTiff.xml new file mode 100644 index 0000000..616e67e --- /dev/null +++ b/src/test/resources/DTCExecuteComplexOutputResponseDocMimeTiff.xml @@ -0,0 +1,17 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + ComplexInputData + + + + + + + ComplexOutputData + + + + \ No newline at end of file diff --git a/src/test/resources/DTCExecuteLiteralOutputRawData.xml b/src/test/resources/DTCExecuteLiteralOutputRawData.xml new file mode 100644 index 0000000..f771c5e --- /dev/null +++ b/src/test/resources/DTCExecuteLiteralOutputRawData.xml @@ -0,0 +1,20 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + LiteralInputData + + 007 + + + + + + LiteralOutputData + + + \ No newline at end of file diff --git a/src/test/resources/DTCExecuteLiteralOutputResponseDoc.xml b/src/test/resources/DTCExecuteLiteralOutputResponseDoc.xml new file mode 100644 index 0000000..ddc8568 --- /dev/null +++ b/src/test/resources/DTCExecuteLiteralOutputResponseDoc.xml @@ -0,0 +1,19 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + LiteralInputData + + 007 + + + + + + + LiteralOutputData + + + + \ No newline at end of file diff --git a/src/test/resources/DummyTestClass.xml b/src/test/resources/DummyTestClass.xml new file mode 100644 index 0000000..ae70113 --- /dev/null +++ b/src/test/resources/DummyTestClass.xml @@ -0,0 +1,23 @@ + + + org.n52.wps.server.algorithm.test.DummyTestClass + + + BBOXInputData + Distance which people will walk to get to a playground. + + + 46.75 13.05 + 46.85 13.25 + + + + + + + + BBOXOutputData + + + + \ No newline at end of file diff --git a/src/test/resources/InputTestExecuteSample.xml b/src/test/resources/InputTestExecuteSample.xml new file mode 100644 index 0000000..d39d4fc --- /dev/null +++ b/src/test/resources/InputTestExecuteSample.xml @@ -0,0 +1,36 @@ + + + HTTP_INPUT_TEST + + + + + INPUT1 + + + + + + INPUT2 + + + + + 1.0.0 + + + + + + + + + + + + + + RESULT + + + \ No newline at end of file diff --git a/src/test/resources/LRDTCCorruptInputResponseDocStatusTrue.xml b/src/test/resources/LRDTCCorruptInputResponseDocStatusTrue.xml new file mode 100644 index 0000000..3248071 --- /dev/null +++ b/src/test/resources/LRDTCCorruptInputResponseDocStatusTrue.xml @@ -0,0 +1,19 @@ + + org.n52.wps.server.algorithm.test.LongRunningDummyTestClass + + + ComplexInputData + ComplexInputData + + + + + + + ComplexOutputData + + + + + + \ No newline at end of file diff --git a/src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm1.xml b/src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm1.xml new file mode 100644 index 0000000..ef0e4a2 --- /dev/null +++ b/src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm1.xml @@ -0,0 +1,47 @@ + + + org.n52.wps.server.algorithm.test.MultipleComplexInAndOutputsDummyTestClass + + + ComplexInputData1 + + + + ComplexInputData2 + + + + BBOXInputData + Distance which people will walk to get to a playground. + + + 46.75 13.05 + 46.85 13.25 + + + + + LiteralInputData + Distance which people will walk to get to a playground. + + 007 + + + + + + + BBOXOutputData + + + ComplexOutputData1 + + + ComplexOutputData2 + + + LiteralOutputData + + + + diff --git a/src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm2.xml b/src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm2.xml new file mode 100644 index 0000000..605dddb --- /dev/null +++ b/src/test/resources/MCIODTCExecuteComplexOutputResponseDocPerm2.xml @@ -0,0 +1,47 @@ + + + org.n52.wps.server.algorithm.test.MultipleComplexInAndOutputsDummyTestClass + + + ComplexInputData2 + + + + ComplexInputData1 + + + + BBOXInputData + Distance which people will walk to get to a playground. + + + 46.75 13.05 + 46.85 13.25 + + + + + LiteralInputData + Distance which people will walk to get to a playground. + + 007 + + + + + + + ComplexOutputData2 + + + ComplexOutputData1 + + + BBOXOutputData + + + LiteralOutputData + + + + diff --git a/src/test/resources/META-INF/services/org.n52.wps.server.IAlgorithm b/src/test/resources/META-INF/services/org.n52.wps.server.IAlgorithm new file mode 100644 index 0000000..1874855 --- /dev/null +++ b/src/test/resources/META-INF/services/org.n52.wps.server.IAlgorithm @@ -0,0 +1 @@ +org.n52.wps.server.CapabilitiesGetProcessDescriptionExceptionTest$InstantiationExceptionAlgorithm diff --git a/src/test/resources/SimpleBufferAlgorithm.xml b/src/test/resources/SimpleBufferAlgorithm.xml new file mode 100644 index 0000000..cad4bfa --- /dev/null +++ b/src/test/resources/SimpleBufferAlgorithm.xml @@ -0,0 +1,199 @@ + + + org.n52.wps.server.algorithm.SimpleBufferAlgorithm + + + data + + + + + + + + + + 146.46858200000003 -41.241478 146.574768 -41.251186 146.64041099999997 -41.255154 146.76612899999998 -41.332348 146.79418900000002 -41.34417 146.82217400000002 -41.362988 146.86343399999998 -41.380234 146.899521 -41.379452 146.929504 -41.378227 147.008041 -41.356079 147.098343 -41.362919 + + + + + street + + + + + + + 147.098343 -41.362919 147.17305 -41.452778 147.213867 -41.503773 147.234894 -41.546661 147.251129 -41.573826 147.26466399999998 -41.602474 147.28448500000002 -41.617554 147.30058300000002 -41.637878 + + + + + highway + + + + + + + 147.30058300000002 -41.637878 147.225815 -41.626938 147.18331899999998 -41.619236 147.08236699999998 -41.577755 147.03132599999998 -41.565205 146.96148699999998 -41.564186 146.92454500000002 -41.568565 146.876328 -41.569614 146.783722 -41.56073 146.684937 -41.536232 146.614258 -41.478153 146.61999500000002 -41.423958 146.582581 -41.365482 146.52478000000002 -41.29541 146.47749299999998 -41.277622 146.46858200000003 -41.241478 + + + + + lane + + + + + + + 147.522247 -41.859921 147.55186500000002 -41.927834 147.59732100000002 -42.017418 147.578644 -42.113216 147.541656 -42.217743 147.46867400000002 -42.22662 + + + + + highway + + + + + + + 146.103699 -41.171677 146.30361900000003 -41.237202 146.36222800000002 -41.236279 146.39418 -41.245384 146.44372600000003 -41.244308 146.46858200000003 -41.241478 + + + + + gravel + + + + + + + 145.856018 -41.08007 145.944839 -41.119896 146.03799400000003 -41.150059 146.103699 -41.171677 + + + + + road + + + + + + + 147.46867400000002 -42.22662 147.474945 -42.292259 147.467697 -42.301292 147.45182799999998 -42.341656 147.42454500000002 -42.378723 147.366013 -42.412552 147.345779 -42.432449 147.28932200000003 -42.476475 147.26451100000003 -42.503899 147.25991800000003 -42.547539 147.24940500000002 -42.614006 147.278351 -42.693249 147.284271 -42.757759 147.25674400000003 -42.778393 + + + + + highway + + + + + + + 148.249252 -41.860851 148.23443600000002 -41.901783 148.19212299999998 -41.93721 148.15576199999998 -41.953667 148.12773099999998 -41.994537 148.053131 -42.100563 + + + + + road + + + + + + + 145.19754 -40.878323 145.24667399999998 -40.86021 145.29328900000002 -40.852802 145.46522499999998 -40.897865 145.538498 -40.936264 145.554062 -40.939201 145.60211199999998 -40.962936 145.646362 -40.98243 145.68383799999998 -40.989883 145.71058699999998 -40.996201 145.74429300000003 -41.007545 145.80195600000002 -41.041782 145.856018 -41.08007 + + + + + logging + + + + + + + 147.360001 -42.91993 147.348816 -42.93726 147.28504900000001 -42.979027 147.220886 -42.995876 147.16442899999998 -43.027004 147.068237 -43.06319 146.96463 -43.116447 146.94955399999998 -43.17004 146.95369 -43.209591 146.96412700000002 -43.224545 146.97572300000002 -43.250484 146.98075899999998 -43.2701 146.98260499999998 -43.287716 146.970871 -43.31691 146.940521 -43.33812 146.94305400000002 -43.362263 146.95219400000002 -43.39278 146.95542899999998 -43.423512 + + + + + road + + + + + + + 147.30058300000002 -41.637878 147.372009 -41.695503 147.40258799999998 -41.725574 147.44406099999998 -41.749676 147.490433 -41.782482 147.506866 -41.795624 147.522919 -41.835609 147.522247 -41.859921 + + + + + highway + + + + + + + 148.053131 -42.100563 148.028229 -42.188286 148.00225799999998 -42.2295 147.96995500000003 -42.254417 147.96029700000003 -42.284897 147.942719 -42.398819 147.92640699999998 -42.486034 147.875092 -42.538582 147.832001 -42.587299 147.744217 -42.631607 147.69329800000003 -42.656067 147.61819500000001 -42.691135 147.57531699999998 -42.743092 147.57829299999997 -42.769539 147.54785199999998 -42.814312 147.50669900000003 -42.842907 147.488312 -42.877041 147.44969200000003 -42.901054 147.416809 -42.902828 + + + + + road + + + + + + + 147.098343 -41.362919 147.065445 -41.311977 147.02407799999997 -41.257534 146.981445 -41.211391 146.94822699999997 -41.181595 146.92677300000003 -41.172501 146.905029 -41.147144 146.940765 -41.085857 146.96266200000002 -41.075096 147.02108800000002 -41.080925 147.09922799999998 -41.123959 147.187607 -41.150597 147.28202800000003 -41.104244 147.29571499999997 -41.075798 147.30659500000002 -41.062832 147.32574499999998 -41.053524 147.36299100000002 -41.080441 147.41902199999998 -41.081764 147.46588100000002 -41.06089 147.51930199999998 -41.092793 147.528595 -41.137089 147.552521 -41.193565 147.594223 -41.233875 147.73440599999998 -41.239891 147.82937600000002 -41.196636 147.882614 -41.163197 147.91127 -41.163109 147.985168 -41.226128 148.022156 -41.292599 148.07511899999997 -41.313915 148.200104 -41.323097 148.23619100000002 -41.339245 148.27298000000002 -41.383488 148.25 -41.45713 148.254395 -41.53941 148.26243599999998 -41.585217 148.249252 -41.860851 + + + + + road + + + + + + + 147.25674400000003 -42.778393 147.22018400000002 -42.824776 147.179596 -42.82143 147.11132800000001 -42.795731 147.057098 -42.741581 147.00347900000003 -42.704803 146.91909800000002 -42.622734 146.91053799999997 -42.610928 146.88998400000003 -42.585396 146.83844 -42.572792 146.78569 -42.539352 146.724335 -42.485966 146.695023 -42.469582 146.64987200000002 -42.450371 146.604965 -42.432274 146.578781 -42.408531 146.539307 -42.364208 146.525055 -42.30883 146.558044 -42.275948 146.57624800000002 -42.23777 146.58146699999998 -42.203426 146.490005 -42.180222 146.3797 -42.146332 146.33406100000002 -42.138741 146.270966 -42.165703 146.197296 -42.224072 146.167908 -42.244835 146.16493200000002 -42.245171 146.111023 -42.265202 146.03747600000003 -42.239738 145.981628 -42.187851 145.85391199999998 -42.133492 145.819611 -42.129154 145.72052000000002 -42.104084 145.61857600000002 -42.056023 145.541718 -42.027241 145.48628200000002 -41.983326 145.452744 -41.926544 145.494034 -41.896477 145.59173600000003 -41.860214 145.64211999999998 -41.838398 145.669449 -41.830734 145.680923 -41.795753 145.68296800000002 -41.743221 145.67515600000002 -41.710377 145.680115 -41.688908 145.70106500000003 -41.648228 145.71479799999997 -41.609509 145.62919599999998 -41.462051 145.64889499999998 -41.470337 145.633423 -41.420902 145.631866 -41.36528 145.640854 -41.301533 145.700424 -41.242611 145.77242999999999 -41.193897 145.80233800000002 -41.161488 145.856018 -41.08007 + + + + + road + + + + + + + + width + Distance which people will walk to get to a playground. + + 20 + + + + + + + result + + + + \ No newline at end of file diff --git a/src/test/resources/org/n52/wps/io/test/inputhandler/generator/wps_config.xml b/src/test/resources/org/n52/wps/io/test/inputhandler/generator/wps_config.xml new file mode 100644 index 0000000..5b7305e --- /dev/null +++ b/src/test/resources/org/n52/wps/io/test/inputhandler/generator/wps_config.xml @@ -0,0 +1,1000 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + application/x-zipped-wkt + Base64 + + + + + + + + + + + + + + + + + + + + + + + + false + 4 + + + + + + + + + + + false + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + admin + geoserver + localhost + 8181 + + + + + admin + geoserver + localhost + 8181 + + + + admin + geoserver + localhost + 8181 + + + + + + + + + + + + R\scripts + R\resources + false + org.n52.wps.server.r.SosPlot + org.n52.wps.server.r.pegel-report + org.n52.wps.server.r.sweave-foo + + + org.n52.wps.server.algorithm.SimpleBufferAlgorithm + org.n52.wps.server.algorithm.coordinatetransform.CoordinateTransformAlgorithm + org.n52.wps.server.algorithm.simplify.DouglasPeuckerAlgorithm + org.n52.wps.server.algorithm.intersection.IntersectionAlgorithm + org.n52.wps.server.algorithm.convexhull.ConvexHullAlgorithm + org.n52.wps.server.algorithm.raster.AddRasterValues + org.n52.wps.server.algorithm.spatialquery.IntersectsAlgorithm + org.n52.wps.server.algorithm.spatialquery.TouchesAlgorithm + org.n52.wps.server.algorithm.test.DummyTestClass + org.n52.wps.server.algorithm.test.LongRunningDummyTestClass + org.n52.wps.server.algorithm.test.MultipleComplexInAndOutputsDummyTestClass + org.n52.wps.server.algorithm.test.MultiReferenceInputAlgorithm + org.n52.wps.server.algorithm.test.MultiReferenceBinaryInputAlgorithm + + + + + org.n52.wps.ags.AGSAnalysisViaWorkspaceProvider + org.n52.wps.aviation.AIXMIntersection + org.n52.wps.aviation.spatialrelation.AIXMContains + org.n52.wps.aviation.spatialrelation.AIXMCovers + org.n52.wps.aviation.spatialrelation.AIXMCrosses + org.n52.wps.aviation.spatialrelation.AIXMDisjoint + org.n52.wps.aviation.spatialrelation.AIXMEquals + org.n52.wps.aviation.spatialrelation.AIXMIntersects + org.n52.wps.aviation.spatialrelation.AIXMOverlaps + org.n52.wps.aviation.spatialrelation.AIXMTouches + org.n52.wps.aviation.spatialrelation.AIXMWithin + + + c:\programme\GRASS 7.0.svn + c:\python25 + c:\python25 + C:\grass\wps-grass-bridge2\gms\ + c:\programme\GRASS 7.0.svn\demolocation\.grassrc70 + C:\tmp\grass_tmp + i.atcorr + i.biomass + i.cca + i.cluster + i.eb.eta + i.eb.evapfr + i.eb.h_SEBAL01 + i.eb.soilheatflux + i.emissivity + i.evapo.time_integration + i.fft + i.gensig + i.gensigset + i.his.rgb + i.ifft + i.landsat.acca + i.landsat.toar + i.latlong + i.maxlik + i.modis.qc + i.pca + i.rectify + i.rgb.his + i.smap + i.sunhours + i.target + i.vi + i.zc + m.cogo + m.measure + r.basins.fill + r.bitpattern + r.buffer2 + r.carve + r.category + r.circle + r.clump + r.coin + r.compress + r.contour + r.cost + r.covar + r.cross + r.describe + r.distance + r.drain + r.fill.dir + r.flow + r.grow.distance + r.gwflow + r.his + r.horizon + r.kappa + r.lake + r.li.cwed + r.li.dominance + r.li.edgedensity + r.li.mpa + r.li.mps + r.li.padcv + r.li.padrange + r.li.padsd + r.li.patchdensity + r.li.patchnum + r.li.richness + r.li.shannon + r.li.shape + r.li.simpson + r.los + r.mapcalc + r.mfilter + r.mode + r.neighbors + r.null + r.param.scale + r.patch + r.profile + r.proj + r.quant + r.quantile + r.random.cells + r.random + r.random.surface + r.reclass + r.recode + r.region + r.regression.line + r.report + r.resamp.bspline + r.resamp.filter + r.resamp.interp + r.resamp.rst + r.resamp.stats + r.resample + r.rescale.eq + r.rescale + r.ros + r.series + r.sim.sediment + r.sim.water + r.slope.aspect + r.solute.transport + r.spread + r.spreadpath + r.statistics + r.statistics2 + r.statistics3 + r.stats + r.sun + r.sunmask + r.support + r.support.stats + r.surf.area + r.surf.contour + r.surf.fractal + r.surf.gauss + r.surf.idw + r.surf.idw2 + r.surf.random + r.terraflow + r.texture + r.thin + r.timestamp + r.to.rast3 + r.to.rast3elev + r.to.vect + r.topidx + r.topmodel + r.transect + r.univar + r.uslek + r.usler + r.volume + r.walk + r.water.outlet + r.watershed + r3.gwflow + r3.info + r3.mapcalc + r3.mask + r3.null + r3.stats + r3.timestamp + r3.to.rast + r3.univar + v.buffer + v.build + v.build.polylines + v.category + v.class + v.convert + v.db.connect + v.db.select + v.delaunay + v.distance + v.drape + v.edit + v.extract + v.extrude + v.generalize + v.hull + v.kcv + v.kernel + v.label + v.lidar.correction + v.lidar.edgedetection + v.lidar.growing + v.lrs.create + v.lrs.label + v.lrs.segment + v.lrs.where + v.mkgrid + v.neighbors + v.net.alloc + v.net.allpairs + v.net.bridge + v.net.centrality + v.net.components + v.net.connectivity + v.net.distance + v.net + v.net.flow + v.net.iso + v.net.path + v.net.salesman + v.net.spanningtree + v.net.steiner + v.net.timetable + v.net.visibility + v.normal + v.outlier + v.overlay + v.parallel + v.patch + v.perturb + v.proj + v.qcount + v.random + v.reclass + v.sample + v.segment + v.select + v.split + v.support + v.surf.bspline + v.surf.idw + v.surf.rst + v.to.3d + v.to.db + v.to.points + v.to.rast + v.to.rast3 + v.transform + v.type + v.univar + v.vol.rst + v.voronoi + + + + + + + + + visibility + recttopolar + crossclassification + aggregationindex + neighborhoodvariance + clipgrid + createhyetogram + randomvector + slope + autoincrementvalue + costinroutesanisotropic + convergence + neighborhooddiversity + neighborhoodcountlowerthan + removeholes + delaunay + acccostcombined + solarradiation + distanceandangle + aspect + vectorizelines + los + histogram + calibrateregression + disttochannelnetwork + multigridcountgreaterthan + spatialcorrelation + ripleysk + transform + costinroutes + vectorfieldcorrelation + pdfnormal + multigridmajority + quadrat + fresnellos + vectormean + ahp + vectorfieldcalculator + intersection + neighborhoodcountequalto + watershedsbysize + locateextremevalues + upslopeareafromarea + neighborhoodcountgreaterthan + rastertopoints + neighborhoodminority + his2rgb + pointcoordinates + geometricpropertieslines + splitlineswithpoints + channelnetwork + groupnearfeatures + fitnpointsinpolygon + splitpolylinesatnodes + ndvi + isocrones + hypsometry + normalitytest + cleanpointslayer + mediancenter + calibrate + equalize + reclassifydisjoint + realarea + changelinedirection + cfactorfromndvi + polygonstatisticsfrompoints + centroids + generateroutes + strahlerorder + difference + multigridminority + simplifylines + nrvi + fragstatsdiversity + changedatatype + density + thinning + clipbyrectangle + reclassifyconsecutive + leastcostpath + croptovaliddata + distancematrix + multigridkurtosis + acccost + tablebasicstats + splitmultipart + variabledistancebuffer + upslopeareafrompoint + thresholdbuffer + classstatistics + profile + clip + idw + mergegrids + crosssections + edgecontamination + multigridmedian + gridorientation + flowlineprofile + curvatures + rasterizevectorlayer + symdifference + multigridvariance + gradientlines + perturbatepointslayer + multigridskewness + contourlines + neighborhoodmeanvalue + aggregate + neighborhoodrange + pdfchisquared + regression + guh + neighborhooddominance + horizonblockage + linestoequispacedpoints + maxvalueuphill + neighborhoodfragmentation + slopelength + savetowkt + neighborhoodmajority + reclassifyequalarea + simplifypolygons + meancenter + minimumenclosingpolygon + roc + hillshade + topographicindices + elevationreliefindex + contraststretching + vectorspatialcluster + merge + pvirichardson + ttvi + fragstatsarea + predictivemodels + union + multigridcountlowerthan + heightoverchannelnetwork + meanvalueuphill + snappoints + gridcompletion + multigridmaxvaluegrid + surfacespecificpoints + neighborhoodminvalue + sortraster + graticulebuilder + visualexposure + multigridminvalue + pointseriesanalysis + countpoints + lacunarity + cva + neighborhoodvarianceradius + erosiondilation + gridbasicstats + nnanalysis + protectionindex + exportvector + gridsfromtableandgrid + watersheds + ctvi + cellbalance + polylinestopolygons + pdfstudent + universalkriging + polartorect + tvi + pdfbinomial + tablefieldcorrelation + lineardecrease + multipleregression + pdfexponential + neighborhoodnumberofclasses + correlation + geometricproperties + gridstatisticsinpolygons + polylinestosinglesegments + cleanvectorlayer + accflow + multigridmeanvalue + polygonstopolylines + multigridmaxvalue + neighborhoodskewness + multigridrange + dissolve + semivariances + filterclumps + multigridcountequalto + reclassifyequalamplitude + removerepeatedgeometries + kriging + pviwalther + neighborhoodmaxvalue + multigridminvaluegrid + smoothlines + neighborhoodmedian + geometriestopoints + rasterbuffer + covariancematrix + invertnodata + vectorbasicstats + sumofcostfromallpoints + neighborhoodkurtosis + closegapsnn + fuzzify + acccostanisotropic + addeventtheme + pviqi + vectorcluster + vectorize + pviperry + boundingbox + closegaps + usped + vectorhistogram + separateshapes + fillsinks + acv + locateallocate + fixeddistancebuffer + kerneldensity + rgb2his + + + + + + + + + false + + +