commit b95572fe28c09b2bf1cc80739b2f3376d252b13f Author: Lucio Lelii Date: Fri Jul 28 15:59:17 2017 +0000 branch for release 4.6.1 git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/branches/data-analysis/52n-wps-algorithm-d4s/3.6@151432 82a268e6-3cf1-43bd-a215-b396298e98cf 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..134f3a6 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + 52n-wps-algorithm-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/LICENSE b/LICENSE new file mode 100644 index 0000000..b09cd78 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..cfafb5a --- /dev/null +++ b/NOTICE @@ -0,0 +1,38 @@ +Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source +Software GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This project includes: + 52North WPS Algorithm API under Apache License, Version 2.0 + 52North WPS Commons under Apache License, Version 2.0 + 52North WPS Input/Output API under Apache License, Version 2.0 + Commons Codec under The Apache Software License, Version 2.0 + Commons IO under The Apache Software License, Version 2.0 + Guava: Google Core Libraries for Java under The Apache Software License, Version 2.0 + Hamcrest Core under New BSD License + Joda time under Apache 2 + JUnit under Common Public License Version 1.0 + OGC OWS schema (spec. v1.1.0) under The Apache Software License, Version 2.0 + OGC WPS schema (spec. v1.0.0) under The Apache Software License, Version 2.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + stax-utils under BSD + Stax2 API under The BSD License + W3C xlink schema (spec. v1.1.0) under The Apache Software License, Version 2.0 + Woodstox under The Apache Software License, Version 2.0 + WPS-Config under The Apache Software License, Version 2.0 + Xalan Java under The Apache Software License, Version 2.0 + Xalan Java Serializer under The Apache Software License, Version 2.0 + XmlBeans under The Apache Software License, Version 2.0 + 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..2a7342c --- /dev/null +++ b/pom.xml @@ -0,0 +1,142 @@ + + + + maven-parent + org.gcube.tools + 1.0.0 + + + 4.0.0 + org.gcube.dataanalysis + 52n-wps-algorithm-gcube + 52North WPS Algorithm API modified for gcube + 3.6.1-SNAPSHOT + API for implementation of algorithms to be exposed as web processes + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + + + + + + + src/main/resources + + **/*.xml + + + + . + + LICENSE + NOTICE + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + test-jar + + + + + + com.mycila + license-maven-plugin + +
../misc/licenses/license_header_for_api_modules.txt
+
+
+ + org.jasig.maven + maven-notice-plugin + + ../misc/licenses/NOTICE_for_api_modules.template + + ../misc/licenses/license-mappings.xml + + + + + check-licenses + + + + +
+
+ + + javassist + javassist + 3.12.1.GA + + + org.n52.wps + 52n-wps-commons + 3.6.1 + + + org.n52.wps + 52n-wps-io + 3.6.1 + + + 52n-xml-wps-v100 + org.n52.sensorweb + 2.3.0 + + + org.n52.wps + 52n-wps-config + 1.2.1 + + + com.google.guava + guava + 19.0 + + + org.slf4j + slf4j-api + 1.7.4 + + + org.slf4j + slf4j-simple + 1.7.4 + test + + + junit + junit + 4.12 + 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..346f157 --- /dev/null +++ b/src/license/THIRD-PARTY.properties @@ -0,0 +1,50 @@ +# 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:01 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.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/algorithm/annotation/Algorithm.java b/src/main/java/org/n52/wps/algorithm/annotation/Algorithm.java new file mode 100644 index 0000000..4d1b4cb --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/Algorithm.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author tkunicki + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Algorithm { + String identifier() default ""; + String title() default ""; + String abstrakt() default ""; // 'abstract' is java reserved keyword + String version(); + boolean storeSupported() default true; + boolean statusSupported() default true; +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospector.java b/src/main/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospector.java new file mode 100644 index 0000000..73abf6f --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospector.java @@ -0,0 +1,280 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; + +import org.n52.wps.algorithm.annotation.AnnotationBinding.ExecuteMethodBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputBinding; +import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataInputFieldAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataInputMethodAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataOutputFieldAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.ComplexDataOutputMethodAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.ExecuteAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.InputAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataInputFieldAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataInputMethodAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataOutputFieldAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.LiteralDataOutputMethodAnnotationParser; +import org.n52.wps.algorithm.annotation.AnnotationParser.OutputAnnotationParser; +import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author tkunicki + */ +public class AnnotatedAlgorithmIntrospector { + + private final static Logger LOGGER = LoggerFactory.getLogger(AnnotatedAlgorithmIntrospector.class); + + private final static List> INPUT_FIELD_PARSERS; + private final static List> INPUT_METHOD_PARSERS; + private final static List> OUTPUT_FIELD_PARSERS; + private final static List> OUTPUT_METHOD_PARSERS; + private final static ExecuteAnnotationParser PROCESS_PARSER; + + static { + List> inputFieldParsers = + new ArrayList>(); + inputFieldParsers.add(new LiteralDataInputFieldAnnotationParser()); + inputFieldParsers.add(new ComplexDataInputFieldAnnotationParser()); + INPUT_FIELD_PARSERS = Collections.unmodifiableList(inputFieldParsers); + + List> inputMethodParsers = + new ArrayList>(); + inputMethodParsers.add(new LiteralDataInputMethodAnnotationParser()); + inputMethodParsers.add(new ComplexDataInputMethodAnnotationParser()); + INPUT_METHOD_PARSERS = Collections.unmodifiableList(inputMethodParsers); + + List> outputFieldParsers = + new ArrayList>(); + outputFieldParsers.add(new LiteralDataOutputFieldAnnotationParser()); + outputFieldParsers.add(new ComplexDataOutputFieldAnnotationParser()); + OUTPUT_FIELD_PARSERS = Collections.unmodifiableList(outputFieldParsers); + + List> outputMethodParsers = + new ArrayList>(); + outputMethodParsers.add(new LiteralDataOutputMethodAnnotationParser()); + outputMethodParsers.add(new ComplexDataOutputMethodAnnotationParser()); + OUTPUT_METHOD_PARSERS = Collections.unmodifiableList(outputMethodParsers); + + PROCESS_PARSER = new ExecuteAnnotationParser(); + } + + private final static Map, AnnotatedAlgorithmIntrospector> INTROSPECTOR_MAP = + new HashMap, AnnotatedAlgorithmIntrospector>(); + public static synchronized AnnotatedAlgorithmIntrospector getInstrospector(Class algorithmClass) { + AnnotatedAlgorithmIntrospector introspector = INTROSPECTOR_MAP.get(algorithmClass); + if (introspector == null) { + introspector = new AnnotatedAlgorithmIntrospector(algorithmClass); + INTROSPECTOR_MAP.put(algorithmClass, introspector); + } + return introspector; + } + + private Class algorithmClass; + + private AlgorithmDescriptor algorithmDescriptor; + + private ExecuteMethodBinding executeMethodBinding; + private Map> inputBindingMap; + private Map> outputBindingMap; + + + public AnnotatedAlgorithmIntrospector(Class algorithmClass) { + + this.algorithmClass = algorithmClass; + + inputBindingMap = new LinkedHashMap>(); + outputBindingMap = new LinkedHashMap>(); + + parseClass(); + + inputBindingMap = Collections.unmodifiableMap(inputBindingMap); + outputBindingMap = Collections.unmodifiableMap(outputBindingMap); + } + + //Modified by Gianpaolo Coro + private void parseClass() { + + if (!algorithmClass.isAnnotationPresent(Algorithm.class)) { + throw new RuntimeException("Class isn't annotated with an Algorithm annotation"); + } + + boolean validContructor = false; + try { + Constructor defaultConstructor = algorithmClass.getConstructor(new Class[0]); + validContructor = (defaultConstructor.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC; + } catch (NoSuchMethodException ex) { + // inherit error message on fall through... + } catch (SecurityException ex) { + throw new RuntimeException("Current security policy limits use of reflection, error introspecting " + algorithmClass.getName()); + } + if (!validContructor) { + throw new RuntimeException("Classes with Algorithm annotation require public no-arg constructor, error introspecting " + algorithmClass.getName()); + } + + + AlgorithmDescriptor.Builder algorithmBuilder = null; + + Algorithm algorithm = algorithmClass.getAnnotation(Algorithm.class); + + //MODIFICATIONS BY G CORO TO ACCOUNT FOR METHODS ORDER + List sortedMethods = new ArrayList(); + List methodsIdxs = new ArrayList(); + try{ + for (Method m:algorithmClass.getDeclaredMethods()){ + //System.out.println("Javaassist Analysing method "+m.getName()); + ClassPool pool = ClassPool.getDefault(); + ClassClassPath ccpath = new ClassClassPath(m.getDeclaringClass()); + pool.insertClassPath(ccpath); + //System.out.println("Javaassist searching for canonical name "+m.getDeclaringClass().getCanonicalName()); + CtClass cc = pool.get(m.getDeclaringClass().getCanonicalName()); + //System.out.println("Javaassist cc "+cc.getName()); + CtMethod javassistMethod = cc.getDeclaredMethod(m.getName()); + //System.out.println("Javaassist method "+javassistMethod.getName()); + + int linenumber = javassistMethod.getMethodInfo().getLineNumber(0); + //System.out.println("Javaassist line number "+linenumber); + int i=0; + for (Integer methodsIdx :methodsIdxs){ + if (methodsIdx>linenumber){ + break; + } + i++; + } + sortedMethods.add(i,m); + methodsIdxs.add(i,linenumber); + } + }catch(Exception e){ + LOGGER.warn("error getting method order",e); + } + + Method[] sMethods = sortedMethods.toArray(new Method[sortedMethods.size()]); + + algorithmBuilder = AlgorithmDescriptor.builder( + algorithm.identifier().length() > 0 ? + algorithm.identifier() : + algorithmClass.getCanonicalName()); + + + + + algorithmBuilder. + title(algorithm.title()). + abstrakt(algorithm.abstrakt()). + version(algorithm.version()). + storeSupported(algorithm.storeSupported()). + statusSupported(algorithm.statusSupported()); + + parseElements(sMethods, + INPUT_METHOD_PARSERS, + OUTPUT_METHOD_PARSERS); + parseElements(algorithmClass.getDeclaredFields(), + INPUT_FIELD_PARSERS, + OUTPUT_FIELD_PARSERS); + + + for (Method method : sMethods) { + if (method.isAnnotationPresent(PROCESS_PARSER.getSupportedAnnotation())) { + ExecuteMethodBinding executeMethodBinding = PROCESS_PARSER.parse(method); + if (executeMethodBinding != null) { + if (this.executeMethodBinding != null) { + // we need to error out here because ordering of getDeclaredMethods() or + // getMethods() is not guarenteed to be consistent, if it were consistent + // maybe we could ignore this state, but having an algorithm behave + // differently betweeen runtimes would be bad... + throw new RuntimeException("Multiple execute method bindings encountered for class " + getClass().getCanonicalName()); + } + this.executeMethodBinding = executeMethodBinding; + } + } + } + + if(this.executeMethodBinding == null) { + throw new RuntimeException("No execute method binding for class " + this.algorithmClass.getCanonicalName()); + } + + for (InputBinding inputBinding : inputBindingMap.values()) { + algorithmBuilder.addInputDescriptor(inputBinding.getDescriptor()); + } + for (OutputBinding outputBinding : outputBindingMap.values()) { + algorithmBuilder.addOutputDescriptor(outputBinding.getDescriptor()); + } + algorithmDescriptor = algorithmBuilder.build(); + } + + public AlgorithmDescriptor getAlgorithmDescriptor() { + return algorithmDescriptor; + } + + public ExecuteMethodBinding getExecuteMethodBinding() { + return executeMethodBinding; + } + + public Map> getInputBindingMap() { + return inputBindingMap; + } + + public Map> getOutputBindingMap() { + return outputBindingMap; + } + + public void parseElements( + M members[], + List> inputParser, + List> outputParser) { + for (M member : members) { + for (OutputAnnotationParser parser : outputParser) { + if (member.isAnnotationPresent(parser.getSupportedAnnotation())) { + OutputBinding binding = parser.parse(member); + if (binding != null) { + outputBindingMap.put(binding.getDescriptor().getIdentifier(), binding); + } + } + } + for (InputAnnotationParser parser : inputParser) { + if (member.isAnnotationPresent(parser.getSupportedAnnotation())) { + InputBinding binding = parser.parse(member); + if (binding != null) { + inputBindingMap.put(binding.getDescriptor().getIdentifier(), binding); + } + } + } + } + } + +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/AnnotationBinding.java b/src/main/java/org/n52/wps/algorithm/annotation/AnnotationBinding.java new file mode 100644 index 0000000..a4f4ff3 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/AnnotationBinding.java @@ -0,0 +1,476 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.List; +import org.n52.wps.algorithm.descriptor.BoundDescriptor; +import org.n52.wps.algorithm.descriptor.InputDescriptor; +import org.n52.wps.algorithm.descriptor.OutputDescriptor; +import org.n52.wps.algorithm.util.ClassUtil; +import org.n52.wps.io.data.IData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author tkunicki + */ +public abstract class AnnotationBinding { + + private final static Logger LOGGER = LoggerFactory.getLogger(AnnotationBinding.class); + + private M member; + + public AnnotationBinding(M member) { + this.member = member; + } + + public M getMember() { + return member; + } + + protected boolean checkModifier() { + return (getMember().getModifiers() & Modifier.PUBLIC) != 0; + } + + public abstract boolean validate(); + + public static class ExecuteMethodBinding extends AnnotationBinding { + + public ExecuteMethodBinding(Method method) { + super(method); + } + + @Override + public boolean validate() { + if (!checkModifier()) { + LOGGER.error("Method {} with Execute annotation can't be used, not public.", getMember()); + return false; + } + // eh, do we really need to care about this? + if (!getMember().getReturnType().equals(void.class)) { + LOGGER.error("Method {} with Execute annotation can't be used, return type not void", getMember()); + return false; + } + if (getMember().getParameterTypes().length != 0) { + LOGGER.error("Method {} with Execute annotation can't be used, method parameter count is > 0.", getMember()); + return false; + } + return true; + } + + public void execute(Object annotatedInstance) { + try { + getMember().invoke(annotatedInstance); + }catch (IllegalAccessException ex) { + throw new RuntimeException("Internal error executing process", ex); + } catch (IllegalArgumentException ex) { + throw new RuntimeException("Internal error executing process", ex); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause() == null ? ex : ex.getCause(); + throw new RuntimeException(cause.getMessage(), cause); + } + } + } + + public static abstract class DataBinding extends AnnotationBinding { + + private D descriptor; + + public DataBinding(M member) { + super(member); + } + + public void setDescriptor(D descriptor) { + this.descriptor = descriptor; + } + + public D getDescriptor() { + return descriptor; + } + + public abstract Type getMemberType(); + + public Type getType() { + return getMemberType(); + } + + public Type getPayloadType() { + Type type = getType(); + if (isTypeEnum()) { + return String.class; + } + if (type instanceof Class) { + Class inputClass = (Class) type; + if (inputClass.isPrimitive()) { + return ClassUtil.wrap(inputClass); + } + } + return type; + } + + public boolean isTypeEnum() { + Type inputType = getType(); + return (inputType instanceof Class) && ((Class) inputType).isEnum(); + } + } + + public static abstract class InputBinding extends DataBinding { + + public InputBinding(M member) { + super(member); + } + + @Override + public Type getType() { + Type memberType = getMemberType(); + Type inputType = memberType; + if (memberType instanceof Class) { + Class memberClass = (Class) memberType; + if (List.class.isAssignableFrom(memberClass)) { + // We treat List as List + inputType = NOT_PARAMETERIZED_TYPE; + } + } else if (memberType instanceof ParameterizedType) { + ParameterizedType parameterizedMemberType = (ParameterizedType) memberType; + Class rawClass = (Class) parameterizedMemberType.getRawType(); + if (List.class.isAssignableFrom(rawClass)) { + inputType = parameterizedMemberType.getActualTypeArguments()[0]; + } + } else { + LOGGER.error("Unable to infer concrete type information for " + getMember()); + } + return inputType; + } + + public boolean isMemberTypeList() { + Type memberType = getMemberType(); + if (memberType instanceof Class) { + return List.class.isAssignableFrom((Class) memberType); + } else if (memberType instanceof ParameterizedType) { + Class rawClass = (Class) ((ParameterizedType) memberType).getRawType(); + return List.class.isAssignableFrom(rawClass); + } else { + LOGGER.error("Unable to infer concrete type information for " + getMember()); + } + return false; + } + + protected boolean checkType() { + Type inputPayloadType = getPayloadType(); + Class bindingClass = getDescriptor().getBinding(); + try { + Class bindingPayloadClass = bindingClass.getMethod("getPayload", (Class[]) null).getReturnType(); + if (inputPayloadType instanceof Class) { + return ((Class) inputPayloadType).isAssignableFrom(bindingPayloadClass); + } else if (inputPayloadType instanceof ParameterizedType) { + // i.e. List> + return ((Class) ((ParameterizedType) inputPayloadType).getRawType()).isAssignableFrom(bindingPayloadClass); + } else if (inputPayloadType instanceof WildcardType) { + // i.e. List or List + WildcardType inputTypeWildcardType = (WildcardType) inputPayloadType; + Type[] lowerBounds = inputTypeWildcardType.getLowerBounds(); + Type[] upperBounds = inputTypeWildcardType.getUpperBounds(); + Class lowerBoundClass = null; + Class upperBoundClass = null; + if (lowerBounds != null && lowerBounds.length > 0) { + if (lowerBounds[0] instanceof Class) { + lowerBoundClass = (Class) lowerBounds[0]; + } else if (lowerBounds[0] instanceof ParameterizedType) { + lowerBoundClass = (Class) ((ParameterizedType) lowerBounds[0]).getRawType(); + } + } + if (upperBounds != null && upperBounds.length > 0) { + if (upperBounds[0] instanceof Class) { + upperBoundClass = (Class) upperBounds[0]; + } else if (upperBounds[0] instanceof ParameterizedType) { + upperBoundClass = (Class) ((ParameterizedType) upperBounds[0]).getRawType(); + } + } + return (upperBoundClass == null || upperBoundClass.isAssignableFrom(bindingPayloadClass)) && (lowerBounds == null || bindingPayloadClass.isAssignableFrom(lowerBoundClass)); + } else { + LOGGER.error("Unable to infer assignability from type for " + getMember()); + } + } catch (NoSuchMethodException e) { + return false; + } + return false; + } + + public Object unbindInput(List boundValueList) { + Object value = null; + if (boundValueList != null && boundValueList.size() > 0) { + if (isMemberTypeList()) { + List valueList = new ArrayList(boundValueList.size()); + for (IData bound : boundValueList) { + value = bound.getPayload(); + if (isTypeEnum()) { + value = Enum.valueOf((Class)getType(), (String)value); + } + valueList.add(value); + } + value = valueList; + } else if (boundValueList.size() == 1) { + value = boundValueList.get(0).getPayload(); + if (isTypeEnum()) { + value = Enum.valueOf((Class)getType(), (String)value); + } + } + } + return value; + } + + public abstract void set(Object annotatedObject, List boundInputList); + } + + public static abstract class OutputBinding extends DataBinding { + + private Constructor bindingConstructor; + + public OutputBinding(M member) { + super(member); + } + + protected boolean checkType( ) { + return getConstructor() != null; + } + + public IData bindOutputValue(Object outputValue) { + try { + if (isTypeEnum()) { + outputValue = ((Enum)outputValue).name(); + } + return getConstructor().newInstance(outputValue); + } catch (InstantiationException ex) { + throw new RuntimeException("Internal error processing outputs", ex); + } catch (SecurityException ex) { + throw new RuntimeException("Internal error processing outputs", ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Internal error processing outputs", ex); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause() == null ? ex : ex.getCause(); + throw new RuntimeException(cause.getMessage(), cause); + } + } + + public abstract IData get(Object annotatedInstance); + + private synchronized Constructor getConstructor() { + if (bindingConstructor == null ){ + try { + Class bindingClass = getDescriptor().getBinding(); + Class outputPayloadClass = bindingClass.getMethod("getPayload", (Class[]) null).getReturnType(); + Type bindingPayloadType = getPayloadType(); + if (bindingPayloadType instanceof Class) { + Class bindingPayloadClass = (Class) bindingPayloadType; + if (bindingPayloadClass.isAssignableFrom(outputPayloadClass)) { + bindingConstructor = bindingClass.getConstructor(bindingPayloadClass); + } + } + } catch (NoSuchMethodException e) { + // error handling on fall-through + } + } + return bindingConstructor; + } + } + + public static class InputFieldBinding extends InputBinding { + + public InputFieldBinding(Field field) { + super(field); + } + + @Override + public Type getMemberType() { + return getMember().getGenericType(); + } + + @Override + public boolean validate() { + if (!checkModifier()) { + LOGGER.error("Field {} with input annotation can't be used, not public.", getMember()); + return false; + } + if (!(getDescriptor().getMaxOccurs().intValue() < 2 || isMemberTypeList())) { + LOGGER.error("Field {} with input annotation can't be used, maxOccurs > 1 and field is not of type List", getMember()); + return false; + } + if (!checkType()) { + LOGGER.error("Field {} with input annotation can't be used, unable to safely assign field using binding payload type", getMember()); + return false; + } + return true; + } + + @Override + public void set(Object annotatedObject, List boundInputList) { + try { + getMember().set(annotatedObject, unbindInput(boundInputList)); + } catch (IllegalArgumentException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } + } + } + + public static class InputMethodBinding extends InputBinding { + + public InputMethodBinding(Method method) { + super(method); + } + + @Override + public Type getMemberType() { + Type[] genericParameterTypes = getMember().getGenericParameterTypes(); + return (genericParameterTypes.length == 0) ? Void.class : genericParameterTypes[0]; + } + + @Override + public boolean validate() { + if (!checkModifier()) { + LOGGER.error("Field {} with input annotation can't be used, not public.", getMember()); + return false; + } + if (!(getDescriptor().getMaxOccurs().intValue() < 2 || isMemberTypeList())) { + LOGGER.error("Field {} with input annotation can't be used, maxOccurs > 1 and field is not of type List", getMember()); + return false; + } + if (!checkType()) { + LOGGER.error("Field {} with input annotation can't be used, unable to safely assign field using binding payload type", getMember()); + return false; + } + return true; + } + + @Override + public void set(Object annotatedObject, List boundInputList) { + try { + getMember().invoke(annotatedObject, unbindInput(boundInputList)); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } catch (IllegalArgumentException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause() == null ? ex : ex.getCause(); + throw new RuntimeException(cause.getMessage(), cause); + } + } + } + + public static class OutputFieldBinding extends OutputBinding { + + public OutputFieldBinding(Field field) { + super(field); + } + + @Override + public Type getMemberType() { + return getMember().getGenericType(); + } + + @Override + public boolean validate() { + if (!checkModifier()) { + LOGGER.error("Field {} with output annotation can't be used, not public.", getMember()); + return false; + } + if (!checkType()) { + LOGGER.error("Field {} with output annotation can't be used, unable to safely construct binding using field type", getMember()); + return false; + } + return true; + } + + @Override + public IData get(Object annotatedInstance) { + Object value; + try { + value = getMember().get(annotatedInstance); + } catch (IllegalArgumentException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } + return value == null ? null : bindOutputValue(value); + } + } + + public static class OutputMethodBinding extends OutputBinding { + + public OutputMethodBinding(Method method) { + super(method); + } + + @Override + public Type getMemberType() { + return getMember().getGenericReturnType(); + } + + @Override + public boolean validate() { + Method method = getMember(); + if (method.getParameterTypes().length != 0) { + LOGGER.error("Method {} with output annotation can't be used, parameter count != 0", getMember()); + return false; + } + if (!checkModifier()) { + LOGGER.error("Method {} with output annotation can't be used, not public", getMember()); + return false; + } + if (!checkType()) { + LOGGER.error("Method {} with output annotation can't be used, unable to safely construct binding using method return type", getMember()); + return false; + } + return true; + } + + @Override + public IData get(Object annotatedInstance) { + Object value; + try { + value = getMember().invoke(annotatedInstance); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } catch (IllegalArgumentException ex) { + throw new RuntimeException("Internal error processing inputs", ex); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause() == null ? ex : ex.getCause(); + throw new RuntimeException(cause.getMessage(), cause); + } + return value == null ? null : bindOutputValue(value); + } + } + + // for example, a type reprecenting the for types of List or List + public final Type NOT_PARAMETERIZED_TYPE = new WildcardType() { + @Override public Type[] getUpperBounds() { return new Type[]{Object.class}; } + @Override public Type[] getLowerBounds() { return new Type[0]; } + }; +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/AnnotationParser.java b/src/main/java/org/n52/wps/algorithm/annotation/AnnotationParser.java new file mode 100644 index 0000000..b540ce3 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/AnnotationParser.java @@ -0,0 +1,324 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputFieldBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputMethodBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputFieldBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputMethodBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.ExecuteMethodBinding; +import org.n52.wps.algorithm.descriptor.BoundDescriptor; +import org.n52.wps.algorithm.descriptor.ComplexDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.ComplexDataOutputDescriptor; +import org.n52.wps.algorithm.descriptor.InputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor; +import org.n52.wps.algorithm.descriptor.OutputDescriptor; +import org.n52.wps.algorithm.util.ClassUtil; +import org.n52.wps.io.BasicXMLTypeFactory; +import org.n52.wps.io.data.ILiteralData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author tkunicki + */ +public abstract class AnnotationParser> { + + public final static Logger LOGGER = LoggerFactory.getLogger(AnnotationParser.class); + + public B parse(M member) { + A annotation = member.getAnnotation(getSupportedAnnotation()); + return annotation == null ? null : parse(annotation, member); + } + + public abstract B parse(A annotation, M member); + + public abstract Class getSupportedAnnotation(); + + public static class ExecuteAnnotationParser extends AnnotationParser { + + @Override + public ExecuteMethodBinding parse(Execute annotation, Method member) { + ExecuteMethodBinding annotationBinding = new ExecuteMethodBinding(member); + return annotationBinding.validate() ? annotationBinding : null; + } + + @Override + public Class getSupportedAnnotation() { + return Execute.class; + } + + } + + public abstract static class DataAnnotationParser> + extends AnnotationParser { + protected abstract B createBinding(M member); + } + + public abstract static class InputAnnotationParser> + extends DataAnnotationParser {} + + public abstract static class OutputAnnotationParser> + extends DataAnnotationParser {} + + public abstract static class LiteralDataInputAnnotationParser> + extends InputAnnotationParser { + + @Override + public B parse(LiteralDataInput annotation, M member) { + + B annotatedBinding = createBinding(member); + // auto generate binding if it's not explicitly declared + Type payloadType = annotatedBinding.getPayloadType(); + Class binding = annotation.binding(); + if (binding == null || ILiteralData.class.equals(binding)) { + if (payloadType instanceof Class) { + binding = BasicXMLTypeFactory.getBindingForPayloadType((Class) payloadType); + if (binding == null) { + LOGGER.error("Unable to locate binding class for {}; binding not found.", payloadType); + } + } else { + if (annotatedBinding.isMemberTypeList()) { + LOGGER.error("Unable to determine binding class for {}; List must be parameterized with a type matching a known binding payload to use auto-binding.", payloadType); + } else { + LOGGER.error("Unable to determine binding class for {}; type must fully resolved to use auto-binding", payloadType); + } + } + } + String[] allowedValues = annotation.allowedValues(); + String defaultValue = annotation.defaultValue(); + int maxOccurs = annotation.maxOccurs(); + // If InputType is enum + // 1) generate allowedValues if not explicitly declared + // 2) validate allowedValues if explicitly declared + // 3) validate defaultValue if declared + // 4) check for special ENUM_COUNT maxOccurs flag + Type inputType = annotatedBinding.getType(); + if (annotatedBinding.isTypeEnum()) { + Class inputEnumClass = (Class) inputType; + // validate contents of allowed values maps to enum + if (allowedValues.length > 0) { + List invalidValues = new ArrayList(); + for (String value : allowedValues) { + try { + Enum.valueOf(inputEnumClass, value); + } catch (IllegalArgumentException e) { + invalidValues.add(value); + LOGGER.warn("Invalid allowed value \"{}\" specified for for enumerated input type {}", value, inputType); + } + } + if (invalidValues.size() > 0) { + List updatedValues = new ArrayList(Arrays.asList(allowedValues)); + updatedValues.removeAll(invalidValues); + allowedValues = updatedValues.toArray(new String[0]); + } + } + // if list is empty, populated with values from enum + if (allowedValues.length == 0) { + allowedValues = ClassUtil.convertEnumToStringArray(inputEnumClass); + } + if (defaultValue.length() > 0) { + try { + Enum.valueOf(inputEnumClass, defaultValue); + } catch (IllegalArgumentException e) { + LOGGER.warn("Invalid default value \"{}\" specified for for enumerated input type {}, ignoring.", defaultValue, inputType); + defaultValue = ""; + } + } + if (maxOccurs == LiteralDataInput.ENUM_COUNT) { + maxOccurs = inputEnumClass.getEnumConstants().length; + } + } else { + if (maxOccurs == LiteralDataInput.ENUM_COUNT) { + maxOccurs = 1; + LOGGER.warn("Invalid maxOccurs \"ENUM_COUNT\" specified for for input type {}, setting maxOccurs to {}", inputType, maxOccurs); + } + } + if (binding != null) { + LiteralDataInputDescriptor descriptor = + LiteralDataInputDescriptor.builder(annotation.identifier(), binding). + title(annotation.title()). + abstrakt(annotation.abstrakt()). + minOccurs(annotation.minOccurs()). + maxOccurs(maxOccurs). + defaultValue(defaultValue). + allowedValues(allowedValues). + build(); + annotatedBinding.setDescriptor(descriptor); + } else { + LOGGER.error("Unable to generate binding for input identifier \"{}\"", annotation.identifier()); + } + return annotatedBinding.validate() ? annotatedBinding : null; + } + + @Override + public Class getSupportedAnnotation() { + return LiteralDataInput.class; + } + } + + public abstract static class LiteralDataOutputAnnotationParser> + extends OutputAnnotationParser { + + @Override + public B parse(LiteralDataOutput annotation, M member) { + B annotatedBinding = createBinding(member); + // auto generate binding if it's not explicitly declared + Type payloadType = annotatedBinding.getPayloadType(); + Class binding = annotation.binding(); + if (binding == null || ILiteralData.class.equals(binding)) { + if (payloadType instanceof Class) { + binding = BasicXMLTypeFactory.getBindingForPayloadType((Class) payloadType); + if (binding == null) { + LOGGER.error("Unable to locate binding class for {}; binding not found.", payloadType); + } + } else { + LOGGER.error("Unable to determine binding class for {}; type must fully resolved to use auto-binding", payloadType); + } + } + if (binding != null) { + LiteralDataOutputDescriptor descriptor = + LiteralDataOutputDescriptor.builder(annotation.identifier(), binding). + title(annotation.title()). + abstrakt(annotation.abstrakt()). + build(); + annotatedBinding.setDescriptor(descriptor); + } else { + LOGGER.error("Unable to generate binding for output identifier \"{}\"", annotation.identifier()); + } + return annotatedBinding.validate() ? annotatedBinding : null; + } + + @Override + public Class getSupportedAnnotation() { + return LiteralDataOutput.class; + } + } + + public static class ComplexDataInputAnnotationParser> + extends InputAnnotationParser { + + @Override + public B parse(ComplexDataInput annotation, M member) { + B annotatedBinding = createBinding(member); + ComplexDataInputDescriptor descriptor = + ComplexDataInputDescriptor.builder(annotation.identifier(), annotation.binding()). + title(annotation.title()). + abstrakt(annotation.abstrakt()). + minOccurs(annotation.minOccurs()). + maxOccurs(annotation.maxOccurs()). + maximumMegaBytes(annotation.maximumMegaBytes()). + build(); + annotatedBinding.setDescriptor(descriptor); + return annotatedBinding.validate() ? annotatedBinding : null; + } + + @Override + public Class getSupportedAnnotation() { + return ComplexDataInput.class; + } + + @Override + protected B createBinding(M member) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + public abstract static class ComplexDataOutputAnnotationParser> + extends OutputAnnotationParser { + + @Override + public B parse (ComplexDataOutput annotation, M member) { + B annotatedBinding = createBinding(member); + ComplexDataOutputDescriptor descriptor = + ComplexDataOutputDescriptor.builder(annotation.identifier(), annotation.binding()). + title(annotation.title()). + abstrakt(annotation.abstrakt()). + build(); + annotatedBinding.setDescriptor(descriptor); + return annotatedBinding.validate() ? annotatedBinding : null; + } + + @Override + public Class getSupportedAnnotation() { + return ComplexDataOutput.class; + } + } + + public static class LiteralDataInputFieldAnnotationParser extends LiteralDataInputAnnotationParser> { + @Override + protected InputBinding createBinding(Field member) { + return new InputFieldBinding(member); + } + } + public static class LiteralDataOutputFieldAnnotationParser extends LiteralDataOutputAnnotationParser> { + @Override + protected OutputBinding createBinding(Field member) { + return new OutputFieldBinding(member); + } + } + public static class ComplexDataInputFieldAnnotationParser extends ComplexDataInputAnnotationParser> { + @Override + protected InputBinding createBinding(Field member) { + return new InputFieldBinding(member); + } + } + public static class ComplexDataOutputFieldAnnotationParser extends ComplexDataOutputAnnotationParser> { + @Override + protected OutputBinding createBinding(Field member) { + return new OutputFieldBinding(member); + } + } + + public static class LiteralDataInputMethodAnnotationParser extends LiteralDataInputAnnotationParser> { + @Override + protected InputBinding createBinding(Method member) { + return new InputMethodBinding(member); + } + } + public static class LiteralDataOutputMethodAnnotationParser extends LiteralDataOutputAnnotationParser> { + @Override + protected OutputBinding createBinding(Method member) { + return new OutputMethodBinding(member); + } + } + public static class ComplexDataInputMethodAnnotationParser extends ComplexDataInputAnnotationParser> { + @Override + protected InputBinding createBinding(Method member) { + return new InputMethodBinding(member); + } + } + public static class ComplexDataOutputMethodAnnotationParser extends ComplexDataOutputAnnotationParser> { + @Override + protected OutputBinding createBinding(Method member) { + return new OutputMethodBinding(member); + } + } +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/ComplexDataInput.java b/src/main/java/org/n52/wps/algorithm/annotation/ComplexDataInput.java new file mode 100644 index 0000000..396bea7 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/ComplexDataInput.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.n52.wps.io.data.IComplexData; + +/** + * + * @author tkunicki + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface ComplexDataInput { + String identifier(); // identifier + String title() default ""; + String abstrakt() default ""; // 'abstract' is java reserved keyword + int minOccurs() default 1; + int maxOccurs() default 1; + int maximumMegaBytes() default 0; + Class binding(); +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/ComplexDataOutput.java b/src/main/java/org/n52/wps/algorithm/annotation/ComplexDataOutput.java new file mode 100644 index 0000000..c7d0b16 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/ComplexDataOutput.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.n52.wps.io.data.IComplexData; + +/** + * + * @author tkunicki + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface ComplexDataOutput { + String identifier(); // identifier + String title() default ""; + String abstrakt() default ""; // 'abstract' is java reserved keyword + Class binding(); +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/Execute.java b/src/main/java/org/n52/wps/algorithm/annotation/Execute.java new file mode 100644 index 0000000..f1e3830 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/Execute.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author tkunicki + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Execute { + +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/LiteralDataInput.java b/src/main/java/org/n52/wps/algorithm/annotation/LiteralDataInput.java new file mode 100644 index 0000000..0dc22a5 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/LiteralDataInput.java @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.n52.wps.io.data.ILiteralData; + +/** + * + * @author tkunicki + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface LiteralDataInput { + String identifier(); // identifier + String title() default ""; + String abstrakt() default ""; // 'abstract' is java reserved keyword + int minOccurs() default 1; + int maxOccurs() default 1; + String defaultValue() default ""; + String[] allowedValues() default {}; + Class binding() default ILiteralData.class; + + //// special maxOccurs flags + // set maxOccurs to enum constant count + public final static int ENUM_COUNT = -1; +} diff --git a/src/main/java/org/n52/wps/algorithm/annotation/LiteralDataOutput.java b/src/main/java/org/n52/wps/algorithm/annotation/LiteralDataOutput.java new file mode 100644 index 0000000..07d52d5 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/annotation/LiteralDataOutput.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.n52.wps.io.data.ILiteralData; + +/** + * + * @author tkunicki + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface LiteralDataOutput { + String identifier(); // identifier + String title() default ""; + String abstrakt() default ""; // 'abstract' is java reserved keyword + Class binding() default ILiteralData.class; +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/AlgorithmDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/AlgorithmDescriptor.java new file mode 100644 index 0000000..706d613 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/AlgorithmDescriptor.java @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author tkunicki + */ +public class AlgorithmDescriptor extends Descriptor { + + private final String version; + private final boolean storeSupported; + private final boolean statusSupported; + private final Map inputDescriptorMap; + private final Map outputDescriptorMap; + + AlgorithmDescriptor(Builder> builder) { + super(builder); + this.version = builder.version; + this.storeSupported = builder.storeSupported; + this.statusSupported = builder.statusSupported; + + Preconditions.checkState( + builder.outputDescriptors.size() > 0, + "Need at minimum 1 output for algorithm."); + + // LinkedHaskMap to preserve order + Map iMap = new LinkedHashMap(); + for (InputDescriptor iDescriptor : builder.inputDescriptors) { + iMap.put(iDescriptor.getIdentifier(), iDescriptor); + } + inputDescriptorMap = Collections.unmodifiableMap(iMap); + + Map oMap = new LinkedHashMap(); + for (OutputDescriptor oDescriptor : builder.outputDescriptors) { + oMap.put(oDescriptor.getIdentifier(), oDescriptor); + } + outputDescriptorMap = Collections.unmodifiableMap(oMap); + } + + public String getVersion() { + return version; + } + + public boolean getStoreSupported() { + return storeSupported; + } + + public boolean getStatusSupported() { + return statusSupported; + } + + public List getInputIdentifiers() { + return Collections.unmodifiableList(new ArrayList(inputDescriptorMap.keySet())); + } + + public InputDescriptor getInputDescriptor(String identifier) { + return inputDescriptorMap.get(identifier); + } + + public Collection getInputDescriptors() { + return inputDescriptorMap.values(); + } + + public List getOutputIdentifiers() { + return Collections.unmodifiableList(new ArrayList(outputDescriptorMap.keySet())); + } + + public OutputDescriptor getOutputDescriptor(String identifier) { + return outputDescriptorMap.get(identifier); + } + + public Collection getOutputDescriptors() { + return outputDescriptorMap.values(); + } + + public static Builder builder(String identifier) { + return new BuilderTyped(identifier); + } + + public static Builder builder(Class clazz) { + Preconditions.checkNotNull(clazz, "clazz may not be null"); + return new BuilderTyped(clazz.getCanonicalName()); + } + + private static class BuilderTyped extends Builder { + public BuilderTyped(String identifier) { + super(identifier); + } + @Override + protected BuilderTyped self() { + return this; + } + } + + public static abstract class Builder> extends Descriptor.Builder{ + + private String version = "1.0.0"; + private boolean storeSupported = true; + private boolean statusSupported = true; + private List inputDescriptors; + private List outputDescriptors; + + protected Builder(String identifier) { + super(identifier); + title(identifier); + inputDescriptors = new ArrayList(); + outputDescriptors = new ArrayList(); + } + + public B version(String version) { + this.version = version; + return self(); + } + + public B storeSupported(boolean storeSupported) { + this.storeSupported = storeSupported; + return self(); + } + + public B statusSupported(boolean statusSupported) { + this.statusSupported = statusSupported; + return self(); + } + + public B addInputDescriptor(InputDescriptor.Builder inputDescriptorBuilder) { + return addInputDescriptor(inputDescriptorBuilder.build()); + } + + public B addInputDescriptor(InputDescriptor inputDescriptor) { + this.inputDescriptors.add(inputDescriptor); + return self(); + } + + public B addInputDescriptors(List inputDescriptors) { + this.inputDescriptors.addAll(inputDescriptors); + return self(); + } + + public B addOutputDescriptor(OutputDescriptor.Builder outputDescriptorBuilder) { + return addOutputDescriptor(outputDescriptorBuilder.build()); + } + + public B addOutputDescriptor(OutputDescriptor outputDescriptor) { + this.outputDescriptors.add(outputDescriptor); + return self(); + } + + public B addOutputDescriptors(List outputDescriptors) { + this.outputDescriptors.addAll(outputDescriptors); + return self(); + } + + public AlgorithmDescriptor build() { + return new AlgorithmDescriptor(this); + } + + } + +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/BoundDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/BoundDescriptor.java new file mode 100644 index 0000000..c0cf2b1 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/BoundDescriptor.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; + +/** + * + * @author tkunicki + */ +public abstract class BoundDescriptor> extends Descriptor { + + private final T binding; + + BoundDescriptor(Builder, T> builder) { + super(builder); + this.binding = builder.binding; + } + + public T getBinding() { + return binding; + } + + public static abstract class Builder, T extends Class> extends Descriptor.Builder { + + private final T binding; + + protected Builder(String identifier, T binding) { + super(identifier); + Preconditions.checkArgument(binding != null, "binding may not be null"); + this.binding = binding; + } + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/ComplexDataInputDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/ComplexDataInputDescriptor.java new file mode 100644 index 0000000..c562833 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/ComplexDataInputDescriptor.java @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; +import java.math.BigInteger; +import org.n52.wps.io.data.IComplexData; + +/** + * + * @author tkunicki + */ +public class ComplexDataInputDescriptor> extends InputDescriptor { + + private final BigInteger maximumMegaBytes; + + private ComplexDataInputDescriptor(Builder builder) { + super(builder); + this.maximumMegaBytes = builder.maximumMegaBytes; + } + + public boolean hasMaximumMegaBytes() { + return maximumMegaBytes != null && maximumMegaBytes.longValue() > 0; + } + + public BigInteger getMaximumMegaBytes() { + return maximumMegaBytes; + } + + public static > Builder builder(String identifier, T binding) { + return new BuilderTyped(identifier, binding); + } + + private static class BuilderTyped> extends Builder, T> { + public BuilderTyped(String identifier, T binding) { + super(identifier, binding); + } + @Override + protected BuilderTyped self() { + return this; + } + } + + public static abstract class Builder, T extends Class> extends InputDescriptor.Builder { + + private BigInteger maximumMegaBytes; + + private Builder(String identifier, T binding) { + super(identifier, binding); + } + + public B maximumMegaBytes(int maximumMegaBytes) { + return maximumMegaBytes(BigInteger.valueOf(maximumMegaBytes)); + } + + public B maximumMegaBytes(BigInteger maximumMegaBytes) { + Preconditions.checkArgument(maximumMegaBytes.longValue() >= 0, "maximumMegabytes must be >= 0"); + this.maximumMegaBytes = maximumMegaBytes; + return self(); + } + + @Override + public ComplexDataInputDescriptor build() { + return new ComplexDataInputDescriptor(this); + } + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/ComplexDataOutputDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/ComplexDataOutputDescriptor.java new file mode 100644 index 0000000..324055a --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/ComplexDataOutputDescriptor.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import org.n52.wps.io.data.IComplexData; + +/** + * + * @author tkunicki + */ +public class ComplexDataOutputDescriptor> extends OutputDescriptor { + + + private ComplexDataOutputDescriptor(Builder builder) { + super(builder); + } + + public static > Builder builder(String identifier, T binding) { + return new BuilderTyped(identifier, binding); + } + + private static class BuilderTyped> extends Builder, T> { + public BuilderTyped(String identifier, T binding) { + super(identifier, binding); + } + @Override + protected BuilderTyped self() { + return this; + } + } + + public static abstract class Builder, T extends Class> extends OutputDescriptor.Builder { + + private Builder(String identifier, T binding) { + super(identifier, binding); + } + + public ComplexDataOutputDescriptor build() { + return new ComplexDataOutputDescriptor(this); + } + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/Descriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/Descriptor.java new file mode 100644 index 0000000..52ecdb8 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/Descriptor.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; + +/** + * + * @author tkunicki + */ +public abstract class Descriptor { + + private final String identifier; + private final String title; + private final String abstrakt; // want 'abstract' but it's a java keyword + + Descriptor(Builder> builder) { + this.identifier = builder.identifier; + this.title = builder.title; + this.abstrakt = builder.abstrakt; + } + + public String getIdentifier() { + return identifier; + } + + public boolean hasTitle() { + return title != null && title.length() > 0; + } + + public String getTitle() { + return title; + } + + public boolean hasAbstract() { + return abstrakt != null && abstrakt.length() > 0; + } + + public String getAbstract() { + return abstrakt; + } + + public static abstract class Builder> { + + private final String identifier; + private String title; + private String abstrakt; // want 'abstract' but it's a java keyword + + public Builder(String identifier) { + Preconditions.checkArgument( + !(identifier == null || identifier.isEmpty()), + "identifier may not be null or an empty String"); + this.identifier = identifier; + } + + public B title(String title) { + this.title = title; + return self(); + } + + // want 'abstract' but it's a java keyword + public B abstrakt(String abstrakt) { + this.abstrakt = abstrakt; + return self(); + } + + protected abstract B self(); + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/InputDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/InputDescriptor.java new file mode 100644 index 0000000..aea1eb7 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/InputDescriptor.java @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; +import java.math.BigInteger; +import org.n52.wps.io.data.IData; + +/** + * + * @author tkunicki + */ +public abstract class InputDescriptor> extends BoundDescriptor { + + private final BigInteger minOccurs; + private final BigInteger maxOccurs; + + protected InputDescriptor(Builder, T> builder) { + super(builder); + this.minOccurs = builder.minOccurs; + this.maxOccurs = builder.maxOccurs; + Preconditions.checkState(maxOccurs.longValue() >= minOccurs.longValue(), "maxOccurs must be >= minOccurs"); + } + + public BigInteger getMinOccurs() { + return minOccurs; + } + + public BigInteger getMaxOccurs() { + return maxOccurs; + } + + public static abstract class Builder, T extends Class> extends BoundDescriptor.Builder{ + + private BigInteger minOccurs = BigInteger.ONE; + private BigInteger maxOccurs = BigInteger.ONE; + + protected Builder(String identifier, T binding) { + super(identifier, binding); + } + + public B minOccurs(int minOccurs) { + return minOccurs(BigInteger.valueOf(minOccurs)); + } + + public B minOccurs(BigInteger minOccurs) { + Preconditions.checkArgument(minOccurs.longValue() >= 0, "minOccurs must be >= 0"); + this.minOccurs = minOccurs; + return self(); + } + + public B maxOccurs(int maxOccurs) { + return maxOccurs(BigInteger.valueOf(maxOccurs)); + } + + public B maxOccurs(BigInteger maxOccurs) { + Preconditions.checkArgument(maxOccurs.longValue() > 0, "maxOccurs must be > 0"); + this.maxOccurs = maxOccurs; + return self(); + } + + public > B maxOccurs(Class enumType) { + return maxOccurs(enumType.getEnumConstants().length); + } + + public abstract InputDescriptor build(); + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/LiteralDataInputDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/LiteralDataInputDescriptor.java new file mode 100644 index 0000000..af82698 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/LiteralDataInputDescriptor.java @@ -0,0 +1,180 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +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.io.BasicXMLTypeFactory; + +/** + * + * @author tkunicki + */ +public class LiteralDataInputDescriptor> extends InputDescriptor { + + private final String dataType; + private final String defaultValue; + private final List allowedValues; + + protected LiteralDataInputDescriptor(Builder builder) { + super(builder); + this.dataType = builder.dataType; + this.defaultValue = builder.defaultValue; + this.allowedValues = builder.allowedValues != null ? + Collections.unmodifiableList(builder.allowedValues) : + Collections.EMPTY_LIST; + // if allowedValues and defaultValue are set, make sure defaultValue is in set of allowedValues + Preconditions.checkState( + !hasAllowedValues() || !hasDefaultValue() || allowedValues.contains(defaultValue), + "defaultValue of %s not in set of allowedValues", defaultValue); + } + + public String getDataType() { + return dataType; + } + + public boolean hasDefaultValue() { + return defaultValue != null && defaultValue.length() > 0; + } + + public String getDefaultValue() { + return defaultValue; + } + + public boolean hasAllowedValues() { + return allowedValues != null && allowedValues.size() > 0; + } + + public List getAllowedValues() { + return allowedValues; + } + + public static > Builder builder(String identifier, T binding) { + return new BuilderTyped(identifier, binding); + } + + // utility functions, quite verbose... + public static Builder> anyURIBuilder(String identifier) { + return builder(identifier, LiteralAnyURIBinding.class); + } + + public static Builder> base64BinaryBuilder(String identifier) { + return builder(identifier, LiteralBase64BinaryBinding.class); + } + + public static Builder> booleanBuilder(String identifier) { + return builder(identifier, LiteralBooleanBinding.class); + } + + public static Builder> byteBuilder(String identifier) { + return builder(identifier, LiteralByteBinding.class); + } + + public static Builder> dateTimeBuilder(String identifier) { + return builder(identifier, LiteralDateTimeBinding.class); + } + + public static Builder> doubleBuilder(String identifier) { + return builder(identifier, LiteralDoubleBinding.class); + } + + public static Builder> floatBuilder(String identifier) { + return builder(identifier, LiteralFloatBinding.class); + } + + public static Builder> intBuilder(String identifier) { + return builder(identifier, LiteralIntBinding.class); + } + + public static Builder> longBuilder(String identifier) { + return builder(identifier, LiteralLongBinding.class); + } + + public static Builder> shortBuilder(String identifier) { + return builder(identifier, LiteralShortBinding.class); + } + + public static Builder> stringBuilder(String identifier) { + return builder(identifier, LiteralStringBinding.class); + } + + private static class BuilderTyped> extends Builder, T> { + public BuilderTyped(String identifier, T binding) { + super(identifier, binding); + } + @Override + protected BuilderTyped self() { + return this; + } + } + + public static abstract class Builder, T extends Class> extends InputDescriptor.Builder { + + private final String dataType; + private String defaultValue; + private List allowedValues; + + protected Builder(String identifier, T binding) { + super(identifier, binding); + this.dataType = Preconditions.checkNotNull( + BasicXMLTypeFactory.getXMLDataTypeforBinding(binding), + "Unable to resolve XML DataType for binding class %s", + binding); + } + + public B defaultValue(String defaultValue) { + this.defaultValue = defaultValue; + return self(); + } + + public B allowedValues(Class allowedValues) { + Enum[] constants = allowedValues.getEnumConstants(); + List names = new ArrayList(constants.length); + for (Enum constant : constants) { names.add(constant.name()); } + return allowedValues(names); + } + + public B allowedValues(String[] allowedValues) { + return allowedValues(Arrays.asList(allowedValues)); + } + + public B allowedValues(List allowedValues) { + this.allowedValues = allowedValues; + return self(); + } + + @Override + public LiteralDataInputDescriptor build() { + return new LiteralDataInputDescriptor(this); + } + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/LiteralDataOutputDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/LiteralDataOutputDescriptor.java new file mode 100644 index 0000000..6aba6c0 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/LiteralDataOutputDescriptor.java @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import com.google.common.base.Preconditions; +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; + +/** + * + * @author tkunicki + */ +public class LiteralDataOutputDescriptor> extends OutputDescriptor { + + private final String dataType; + + protected LiteralDataOutputDescriptor(Builder builder) { + super(builder); + this.dataType = builder.dataType; + } + + public String getDataType() { + return dataType; + } + + public static > Builder builder(String identifier, T binding) { + return new BuilderTyped(identifier, binding); + } + + // utility functions, quite verbose... + public static Builder> anyURIBuilder(String identifier) { + return builder(identifier, LiteralAnyURIBinding.class); + } + + public static Builder> base64BinaryBuilder(String identifier) { + return builder(identifier, LiteralBase64BinaryBinding.class); + } + + public static Builder> booleanBuilder(String identifier) { + return builder(identifier, LiteralBooleanBinding.class); + } + + public static Builder> byteBuilder(String identifier) { + return builder(identifier, LiteralByteBinding.class); + } + + public static Builder> dateTimeBuilder(String identifier) { + return builder(identifier, LiteralDateTimeBinding.class); + } + + public static Builder> doubleBuilder(String identifier) { + return builder(identifier, LiteralDoubleBinding.class); + } + + public static Builder> floatBuilder(String identifier) { + return builder(identifier, LiteralFloatBinding.class); + } + + public static Builder> intBuilder(String identifier) { + return builder(identifier, LiteralIntBinding.class); + } + + public static Builder> longBuilder(String identifier) { + return builder(identifier, LiteralLongBinding.class); + } + + public static Builder> shortBuilder(String identifier) { + return builder(identifier, LiteralShortBinding.class); + } + + public static Builder> stringBuilder(String identifier) { + return builder(identifier, LiteralStringBinding.class); + } + + private static class BuilderTyped> extends Builder, T> { + public BuilderTyped(String identifier, T binding) { + super(identifier, binding); + } + @Override + protected BuilderTyped self() { + return this; + } + } + + public static abstract class Builder, T extends Class> extends OutputDescriptor.Builder { + + private final String dataType; + + protected Builder(String identifier, T binding) { + super(identifier, binding); + this.dataType = Preconditions.checkNotNull( + BasicXMLTypeFactory.getXMLDataTypeforBinding(binding), + "Unable to resolve XML DataType for binding class %s", + binding); + } + + @Override + public LiteralDataOutputDescriptor build() { + return new LiteralDataOutputDescriptor(this); + } + } +} diff --git a/src/main/java/org/n52/wps/algorithm/descriptor/OutputDescriptor.java b/src/main/java/org/n52/wps/algorithm/descriptor/OutputDescriptor.java new file mode 100644 index 0000000..01b8df8 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/descriptor/OutputDescriptor.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import org.n52.wps.io.data.IData; + +/** + * + * @author tkunicki + */ +public abstract class OutputDescriptor> extends BoundDescriptor { + + OutputDescriptor(Builder, T> builder) { + super(builder); + } + + public static abstract class Builder, T extends Class> extends BoundDescriptor.Builder{ + + protected Builder(String identifier, T binding) { + super(identifier, binding); + } + + public abstract OutputDescriptor build(); + } +} diff --git a/src/main/java/org/n52/wps/algorithm/util/ClassUtil.java b/src/main/java/org/n52/wps/algorithm/util/ClassUtil.java new file mode 100644 index 0000000..2b57b17 --- /dev/null +++ b/src/main/java/org/n52/wps/algorithm/util/ClassUtil.java @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.util; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * + * @author tkunicki + */ +public class ClassUtil { + + private final static Map, Class> TO_WRAPPER; + private final static Map, Class> FROM_WRAPPER; + + static { + BiMap, Class> map = HashBiMap.create(); + map.put(Float.TYPE, Float.class); + map.put(Double.TYPE, Double.class); + map.put(Byte.TYPE, Byte.class); + map.put(Short.TYPE, Short.class); + map.put(Integer.TYPE, Integer.class); + map.put(Long.TYPE, Long.class); + map.put(Character.TYPE, Character.class); + map.put(Boolean.TYPE, Boolean.class); + TO_WRAPPER = Collections.unmodifiableMap(map); + FROM_WRAPPER = Collections.unmodifiableMap(map.inverse()); + } + + + public static > List convertStringToEnumList(Class enumType, List stringList) { + List enumList = new ArrayList(); + for (String string : stringList) { + enumList.add(Enum.valueOf(enumType, string)); + } + return enumList; + } + + public static > List convertEnumToStringList(Class enumType) { + ArrayList stringList = null; + T[] constants = enumType.getEnumConstants(); + if (constants != null && constants.length > 0) { + stringList = new ArrayList(constants.length); + for (T constant : constants) { + stringList.add(constant.name()); + } + } + return stringList; + } + + public static > String[] convertEnumToStringArray(Class enumType) { + List stringList = convertEnumToStringList(enumType); + return stringList == null ? null : + stringList.toArray(new String[stringList.size()]); + } + + public static boolean isWrapper(Class clazz) { + return FROM_WRAPPER.containsKey(clazz); + } + + public static Class unwrap(Class clazz) { + Class unwrapped = FROM_WRAPPER.get(clazz); + if (unwrapped == null) + return clazz; + return unwrapped; + + } + + public static Class wrap(Class clazz) { + Class wrapped = TO_WRAPPER.get(clazz); + if (wrapped == null) + return clazz; + return wrapped; + } + +} diff --git a/src/main/java/org/n52/wps/server/AbstractAlgorithm.java b/src/main/java/org/n52/wps/server/AbstractAlgorithm.java new file mode 100644 index 0000000..07d4e05 --- /dev/null +++ b/src/main/java/org/n52/wps/server/AbstractAlgorithm.java @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + + +import java.io.IOException; +import java.io.InputStream; + +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionsDocument; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; + +/** + * This class has to be extended in order to be served through the WPS. + * The class file should also include a description file of the algorithm. This file has to be + * valid against the describeProcess.xsd. The file has to be placed in the folder of the class file and has + * to be named the same as the Algorithm. + * + *

If you want to apply a different initialization method, just override the initializeDescription() method. + * + * NOTE: This class is an adapter and it is recommended to extend this. + * @author foerster + * + */ +public abstract class AbstractAlgorithm implements IAlgorithm +{ + private ProcessDescriptionType description; // private, force access through getter method for lazy loading. + private final String wkName; + private static Logger LOGGER = LoggerFactory.getLogger(AbstractAlgorithm.class); + + /** + * default constructor, calls the initializeDescription() Method + */ + public AbstractAlgorithm() { + this.wkName = this.getClass().getName(); + } + + /** + * default constructor, calls the initializeDescription() Method + */ + public AbstractAlgorithm(String wellKnownName) { + this.wkName = wellKnownName; + } + + /** + * This method should be overwritten, in case you want to have a way of initializing. + * + * In detail it looks for a xml descfile, which is located in the same directory as the implementing class and has the same + * name as the class, but with the extension XML. + * @return + */ + protected ProcessDescriptionType initializeDescription() { + String className = this.getClass().getName().replace(".", "/"); + InputStream xmlDesc = this.getClass().getResourceAsStream("/" + className + ".xml"); + try { + XmlOptions option = new XmlOptions(); + option.setLoadTrimTextBuffer(); + ProcessDescriptionsDocument doc = ProcessDescriptionsDocument.Factory.parse(xmlDesc, option); + if(doc.getProcessDescriptions().getProcessDescriptionArray().length == 0) { + LOGGER.warn("ProcessDescription does not contain correct any description"); + return null; + } + + // Checking that the process name (full class name or well-known name) matches the identifier. + if(!doc.getProcessDescriptions().getProcessDescriptionArray(0).getIdentifier().getStringValue().equals(this.getClass().getName()) && + !doc.getProcessDescriptions().getProcessDescriptionArray(0).getIdentifier().getStringValue().equals(this.getWellKnownName())) { + doc.getProcessDescriptions().getProcessDescriptionArray(0).getIdentifier().setStringValue(this.getClass().getName()); + LOGGER.warn("Identifier was not correct, was changed now temporary for server use to " + this.getClass().getName() + ". Please change it later in the description!"); + } + + return doc.getProcessDescriptions().getProcessDescriptionArray(0); + } + catch(IOException e) { + LOGGER.warn("Could not initialize algorithm, parsing error: " + this.getClass().getName(), e); + } + catch(XmlException e) { + LOGGER.warn("Could not initialize algorithm, parsing error: " + this.getClass().getName(), e); + } + return null; + } + + @Override + public synchronized ProcessDescriptionType getDescription() { + if (description == null) { + description = initializeDescription(); + } + return description; + } + + @Override + public boolean processDescriptionIsValid() { + return getDescription().validate(); + } + + @Override + public String getWellKnownName() { + return this.wkName; + } +} diff --git a/src/main/java/org/n52/wps/server/AbstractAnnotatedAlgorithm.java b/src/main/java/org/n52/wps/server/AbstractAnnotatedAlgorithm.java new file mode 100644 index 0000000..66b313a --- /dev/null +++ b/src/main/java/org/n52/wps/server/AbstractAnnotatedAlgorithm.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.n52.wps.algorithm.annotation.AnnotatedAlgorithmIntrospector; +import static org.n52.wps.algorithm.annotation.AnnotatedAlgorithmIntrospector.getInstrospector; +import org.n52.wps.algorithm.annotation.AnnotationBinding; +import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor; +import org.n52.wps.io.data.IData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * + * @author tkunicki + */ +public abstract class AbstractAnnotatedAlgorithm extends AbstractDescriptorAlgorithm { + + private final static Logger LOGGER = LoggerFactory.getLogger(AbstractAnnotatedAlgorithm.class); + + @Override + protected AlgorithmDescriptor createAlgorithmDescriptor() { + return getInstrospector(getAlgorithmClass()).getAlgorithmDescriptor(); + } + + @Override + public Map run(Map> inputMap) { + Object annotatedInstance = getAlgorithmInstance(); + + AnnotatedAlgorithmIntrospector introspector = getInstrospector(annotatedInstance.getClass()); + + for (Map.Entry> iEntry : introspector.getInputBindingMap().entrySet()) { + iEntry.getValue().set(annotatedInstance, inputMap.get(iEntry.getKey())); + } + + getInstrospector(annotatedInstance.getClass()).getExecuteMethodBinding().execute(annotatedInstance); + + Map oMap = new HashMap(); + for (Map.Entry> oEntry : introspector.getOutputBindingMap().entrySet()) { + oMap.put(oEntry.getKey(), oEntry.getValue().get(annotatedInstance)); + } + return oMap; + } + + public Object getAlgorithmInstance() { + return this; + } + + public Class getAlgorithmClass() { + return getClass(); + } + + public static class Proxy extends AbstractAnnotatedAlgorithm { + + final private Class proxiedClass; + final private Object proxiedInstance; + + public Proxy(Class proxiedClass) { + this.proxiedClass = proxiedClass; + try { + this.proxiedInstance = proxiedClass.newInstance(); + } catch (InstantiationException ex) { + throw new RuntimeException("unable to instantiate proxied algorithm instance"); + } catch (IllegalAccessException ex) { + throw new RuntimeException("unable to instantiate proxied algorithm instance"); + } + } + + @Override + public Class getAlgorithmClass() { + return proxiedClass; + } + + @Override + public Object getAlgorithmInstance() { + return proxiedInstance; + } + } + +} diff --git a/src/main/java/org/n52/wps/server/AbstractDescriptorAlgorithm.java b/src/main/java/org/n52/wps/server/AbstractDescriptorAlgorithm.java new file mode 100644 index 0000000..310e4f3 --- /dev/null +++ b/src/main/java/org/n52/wps/server/AbstractDescriptorAlgorithm.java @@ -0,0 +1,397 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import net.opengis.ows.x11.AllowedValuesDocument.AllowedValues; +import net.opengis.wps.x100.ComplexDataCombinationType; +import net.opengis.wps.x100.ComplexDataCombinationsType; +import net.opengis.wps.x100.ComplexDataDescriptionType; +import net.opengis.wps.x100.InputDescriptionType; +import net.opengis.wps.x100.LiteralInputType; +import net.opengis.wps.x100.OutputDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType.DataInputs; +import net.opengis.wps.x100.ProcessDescriptionType.ProcessOutputs; +import net.opengis.wps.x100.ProcessDescriptionsDocument; +import net.opengis.wps.x100.ProcessDescriptionsDocument.ProcessDescriptions; +import net.opengis.wps.x100.SupportedComplexDataInputType; +import net.opengis.wps.x100.SupportedComplexDataType; + +import org.apache.xmlbeans.XmlOptions; +import org.apache.xmlbeans.XmlValidationError; +import org.n52.wps.FormatDocument.Format; +import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor; +import org.n52.wps.algorithm.descriptor.ComplexDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.ComplexDataOutputDescriptor; +import org.n52.wps.algorithm.descriptor.InputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor; +import org.n52.wps.algorithm.descriptor.OutputDescriptor; +import org.n52.wps.io.GeneratorFactory; +import org.n52.wps.io.IGenerator; +import org.n52.wps.io.IOHandler; +import org.n52.wps.io.IParser; +import org.n52.wps.io.ParserFactory; +import org.n52.wps.io.data.IData; +import org.n52.wps.server.observerpattern.IObserver; +import org.n52.wps.server.observerpattern.ISubject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; + +public abstract class AbstractDescriptorAlgorithm implements IAlgorithm, ISubject { + + private final static Logger LOGGER = LoggerFactory.getLogger(AbstractDescriptorAlgorithm.class); + + private AlgorithmDescriptor descriptor; + private ProcessDescriptionType description; + + public AbstractDescriptorAlgorithm() { + super(); + } + + @Override + public synchronized ProcessDescriptionType getDescription() { + if (description == null) { + description = createProcessDescription(); + } + return description; + } + + @Override + public String getWellKnownName() { + return getAlgorithmDescriptor().getIdentifier(); + } + + private ProcessDescriptionType createProcessDescription() { + + AlgorithmDescriptor algorithmDescriptor = getAlgorithmDescriptor(); + + ProcessDescriptionsDocument document = ProcessDescriptionsDocument.Factory.newInstance(); + ProcessDescriptions processDescriptions = document.addNewProcessDescriptions(); + ProcessDescriptionType processDescription = processDescriptions.addNewProcessDescription(); + + if (algorithmDescriptor == null) { + throw new IllegalStateException("Instance must have an algorithm descriptor"); + } else { + + // 1. Identifier + processDescription.setStatusSupported(algorithmDescriptor.getStatusSupported()); + processDescription.setStoreSupported(algorithmDescriptor.getStoreSupported()); + processDescription.setProcessVersion(algorithmDescriptor.getVersion()); + processDescription.addNewIdentifier().setStringValue(algorithmDescriptor.getIdentifier()); + processDescription.addNewTitle().setStringValue( algorithmDescriptor.hasTitle() ? + algorithmDescriptor.getTitle() : + algorithmDescriptor.getIdentifier()); + if (algorithmDescriptor.hasAbstract()) { + processDescription.addNewAbstract().setStringValue(algorithmDescriptor.getAbstract()); + } + + // 2. Inputs + Collection inputDescriptors = algorithmDescriptor.getInputDescriptors(); + DataInputs dataInputs = null; + if (inputDescriptors.size() > 0) { + dataInputs = processDescription.addNewDataInputs(); + } + for (InputDescriptor inputDescriptor : inputDescriptors) { + + InputDescriptionType dataInput = dataInputs.addNewInput(); + dataInput.setMinOccurs(inputDescriptor.getMinOccurs()); + dataInput.setMaxOccurs(inputDescriptor.getMaxOccurs()); + + dataInput.addNewIdentifier().setStringValue(inputDescriptor.getIdentifier()); + dataInput.addNewTitle().setStringValue( inputDescriptor.hasTitle() ? + inputDescriptor.getTitle() : + inputDescriptor.getIdentifier()); + if (inputDescriptor.hasAbstract()) { + dataInput.addNewAbstract().setStringValue(inputDescriptor.getAbstract()); + } + + if (inputDescriptor instanceof LiteralDataInputDescriptor) { + LiteralDataInputDescriptor literalDescriptor = (LiteralDataInputDescriptor)inputDescriptor; + + LiteralInputType literalData = dataInput.addNewLiteralData(); + literalData.addNewDataType().setReference(literalDescriptor.getDataType()); + + if (literalDescriptor.hasDefaultValue()) { + literalData.setDefaultValue(literalDescriptor.getDefaultValue()); + } + if (literalDescriptor.hasAllowedValues()) { + AllowedValues allowed = literalData.addNewAllowedValues(); + for (String allowedValue : literalDescriptor.getAllowedValues()) { + allowed.addNewValue().setStringValue(allowedValue); + } + } else { + literalData.addNewAnyValue(); + } + + } else if (inputDescriptor instanceof ComplexDataInputDescriptor) { + SupportedComplexDataInputType complexDataType = dataInput.addNewComplexData(); + ComplexDataInputDescriptor complexInputDescriptor = + (ComplexDataInputDescriptor)inputDescriptor; + if (complexInputDescriptor.hasMaximumMegaBytes()) { + complexDataType.setMaximumMegabytes(complexInputDescriptor.getMaximumMegaBytes()); + } + describeComplexDataInputType(complexDataType, inputDescriptor.getBinding()); + } + } + + // 3. Outputs + ProcessOutputs dataOutputs = processDescription.addNewProcessOutputs(); + Collection outputDescriptors = algorithmDescriptor.getOutputDescriptors(); + if (outputDescriptors.size() < 1) { + LOGGER.error("No outputs found for algorithm {}", algorithmDescriptor.getIdentifier()); + } + for (OutputDescriptor outputDescriptor : outputDescriptors) { + + OutputDescriptionType dataOutput = dataOutputs.addNewOutput(); + dataOutput.addNewIdentifier().setStringValue(outputDescriptor.getIdentifier()); + dataOutput.addNewTitle().setStringValue( outputDescriptor.hasTitle() ? + outputDescriptor.getTitle() : + outputDescriptor.getIdentifier()); + if (outputDescriptor.hasAbstract()) { + dataOutput.addNewAbstract().setStringValue(outputDescriptor.getAbstract()); + } + + if (outputDescriptor instanceof LiteralDataOutputDescriptor) { + LiteralDataOutputDescriptor literalDescriptor = (LiteralDataOutputDescriptor)outputDescriptor; + dataOutput.addNewLiteralOutput().addNewDataType(). + setReference(literalDescriptor.getDataType()); + } else if (outputDescriptor instanceof ComplexDataOutputDescriptor) { + describeComplexDataOutputType(dataOutput.addNewComplexOutput(), outputDescriptor.getBinding()); + } + } + } + return document.getProcessDescriptions().getProcessDescriptionArray(0); + } + + private void describeComplexDataInputType(SupportedComplexDataType complexData, Class dataTypeClass) { + List parsers = ParserFactory.getInstance().getAllParsers(); + List foundParsers = new ArrayList(); + for (IParser parser : parsers) { +// /*2.0*/ Class[] supportedClasses = parser.getSupportedInternalOutputDataType(); + /*3.0*/ Class[] supportedClasses = parser.getSupportedDataBindings(); + for (Class clazz : supportedClasses) { + if (dataTypeClass.isAssignableFrom(clazz)) { + foundParsers.add(parser); + } + } + } + describeComplexDataType(complexData, foundParsers); + } + + private void describeComplexDataOutputType(SupportedComplexDataType complexData, Class dataTypeClass) { + + List generators = GeneratorFactory.getInstance().getAllGenerators(); + List foundGenerators = new ArrayList(); + for (IGenerator generator : generators) { +// /*2.0*/ Class[] supportedClasses = generator.getSupportedInternalInputDataType(); // appears to have been removed in 52n WPS 3.0 + /*3.0*/ Class[] supportedClasses = generator.getSupportedDataBindings(); + for (Class clazz : supportedClasses) { + if (clazz.isAssignableFrom(dataTypeClass)) { + foundGenerators.add(generator); + } + } + } + describeComplexDataType(complexData, foundGenerators); + } + + private void describeComplexDataType( + SupportedComplexDataType complexData, + List handlers) + { + ComplexDataCombinationType defaultFormatType = complexData.addNewDefault(); + ComplexDataCombinationsType supportedFormatType = complexData.addNewSupported(); + + boolean needDefault = true; + for (IOHandler handler : handlers) { + + Format[] fullFormats = handler.getSupportedFullFormats(); + if (fullFormats != null && fullFormats.length > 0) { + if (needDefault) { + needDefault = false; + describeComplexDataFormat( + defaultFormatType.addNewFormat(), + fullFormats[0]); + } + for (int formatIndex = 0, formatCount = fullFormats.length; formatIndex < formatCount; ++formatIndex) { + describeComplexDataFormat( + supportedFormatType.addNewFormat(), + fullFormats[formatIndex]); + } + } else { + + String[] formats = handler.getSupportedFormats(); + + if (formats == null || formats.length == 0) { + LOGGER.warn("Skipping IOHandler {} in ProcessDescription generation for {}, no formats specified", + handler.getClass().getSimpleName(), + getWellKnownName()); + } else { + // if formats, encodings or schemas arrays are 'null' or empty, create + // new array with single 'null' element. We do this so we can utilize + // a single set of nested loops to process all permutations. 'null' + // values will not be output... + String[] encodings = handler.getSupportedEncodings(); + if (encodings == null || encodings.length == 0) { + encodings = new String[] { null }; + } + String[] schemas = handler.getSupportedSchemas(); + if (schemas == null || schemas.length == 0) { + schemas = new String[] { null }; + } + + for (String format : formats) { + for (String encoding : encodings) { + for (String schema : schemas) { + if (needDefault) { + needDefault = false; + describeComplexDataFormat( + defaultFormatType.addNewFormat(), + format, encoding, schema); + } + describeComplexDataFormat( + supportedFormatType.addNewFormat(), + format, encoding, schema); + } + } + } + } + } + } + } + + private void describeComplexDataFormat( + ComplexDataDescriptionType description, + Format format) + { + describeComplexDataFormat(description, + format.getMimetype(), + format.getEncoding(), + format.getSchema()); + } + + private void describeComplexDataFormat(ComplexDataDescriptionType description, + String format, + String encoding, + String schema) { + if ( !Strings.isNullOrEmpty(format)) { + description.setMimeType(format); + } + if ( !Strings.isNullOrEmpty(encoding)) { + description.setEncoding(encoding); + } + if ( !Strings.isNullOrEmpty(schema)) { + description.setSchema(schema); + } + } + + @Override + public boolean processDescriptionIsValid() { + XmlOptions xmlOptions = new XmlOptions(); + List xmlValidationErrorList = new ArrayList(); + xmlOptions.setErrorListener(xmlValidationErrorList); + boolean valid = getDescription().validate(xmlOptions); + if (!valid) { + LOGGER.error("Error validating process description for " + getClass().getCanonicalName()); + for (XmlValidationError xmlValidationError : xmlValidationErrorList) { + LOGGER.error("\tMessage: {}", xmlValidationError.getMessage()); + LOGGER.error("\tLocation of invalid XML: {}", + xmlValidationError.getCursorLocation().xmlText()); + } + } + return valid; + } + + protected final synchronized AlgorithmDescriptor getAlgorithmDescriptor() { + if (descriptor == null) { + descriptor = createAlgorithmDescriptor(); + } + return descriptor; + } + + protected abstract AlgorithmDescriptor createAlgorithmDescriptor(); + + @Override + public Class getInputDataType(String identifier) { + AlgorithmDescriptor algorithmDescriptor = getAlgorithmDescriptor(); + if (algorithmDescriptor != null) { + return getAlgorithmDescriptor().getInputDescriptor(identifier).getBinding(); + } else { + throw new IllegalStateException("Instance must have an algorithm descriptor"); + } + } + + @Override + public Class getOutputDataType(String identifier) { + AlgorithmDescriptor algorithmDescriptor = getAlgorithmDescriptor(); + if (algorithmDescriptor != null) { + return getAlgorithmDescriptor().getOutputDescriptor(identifier).getBinding(); + } else { + throw new IllegalStateException("Instance must have an algorithm descriptor"); + } + } + + private List observers = new ArrayList(); + private Object state = null; + + @Override + public Object getState() { + return state; + } + + @Override + public void update(Object state) { + this.state = state; + notifyObservers(); + } + + @Override + public void addObserver(IObserver o) { + observers.add(o); + } + + @Override + public void removeObserver(IObserver o) { + observers.remove(o); + } + + public void notifyObservers() { + Iterator i = observers.iterator(); + while (i.hasNext()) { + IObserver o = (IObserver) i.next(); + o.update(this); + } + } + + List errorList = new ArrayList(); + protected List addError(String error) { + errorList.add(error); + return errorList; + } + + @Override + public List getErrors() { + return errorList; + } +} diff --git a/src/main/java/org/n52/wps/server/AbstractObservableAlgorithm.java b/src/main/java/org/n52/wps/server/AbstractObservableAlgorithm.java new file mode 100644 index 0000000..987ac45 --- /dev/null +++ b/src/main/java/org/n52/wps/server/AbstractObservableAlgorithm.java @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionsDocument; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.n52.wps.server.observerpattern.IObserver; +import org.n52.wps.server.observerpattern.ISubject; + +public abstract class AbstractObservableAlgorithm implements IAlgorithm, ISubject{ + + protected ProcessDescriptionType description; + protected final String wkName; + private static Logger LOGGER = LoggerFactory.getLogger(AbstractAlgorithm.class); + + /** + * default constructor, calls the initializeDescription() Method + */ + public AbstractObservableAlgorithm() { + this.description = initializeDescription(); + this.wkName = ""; + } + + public AbstractObservableAlgorithm(ProcessDescriptionType description) { + this.description = description; + this.wkName = ""; + } + + /** + * default constructor, calls the initializeDescription() Method + */ + public AbstractObservableAlgorithm(String wellKnownName) { + this.wkName = wellKnownName; // Has to be initialized before the description. + this.description = initializeDescription(); + } + + /** + * This method should be overwritten, in case you want to have a way of initializing. + * + * In detail it looks for a xml descfile, which is located in the same directory as the implementing class and has the same + * name as the class, but with the extension XML. + * @return + */ + protected ProcessDescriptionType initializeDescription() { + String className = this.getClass().getName().replace(".", "/"); + InputStream xmlDesc = this.getClass().getResourceAsStream("/" + className + ".xml"); + try { + XmlOptions option = new XmlOptions(); + option.setLoadTrimTextBuffer(); + ProcessDescriptionsDocument doc = ProcessDescriptionsDocument.Factory.parse(xmlDesc, option); + if(doc.getProcessDescriptions().getProcessDescriptionArray().length == 0) { + LOGGER.warn("ProcessDescription does not contain correct any description"); + return null; + } + + // Checking that the process name (full class name or well-known name) matches the identifier. + if(!doc.getProcessDescriptions().getProcessDescriptionArray(0).getIdentifier().getStringValue().equals(this.getClass().getName()) && + !doc.getProcessDescriptions().getProcessDescriptionArray(0).getIdentifier().getStringValue().equals(this.getWellKnownName())) { + doc.getProcessDescriptions().getProcessDescriptionArray(0).getIdentifier().setStringValue(this.getClass().getName()); + LOGGER.warn("Identifier was not correct, was changed now temporary for server use to " + this.getClass().getName() + ". Please change it later in the description!"); + } + + return doc.getProcessDescriptions().getProcessDescriptionArray(0); + } + catch(IOException e) { + LOGGER.warn("Could not initialize algorithm, parsing error: " + this.getClass().getName(), e); + } + catch(XmlException e) { + LOGGER.warn("Could not initialize algorithm, parsing error: " + this.getClass().getName(), e); + } + return null; + } + + public ProcessDescriptionType getDescription() { + return description; + } + + public boolean processDescriptionIsValid() { + return description.validate(); + } + + public String getWellKnownName() { + return this.wkName; + } + + private List observers = new ArrayList(); + + private Object state = null; + + public Object getState() { + return state; + } + + public void update(Object state) { + this.state = state; + notifyObservers(); + } + + public void addObserver(IObserver o) { + observers.add(o); + } + + public void removeObserver(IObserver o) { + observers.remove(o); + } + + public void notifyObservers() { + Iterator i = observers.iterator(); + while (i.hasNext()) { + IObserver o = (IObserver) i.next(); + o.update(this); + } + } +} diff --git a/src/main/java/org/n52/wps/server/AbstractSelfDescribingAlgorithm.java b/src/main/java/org/n52/wps/server/AbstractSelfDescribingAlgorithm.java new file mode 100644 index 0000000..0f198b0 --- /dev/null +++ b/src/main/java/org/n52/wps/server/AbstractSelfDescribingAlgorithm.java @@ -0,0 +1,420 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.lang.reflect.Constructor; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import net.opengis.ows.x11.DomainMetadataType; +import net.opengis.wps.x100.CRSsType; +import net.opengis.wps.x100.ComplexDataCombinationType; +import net.opengis.wps.x100.ComplexDataCombinationsType; +import net.opengis.wps.x100.ComplexDataDescriptionType; +import net.opengis.wps.x100.InputDescriptionType; +import net.opengis.wps.x100.LiteralInputType; +import net.opengis.wps.x100.LiteralOutputType; +import net.opengis.wps.x100.OutputDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType; +import net.opengis.wps.x100.ProcessDescriptionType.DataInputs; +import net.opengis.wps.x100.ProcessDescriptionType.ProcessOutputs; +import net.opengis.wps.x100.ProcessDescriptionsDocument; +import net.opengis.wps.x100.ProcessDescriptionsDocument.ProcessDescriptions; +import net.opengis.wps.x100.SupportedCRSsType; +import net.opengis.wps.x100.SupportedCRSsType.Default; +import net.opengis.wps.x100.SupportedComplexDataInputType; +import net.opengis.wps.x100.SupportedComplexDataType; + +import org.n52.wps.FormatDocument.Format; +import org.n52.wps.io.GeneratorFactory; +import org.n52.wps.io.IGenerator; +import org.n52.wps.io.IParser; +import org.n52.wps.io.ParserFactory; +import org.n52.wps.io.data.IBBOXData; +import org.n52.wps.io.data.IComplexData; +import org.n52.wps.io.data.ILiteralData; +import org.n52.wps.server.observerpattern.IObserver; +import org.n52.wps.server.observerpattern.ISubject; + + +public abstract class AbstractSelfDescribingAlgorithm extends AbstractAlgorithm implements ISubject{ + + @Override + protected ProcessDescriptionType initializeDescription() { + ProcessDescriptionsDocument document = ProcessDescriptionsDocument.Factory.newInstance(); + ProcessDescriptions processDescriptions = document.addNewProcessDescriptions(); + ProcessDescriptionType processDescription = processDescriptions.addNewProcessDescription(); + processDescription.setStatusSupported(true); + processDescription.setStoreSupported(true); + processDescription.setProcessVersion("1.0.0"); + + //1. Identifier + processDescription.addNewIdentifier().setStringValue(this.getClass().getName()); + processDescription.addNewTitle().setStringValue(this.getClass().getCanonicalName()); + + //2. Inputs + List identifiers = this.getInputIdentifiers(); + DataInputs dataInputs = null; + if(identifiers.size()>0){ + dataInputs = processDescription.addNewDataInputs(); + } + + for(String identifier : identifiers){ + InputDescriptionType dataInput = dataInputs.addNewInput(); + dataInput.setMinOccurs(getMinOccurs(identifier)); + dataInput.setMaxOccurs(getMaxOccurs(identifier)); + dataInput.addNewIdentifier().setStringValue(identifier); + dataInput.addNewTitle().setStringValue(identifier); + + Class inputDataTypeClass = this.getInputDataType(identifier); + Class[] interfaces = inputDataTypeClass.getInterfaces(); + + //we have to add this because of the new AbstractLiteralDataBinding class + if(interfaces.length == 0){ + interfaces = inputDataTypeClass.getSuperclass().getInterfaces(); + } + + for(Class implementedInterface : interfaces){ + if(implementedInterface.equals(ILiteralData.class)){ + LiteralInputType literalData = dataInput.addNewLiteralData(); + String inputClassType = ""; + + Constructor[] constructors = inputDataTypeClass.getConstructors(); + for(Constructor constructor : constructors){ + Class[] parameters = constructor.getParameterTypes(); + if(parameters.length==1){ + inputClassType = parameters[0].getSimpleName(); + } + } + + if(inputClassType.length()>0){ + DomainMetadataType datatype = literalData.addNewDataType(); + datatype.setReference("xs:"+inputClassType.toLowerCase()); + literalData.addNewAnyValue(); + } + }else if(implementedInterface.equals(IBBOXData.class)){ + SupportedCRSsType bboxData = dataInput.addNewBoundingBoxData(); + String[] supportedCRSAray = getSupportedCRSForBBOXInput(identifier); + for(int i = 0; i parsers = ParserFactory.getInstance().getAllParsers(); + List foundParsers = new ArrayList(); + for(IParser parser : parsers) { + Class[] supportedClasses = parser.getSupportedDataBindings(); + for(Class clazz : supportedClasses){ + if(clazz.equals(inputDataTypeClass)){ + foundParsers.add(parser); + } + + } + } + + addInputFormats(complexData, foundParsers); + + } + } + } + + //3. Outputs + ProcessOutputs dataOutputs = processDescription.addNewProcessOutputs(); + List outputIdentifiers = this.getOutputIdentifiers(); + for(String identifier : outputIdentifiers){ + OutputDescriptionType dataOutput = dataOutputs.addNewOutput(); + + + dataOutput.addNewIdentifier().setStringValue(identifier); + dataOutput.addNewTitle().setStringValue(identifier); + dataOutput.addNewAbstract().setStringValue(identifier); + + Class outputDataTypeClass = this.getOutputDataType(identifier); + Class[] interfaces = outputDataTypeClass.getInterfaces(); + + //we have to add this because of the new AbstractLiteralDataBinding class + if(interfaces.length == 0){ + interfaces = outputDataTypeClass.getSuperclass().getInterfaces(); + } + for(Class implementedInterface : interfaces){ + + + if(implementedInterface.equals(ILiteralData.class)){ + LiteralOutputType literalData = dataOutput.addNewLiteralOutput(); + String outputClassType = ""; + + Constructor[] constructors = outputDataTypeClass.getConstructors(); + for(Constructor constructor : constructors){ + Class[] parameters = constructor.getParameterTypes(); + if(parameters.length==1){ + outputClassType = parameters[0].getSimpleName(); + } + } + + if(outputClassType.length()>0){ + literalData.addNewDataType().setReference("xs:"+outputClassType.toLowerCase()); + } + + }else if(implementedInterface.equals(IBBOXData.class)){ + SupportedCRSsType bboxData = dataOutput.addNewBoundingBoxOutput(); + String[] supportedCRSAray = getSupportedCRSForBBOXOutput(identifier); + for(int i = 0; i generators = GeneratorFactory.getInstance().getAllGenerators(); + List foundGenerators = new ArrayList(); + for(IGenerator generator : generators) { + Class[] supportedClasses = generator.getSupportedDataBindings(); + for(Class clazz : supportedClasses){ + if(clazz.equals(outputDataTypeClass)){ + foundGenerators.add(generator); + } + + } + } + + addOutputFormats(complexData, foundGenerators); + + } + } + } + + return document.getProcessDescriptions().getProcessDescriptionArray(0); + } + + /** + * Override this class for BBOX input data to set supported mime types. The first one in the resulting array will be the default one. + * @param identifier ID of the input BBOXType + * @return + */ + public String[] getSupportedCRSForBBOXInput(String identifier){ + return new String[0]; + } + + /** + * Override this class for BBOX output data to set supported mime types. The first one in the resulting array will be the default one. + * @param identifier ID of the input BBOXType + * @return + */ + public String[] getSupportedCRSForBBOXOutput(String identifier){ + return new String[0]; + } + + public BigInteger getMinOccurs(String identifier){ + return new BigInteger("1"); + } + public BigInteger getMaxOccurs(String identifier){ + return new BigInteger("1"); + } + + public abstract List getInputIdentifiers(); + public abstract List getOutputIdentifiers(); + + + + private List observers = new ArrayList(); + + private Object state = null; + + public Object getState() { + return state; + } + + public void update(Object state) { + this.state = state; + notifyObservers(); + } + + public void addObserver(IObserver o) { + observers.add(o); + } + + public void removeObserver(IObserver o) { + observers.remove(o); + } + + public void notifyObservers() { + Iterator i = observers.iterator(); + while (i.hasNext()) { + IObserver o = (IObserver) i.next(); + o.update(this); + } + } + + @Override + public List getErrors() { + List errors = new ArrayList(); + return errors; + } + + + private void addInputFormats(SupportedComplexDataInputType complexData, + List foundParsers) { + ComplexDataCombinationsType supportedInputFormat = complexData + .addNewSupported(); + + for (int i = 0; i < foundParsers.size(); i++) { + IParser parser = foundParsers.get(i); + + Format[] supportedFullFormats = parser.getSupportedFullFormats(); + + if (complexData.getDefault() == null) { + ComplexDataCombinationType defaultInputFormat = complexData + .addNewDefault(); + /* + * default format will be the first config format + */ + Format format = supportedFullFormats[0]; + ComplexDataDescriptionType defaultFormat = defaultInputFormat + .addNewFormat(); + defaultFormat.setMimeType(format.getMimetype()); + + String encoding = format.getEncoding(); + + if (encoding != null && !encoding.equals("")) { + defaultFormat.setEncoding(encoding); + } + + String schema = format.getSchema(); + + if (schema != null && !schema.equals("")) { + defaultFormat.setSchema(schema); + } + + } + + for (int j = 0; j < supportedFullFormats.length; j++) { + /* + * create supportedFormat for each mimetype, encoding, schema + * composition mimetypes can have several encodings and schemas + */ + Format format1 = supportedFullFormats[j]; + + /* + * add one format for this mimetype + */ + ComplexDataDescriptionType supportedFormat = supportedInputFormat + .addNewFormat(); + supportedFormat.setMimeType(format1.getMimetype()); + if (format1.getEncoding() != null) { + supportedFormat.setEncoding(format1.getEncoding()); + } + if (format1.getSchema() != null) { + supportedFormat.setSchema(format1.getSchema()); + } + } + } + } + + private void addOutputFormats(SupportedComplexDataType complexData, + List foundGenerators) { + ComplexDataCombinationsType supportedOutputFormat = complexData + .addNewSupported(); + + for (int i = 0; i < foundGenerators.size(); i++) { + IGenerator generator = foundGenerators.get(i); + + Format[] supportedFullFormats = generator.getSupportedFullFormats(); + + if (complexData.getDefault() == null) { + ComplexDataCombinationType defaultInputFormat = complexData + .addNewDefault(); + /* + * default format will be the first config format + */ + Format format = supportedFullFormats[0]; + ComplexDataDescriptionType defaultFormat = defaultInputFormat + .addNewFormat(); + defaultFormat.setMimeType(format.getMimetype()); + + String encoding = format.getEncoding(); + + if (encoding != null && !encoding.equals("")) { + defaultFormat.setEncoding(encoding); + } + + String schema = format.getSchema(); + + if (schema != null && !schema.equals("")) { + defaultFormat.setSchema(schema); + } + + } + + for (int j = 0; j < supportedFullFormats.length; j++) { + /* + * create supportedFormat for each mimetype, encoding, schema + * composition mimetypes can have several encodings and schemas + */ + Format format1 = supportedFullFormats[j]; + + /* + * add one format for this mimetype + */ + ComplexDataDescriptionType supportedFormat = supportedOutputFormat + .addNewFormat(); + supportedFormat.setMimeType(format1.getMimetype()); + if (format1.getEncoding() != null) { + supportedFormat.setEncoding(format1.getEncoding()); + } + if (format1.getSchema() != null) { + supportedFormat.setSchema(format1.getSchema()); + } + } + } + } + +} diff --git a/src/main/java/org/n52/wps/server/AbstractTransactionalAlgorithm.java b/src/main/java/org/n52/wps/server/AbstractTransactionalAlgorithm.java new file mode 100644 index 0000000..7f529ba --- /dev/null +++ b/src/main/java/org/n52/wps/server/AbstractTransactionalAlgorithm.java @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + + +import java.util.Map; + +import org.n52.wps.io.data.IData; + +import net.opengis.wps.x100.ExecuteDocument; + +public abstract class AbstractTransactionalAlgorithm implements IAlgorithm{ + + + protected String algorithmID; + + + public AbstractTransactionalAlgorithm(String algorithmID){ + this.algorithmID = algorithmID; + + } + + public String getAlgorithmID() { + return algorithmID; + } + + public abstract Map run(ExecuteDocument document); + + + +} diff --git a/src/main/java/org/n52/wps/server/AlgorithmParameterException.java b/src/main/java/org/n52/wps/server/AlgorithmParameterException.java new file mode 100644 index 0000000..cf3dcbc --- /dev/null +++ b/src/main/java/org/n52/wps/server/AlgorithmParameterException.java @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + + +public class AlgorithmParameterException extends RuntimeException { + private static final long serialVersionUID = -514774594848925943L; + + public AlgorithmParameterException() { + super(); + } + + public AlgorithmParameterException(String message) { + super(message); + } +} diff --git a/src/main/java/org/n52/wps/server/IAlgorithm.java b/src/main/java/org/n52/wps/server/IAlgorithm.java new file mode 100644 index 0000000..e5cc3ce --- /dev/null +++ b/src/main/java/org/n52/wps/server/IAlgorithm.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.List; +import java.util.Map; + +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.n52.wps.io.data.IData; +import org.n52.wps.server.ExceptionReport; + +/** + * @author Bastian Schaeffer, University of Muenster, Theodor Foerster, ITC + * + */ +public interface IAlgorithm { + + Map run(Map> inputData) throws ExceptionReport; + + List getErrors(); + + ProcessDescriptionType getDescription(); + + /** Returns some well-known name for the process. + * + * @return Returns some well-known name for the process or algorithm + * if that exists, else returns an empty String, never null. + * @note The fully-qualified class name is gotten via getName(); + */ + String getWellKnownName(); + + /** + * Checks if the processDescription complies to the process itself and fits any schema or other dependencies. + */ + boolean processDescriptionIsValid(); + + Class< ? > getInputDataType(String id); + + Class< ? > getOutputDataType(String id); + +} diff --git a/src/main/java/org/n52/wps/server/IAlgorithmRepository.java b/src/main/java/org/n52/wps/server/IAlgorithmRepository.java new file mode 100644 index 0000000..9062db6 --- /dev/null +++ b/src/main/java/org/n52/wps/server/IAlgorithmRepository.java @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.Collection; + +import net.opengis.wps.x100.ProcessDescriptionType; + +/** + * @author Bastian Schaeffer, University of Muenster, Theodor Foerster, ITC + * + */ +public interface IAlgorithmRepository { + Collection getAlgorithmNames(); + + IAlgorithm getAlgorithm(String processID); + + ProcessDescriptionType getProcessDescription(String processID); + + boolean containsAlgorithm(String processID); + + /** + * use to free resources + */ + public void shutdown(); + + +} diff --git a/src/main/java/org/n52/wps/server/ITransactionalAlgorithmRepository.java b/src/main/java/org/n52/wps/server/ITransactionalAlgorithmRepository.java new file mode 100644 index 0000000..12810ed --- /dev/null +++ b/src/main/java/org/n52/wps/server/ITransactionalAlgorithmRepository.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +/** + * @author Bastian Schaeffer, University of Muenster + * + */ +public interface ITransactionalAlgorithmRepository extends IAlgorithmRepository{ + boolean addAlgorithm(Object className); + boolean removeAlgorithm(Object className); +} + diff --git a/src/main/java/org/n52/wps/server/IWorkspaceRepository.java b/src/main/java/org/n52/wps/server/IWorkspaceRepository.java new file mode 100644 index 0000000..1eddd22 --- /dev/null +++ b/src/main/java/org/n52/wps/server/IWorkspaceRepository.java @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.io.File; + +public interface IWorkspaceRepository { + File getWorkspace(); + boolean createWorkspace(); +} diff --git a/src/main/java/org/n52/wps/server/LocalAlgorithmRepository.java b/src/main/java/org/n52/wps/server/LocalAlgorithmRepository.java new file mode 100644 index 0000000..1f74349 --- /dev/null +++ b/src/main/java/org/n52/wps/server/LocalAlgorithmRepository.java @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.PropertyDocument.Property; +import org.n52.wps.algorithm.annotation.Algorithm; +import org.n52.wps.commons.WPSConfig; + + + +/** + * A static repository to retrieve the available algorithms. + * @author foerster + * + */ +public class LocalAlgorithmRepository implements ITransactionalAlgorithmRepository{ + + private static Logger LOGGER = LoggerFactory.getLogger(LocalAlgorithmRepository.class); + private Map algorithmMap; + private Map processDescriptionMap; + + public LocalAlgorithmRepository() { + algorithmMap = new HashMap(); + processDescriptionMap = new HashMap(); + + // check if the repository is active + if(WPSConfig.getInstance().isRepositoryActive(this.getClass().getCanonicalName())){ + Property[] propertyArray = WPSConfig.getInstance().getPropertiesForRepositoryClass(this.getClass().getCanonicalName()); + for(Property property : propertyArray){ + // check the name and active state + if(property.getName().equalsIgnoreCase("Algorithm") && property.getActive()){ + addAlgorithm(property.getStringValue()); + } + } + } else { + LOGGER.debug("Local Algorithm Repository is inactive."); + } + } + + public boolean addAlgorithms(String[] algorithms) { + for(String algorithmClassName : algorithms) { + addAlgorithm(algorithmClassName); + } + LOGGER.info("Algorithms registered!"); + return true; + + } + + public IAlgorithm getAlgorithm(String className) { + try { + return loadAlgorithm(algorithmMap.get(className)); + } catch (Exception e) { + LOGGER.error("error getting algorithm",e); + return null; + } + } + + public Collection getAlgorithmNames() { + return new ArrayList(algorithmMap.keySet()); + } + + public boolean containsAlgorithm(String className) { + return algorithmMap.containsKey(className); + } + + private IAlgorithm loadAlgorithm(String algorithmClassName) throws Exception { + Class algorithmClass = LocalAlgorithmRepository.class.getClassLoader().loadClass(algorithmClassName); + IAlgorithm algorithm = null; + if (IAlgorithm.class.isAssignableFrom(algorithmClass)) { + algorithm = IAlgorithm.class.cast(algorithmClass.newInstance()); + } else if (algorithmClass.isAnnotationPresent(Algorithm.class)) { + // we have an annotated algorithm that doesn't implement IAlgorithm + // wrap it in a proxy class + algorithm = new AbstractAnnotatedAlgorithm.Proxy(algorithmClass); + } + else { + throw new Exception("Could not load algorithm " + algorithmClassName + " does not implement IAlgorithm or have a Algorithm annotation."); + } + + if(!algorithm.processDescriptionIsValid()) { + LOGGER.warn("Algorithm description is not valid: " + algorithmClassName); + throw new Exception("Could not load algorithm " +algorithmClassName +". ProcessDescription Not Valid."); + } + return algorithm; + } + + public boolean addAlgorithm(Object processID) { + if(!(processID instanceof String)){ + return false; + } + String algorithmClassName = (String) processID; + + algorithmMap.put(algorithmClassName, algorithmClassName); + LOGGER.info("Algorithm class registered: " + algorithmClassName); + + return true; + + } + + public boolean removeAlgorithm(Object processID) { + if(!(processID instanceof String)){ + return false; + } + String className = (String) processID; + if(algorithmMap.containsKey(className)){ + algorithmMap.remove(className); + return true; + } + return false; + } + + @Override + public ProcessDescriptionType getProcessDescription(String processID) { + if(!processDescriptionMap.containsKey(processID)){ + processDescriptionMap.put(processID, getAlgorithm(processID).getDescription()); + } + return processDescriptionMap.get(processID); + } + + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + + + + + + + + + + + +} diff --git a/src/main/java/org/n52/wps/server/ServiceLoaderAlgorithmRepository.java b/src/main/java/org/n52/wps/server/ServiceLoaderAlgorithmRepository.java new file mode 100644 index 0000000..e47d280 --- /dev/null +++ b/src/main/java/org/n52/wps/server/ServiceLoaderAlgorithmRepository.java @@ -0,0 +1,90 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.opengis.wps.x100.ProcessDescriptionType; + +public class ServiceLoaderAlgorithmRepository implements IAlgorithmRepository { + + private static final Logger logger = LoggerFactory.getLogger(ServiceLoaderAlgorithmRepository.class); + private Map> currentAlgorithms; + + public ServiceLoaderAlgorithmRepository() { + this.currentAlgorithms = loadAlgorithms(); + } + + private Map> loadAlgorithms() { + Map> result = new HashMap>(); + ServiceLoader loader = ServiceLoader.load(IAlgorithm.class); + + for (IAlgorithm ia : loader) { + logger.debug("Adding algorithm with identifier {} and class {}", + ia.getWellKnownName(), ia.getClass().getCanonicalName()); + result.put(ia.getWellKnownName(), ia.getClass()); + } + + return result; + } + + @Override + public Collection getAlgorithmNames() { + return this.currentAlgorithms.keySet(); + } + + @Override + public IAlgorithm getAlgorithm(String processID) { + Class clazz = this.currentAlgorithms.get(processID); + if (clazz != null) { + try { + return clazz.newInstance(); + } catch (InstantiationException e) { + logger.warn(e.getMessage(), e); + } catch (IllegalAccessException e) { + logger.warn(e.getMessage(), e); + } + } + return null; + } + + @Override + public ProcessDescriptionType getProcessDescription(String processID) { + IAlgorithm algo = getAlgorithm(processID); + if (algo != null) { + return algo.getDescription(); + } + return null; + } + + @Override + public boolean containsAlgorithm(String processID) { + return this.currentAlgorithms.containsKey(processID); + } + + @Override + public void shutdown() { + } + + +} diff --git a/src/main/java/org/n52/wps/server/UploadedAlgorithmRepository.java b/src/main/java/org/n52/wps/server/UploadedAlgorithmRepository.java new file mode 100644 index 0000000..9d840db --- /dev/null +++ b/src/main/java/org/n52/wps/server/UploadedAlgorithmRepository.java @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.n52.wps.PropertyDocument.Property; +import org.n52.wps.commons.WPSConfig; + +/** + * A static repository to retrieve the available algorithms. + * + * @author foerster, Bastian Schaeffer, University of Muenster + * + */ +public class UploadedAlgorithmRepository implements + ITransactionalAlgorithmRepository { + + private static Logger LOGGER = LoggerFactory + .getLogger(LocalAlgorithmRepository.class); + private Map algorithmMap; + private Map processDescriptionMap; + + public UploadedAlgorithmRepository() { + algorithmMap = new HashMap(); + processDescriptionMap = new HashMap(); + + if (WPSConfig.getInstance().isRepositoryActive( + this.getClass().getCanonicalName())) { + Property[] propertyArray = WPSConfig.getInstance() + .getPropertiesForRepositoryClass( + this.getClass().getCanonicalName()); + for (Property property : propertyArray) { + if (property.getName().equalsIgnoreCase("Algorithm") + && property.getActive()) { + addAlgorithm(property.getStringValue()); + } + } + } else { + LOGGER.debug("Local Algorithm Repository is inactive."); + } + + } + + public boolean addAlgorithms(String[] algorithms) { + for (String algorithmClassName : algorithms) { + addAlgorithm(algorithmClassName); + } + LOGGER.info("Algorithms registered!"); + return true; + + } + + public IAlgorithm getAlgorithm(String className) { + try { + return loadAlgorithm(algorithmMap.get(className)); + } catch (Exception e) { + LOGGER.error("error getting algorithm",e); + return null; + } + } + + public Collection getAlgorithms() { + Collection resultList = new ArrayList(); + try { + for (String algorithmClasses : algorithmMap.values()) { + resultList + .add(loadAlgorithm(algorithmMap.get(algorithmClasses))); + } + } catch (Exception e) { + LOGGER.error("error getting algorithm",e); + } + return resultList; + } + + public Collection getAlgorithmNames() { + return new ArrayList(algorithmMap.keySet()); + } + + public boolean containsAlgorithm(String className) { + return algorithmMap.containsKey(className); + } + + private IAlgorithm loadAlgorithm(String algorithmClassName) + throws Exception { + IAlgorithm algorithm = (IAlgorithm) LocalAlgorithmRepository.class + .getClassLoader().loadClass(algorithmClassName).newInstance(); + if (!algorithm.processDescriptionIsValid()) { + LOGGER.warn("Algorithm description is not valid: " + + algorithmClassName); + throw new Exception("Could not load algorithm " + + algorithmClassName + ". ProcessDescription Not Valid."); + } + return algorithm; + } + + public boolean addAlgorithm(Object processID) { + if (!(processID instanceof String)) { + return false; + } + String algorithmClassName = (String) processID; + + algorithmMap.put(algorithmClassName, algorithmClassName); + LOGGER.info("Algorithm class registered: " + algorithmClassName); + + return true; + + } + + public boolean removeAlgorithm(Object processID) { + if (!(processID instanceof String)) { + return false; + } + String className = (String) processID; + if (algorithmMap.containsKey(className)) { + algorithmMap.remove(className); + return true; + } + return false; + } + + @Override + public ProcessDescriptionType getProcessDescription(String processID) { + if (!processDescriptionMap.containsKey(processID)) { + processDescriptionMap.put(processID, getAlgorithm(processID) + .getDescription()); + } + return processDescriptionMap.get(processID); + } + + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/test/java/org/n52/test/mock/MockBinding.java b/src/test/java/org/n52/test/mock/MockBinding.java new file mode 100644 index 0000000..47faf5a --- /dev/null +++ b/src/test/java/org/n52/test/mock/MockBinding.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.test.mock; + +import org.n52.wps.io.data.IComplexData; + +/** + * + * @author tkunicki + */ +public class MockBinding implements IComplexData { + + private final MockComplexObject payload; + + public MockBinding(MockComplexObject payload) { + this.payload = payload; + } + + @Override + public MockComplexObject getPayload() { + return payload; + } + + @Override + public Class getSupportedClass() { + return MockComplexObject.class; + } + + @Override + public void dispose() { + + } +} diff --git a/src/test/java/org/n52/test/mock/MockComplexObject.java b/src/test/java/org/n52/test/mock/MockComplexObject.java new file mode 100644 index 0000000..8445f6b --- /dev/null +++ b/src/test/java/org/n52/test/mock/MockComplexObject.java @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.test.mock; + +/** + * + * @author tkunicki + */ +public class MockComplexObject { + + +} diff --git a/src/test/java/org/n52/test/mock/MockEnum.java b/src/test/java/org/n52/test/mock/MockEnum.java new file mode 100644 index 0000000..a34bc5c --- /dev/null +++ b/src/test/java/org/n52/test/mock/MockEnum.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.test.mock; + +/** + * + * @author tkunicki + */ +public enum MockEnum { + VALUE1, + VALUE2, + VALUE3 +} diff --git a/src/test/java/org/n52/test/mock/MockGenerator.java b/src/test/java/org/n52/test/mock/MockGenerator.java new file mode 100644 index 0000000..fb14ec1 --- /dev/null +++ b/src/test/java/org/n52/test/mock/MockGenerator.java @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.test.mock; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.n52.wps.FormatDocument.Format; +import org.n52.wps.io.IGenerator; +import org.n52.wps.io.data.IData; + +/** + * + * @author tkunicki + */ +public class MockGenerator implements IGenerator { + + public final static List schemaSet; + public final static List formatSet; + public final static List encodingSet; + + static { + schemaSet = Collections.unmodifiableList(new ArrayList(MockUtil.getParserSupportedSchemas(MockParser.class))); + formatSet = Collections.unmodifiableList(new ArrayList(MockUtil.getParserSupportedFormats(MockParser.class))); + encodingSet = Collections.unmodifiableList(new ArrayList(MockUtil.getParserSupportedEncodings(MockParser.class))); + } + +// @Override +// public OutputStream generate(IData coll) { +// throw new UnsupportedOperationException("Not supported yet."); +// } +// +// @Override +// public Class[] getSupportedInternalInputDataType() { +// return new Class[] { MockBinding.class }; +// } + + @Override + public boolean isSupportedSchema(String schema) { + return schemaSet.contains(schema); + } + + @Override + public boolean isSupportedFormat(String format) { + return formatSet.contains(format); + } + + @Override + public boolean isSupportedEncoding(String encoding) { + return encodingSet.contains(encoding); + } + + @Override + public String[] getSupportedSchemas() { + return schemaSet.toArray(new String[0]); + } + + @Override + public String[] getSupportedFormats() { + return formatSet.toArray(new String[0]); + } + + @Override + public String[] getSupportedEncodings() { + return encodingSet.toArray(new String[0]); + } + + @Override + public InputStream generateStream(IData data, String mimeType, String schema) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public InputStream generateBase64Stream(IData data, String mimeType, String schema) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupportedDataBinding(Class clazz) { + return Arrays.binarySearch(getSupportedDataBindings(), clazz) > -1; + } + + @Override + public Format[] getSupportedFullFormats() { + Format f = Format.Factory.newInstance(); + f.setSchema(schemaSet.get(0)); + f.setEncoding(encodingSet.get(0)); + f.setMimetype(formatSet.get(0)); + return new Format[] {f}; + } + + @Override + public Class[] getSupportedDataBindings() { + return new Class[] { MockBinding.class }; + } + +} diff --git a/src/test/java/org/n52/test/mock/MockParser.java b/src/test/java/org/n52/test/mock/MockParser.java new file mode 100644 index 0000000..53ce1ef --- /dev/null +++ b/src/test/java/org/n52/test/mock/MockParser.java @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.test.mock; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.n52.wps.FormatDocument.Format; +import org.n52.wps.io.IParser; +import org.n52.wps.io.data.IData; + +/** + * + * @author tkunicki + */ +public class MockParser implements IParser { + + public final static List schemaSet; + public final static List formatSet; + public final static List encodingSet; + + static { + schemaSet = Collections.unmodifiableList(new ArrayList(MockUtil.getParserSupportedSchemas(MockParser.class))); + formatSet = Collections.unmodifiableList(new ArrayList(MockUtil.getParserSupportedFormats(MockParser.class))); + encodingSet = Collections.unmodifiableList(new ArrayList(MockUtil.getParserSupportedEncodings(MockParser.class))); + } + +// @Override +// public IData parse(InputStream input, String mimeType) { +// throw new UnsupportedOperationException("Not supported yet."); +// } +// +// @Override +// public Class[] getSupportedInternalOutputDataType() { +// return new Class[] { MockBinding.class }; +// } + + @Override + public boolean isSupportedSchema(String schema) { + return schemaSet.contains(schema); + } + + @Override + public boolean isSupportedFormat(String format) { + return formatSet.contains(format); + } + + @Override + public boolean isSupportedEncoding(String encoding) { + return encodingSet.contains(encoding); + } + + @Override + public String[] getSupportedSchemas() { + return schemaSet.toArray(new String[0]); + } + + @Override + public String[] getSupportedFormats() { + return formatSet.toArray(new String[0]); + } + + @Override + public String[] getSupportedEncodings() { + return encodingSet.toArray(new String[0]); + } + + @Override + public IData parse(InputStream input, String mimeType, String schema) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public IData parseBase64(InputStream input, String mimeType, String schema) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupportedDataBinding(Class clazz) { + return Arrays.binarySearch(getSupportedDataBindings(), clazz) > -1; + } + + @Override + public Format[] getSupportedFullFormats() { + Format f = Format.Factory.newInstance(); + f.setSchema(schemaSet.get(0)); + f.setEncoding(encodingSet.get(0)); + f.setMimetype(formatSet.get(0)); + return new Format[] {f}; + } + + @Override + public Class[] getSupportedDataBindings() { + return new Class[] { MockBinding.class }; + } + +} diff --git a/src/test/java/org/n52/test/mock/MockUtil.java b/src/test/java/org/n52/test/mock/MockUtil.java new file mode 100644 index 0000000..b5e1651 --- /dev/null +++ b/src/test/java/org/n52/test/mock/MockUtil.java @@ -0,0 +1,124 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.test.mock; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import org.apache.commons.io.IOUtils; +import org.apache.xmlbeans.XmlException; +import org.n52.wps.PropertyDocument; +import org.n52.wps.PropertyDocument.Property; +import org.n52.wps.commons.WPSConfig; +import org.n52.wps.io.GeneratorFactory; +import org.n52.wps.io.IGenerator; +import org.n52.wps.io.IParser; +import org.n52.wps.io.ParserFactory; + +/** + * + * @author tkunicki + */ +public class MockUtil { + + public final static String SUPPORTED_SCHEMA = "supportedSchema"; + public final static String SUPPORTED_FORMAT = "supportedFormat"; + public final static String SUPPORTED_ENCODING = "supportedEncoding"; + + + private static WPSConfig MOCK_CONFIG; + public synchronized static WPSConfig getMockConfig() throws XmlException, IOException { + if (MOCK_CONFIG == null) { + InputStream configInputStream = null; + try { + configInputStream = MockUtil.class.getResourceAsStream( + "/org/n52/test/mock/wps_config.xml"); + WPSConfig.forceInitialization(configInputStream); + MOCK_CONFIG = WPSConfig.getInstance(); + ParserFactory.initialize(MOCK_CONFIG.getActiveRegisteredParser()); + GeneratorFactory.initialize(MOCK_CONFIG.getActiveRegisteredGenerator()); + } finally { + IOUtils.closeQuietly(configInputStream); + } + } + return MOCK_CONFIG; + } + + public static Collection getParserSupportedSchemas(Class clazz) { + return getParserPropertyValues(clazz, SUPPORTED_SCHEMA); + } + + public static Collection getParserSupportedFormats(Class clazz) { + return getParserPropertyValues(clazz, SUPPORTED_FORMAT); + } + + public static Collection getParserSupportedEncodings(Class clazz) { + return getParserPropertyValues(clazz, SUPPORTED_ENCODING); + } + + public static Collection getGeneratorSupportedSchemas(Class clazz) { + return getGeneratorPropertyValues(clazz, SUPPORTED_SCHEMA); + } + + public static Collection getGeneratorSupportedFormats(Class clazz) { + return getGeneratorPropertyValues(clazz, SUPPORTED_FORMAT); + } + + public static Collection getGeneratorSupportedEncodings(Class clazz) { + return getGeneratorPropertyValues(clazz, SUPPORTED_ENCODING); + } + + public static Collection getParserPropertyValues(Class clazz, String propertyName) { + String clazzName = clazz.getName(); + ArrayList propertyList = new ArrayList(); + try { + WPSConfig mockConfig = MockUtil.getMockConfig(); + PropertyDocument.Property properties[] = + mockConfig.getPropertiesForParserClass(clazzName); + for (Property property : properties) { + if (propertyName.equals(property.getName())) { + propertyList.add(property.getStringValue()); + } + } + propertyList.trimToSize(); + } catch (Exception e) { + System.err.println("ERROR parsing property " + propertyName + " for Parser class " + clazzName); + } + return propertyList; + } + + public static Collection getGeneratorPropertyValues(Class clazz, String propertyName) { + String clazzName = clazz.getName(); + ArrayList propertyList = new ArrayList(); + try { + WPSConfig mockConfig = MockUtil.getMockConfig(); + PropertyDocument.Property properties[] = + mockConfig.getPropertiesForGeneratorClass(clazzName); + for (Property property : properties) { + if (propertyName.equals(property.getName())) { + propertyList.add(property.getStringValue()); + } + } + } catch (Exception e) { + System.err.println("ERROR parsing property " + propertyName + " for Generator class " + clazzName); + } + propertyList.trimToSize(); + return propertyList; + } + +} diff --git a/src/test/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospectorTest.java b/src/test/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospectorTest.java new file mode 100644 index 0000000..2847c86 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/annotation/AnnotatedAlgorithmIntrospectorTest.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import org.hamcrest.CoreMatchers; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class AnnotatedAlgorithmIntrospectorTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + public AnnotatedAlgorithmIntrospector instance; + + @Test + public void testClassWithNoExecuteAnnotation() { + thrown.expect(RuntimeException.class); + thrown.expectMessage(CoreMatchers.containsString("No execute method binding")); + instance = new AnnotatedAlgorithmIntrospector(ClassWithNoExecuteAnnotation.class); + } +} diff --git a/src/test/java/org/n52/wps/algorithm/annotation/AnnotatedMemberDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/annotation/AnnotatedMemberDescriptorTest.java new file mode 100644 index 0000000..fa41484 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/annotation/AnnotatedMemberDescriptorTest.java @@ -0,0 +1,592 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import junit.framework.TestCase; +import org.n52.test.mock.MockEnum; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputFieldBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.InputMethodBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputFieldBinding; +import org.n52.wps.algorithm.annotation.AnnotationBinding.OutputMethodBinding; + +/** + * + * @author tkunicki + */ +public class AnnotatedMemberDescriptorTest extends TestCase { + + // START - TEST DATA AS CLASS FIELDS AND METHODS + + // represent almost all cases: literal or complex data, input or output. with expections noted below + public String stringField; + public List stringListField; + public List stringExtendListField; + public List stringSuperListField; + public List unboundListField; // effectively List + + // no polymorphism in test methods, will break use of methodMap + public void setString(String stringParameter) { + this.stringField = stringParameter; + } + public String getString() { + return stringField; + } + public void setStringList(List stringListParameter) { + this.stringListField = stringListParameter; + } + public List getStringList() { + return this.stringListField; + } + public void setStringExtendList(List stringExtendListParameter) { + this.stringExtendListField = stringExtendListParameter; + } + public List getStringExtendList() { + return this.stringExtendListField; + } + public void setStringSuperList(List stringSuperListParameter) { + this.stringSuperListField = stringSuperListParameter; + } + public List getStringSuperList() { + return this.stringSuperListField; + } + public void setUnboundList(List unboundListParameter) { + this.unboundListField = unboundListParameter; + } + public List getUnboundList() { + return this.unboundListField; + } + + // special case: enumerations for *inputs* have data type of String *unless* List for outputs + public MockEnum enumField; + public List enumListField; + /* NOT CURRENTLY SUPPORTED, need to be able to infer concrete type by reflection + public List enumExtendsListField; + public List enumSuperListField; + */ + + // no polymorphism in test methods, will break use of methodMap + public MockEnum getEnum() { + return enumField; + } + public void setEnum(MockEnum enumParameter) { + this.enumField = enumParameter; + } + public List getEnumList() { + return enumListField; + } + public void setEnumList(List enumListParameter) { + this.enumListField = enumListParameter; + } + /* NOT CURRENTLY SUPPORTED, need to be able to infer concrete type by reflection + public List getEnumExtendList() { + return enumExtendsListField; + } + public void setEnumExtendList(List enumExtendsListParameter) { + this.enumExtendsListField = enumExtendsListParameter; + } + public List getEnumSuperList() { + return enumSuperListField; + } + public void setEnumSuperList(List enumSuperListParameter) { + this.enumSuperListField = enumSuperListParameter; + } + */ + + // END - TEST DATA AS CLASS FIELDS AND METHODS + + Map methodMap; + + public AnnotatedMemberDescriptorTest(String testName) { + super(testName); + methodMap = new HashMap(); + for (Method method : AnnotationMemberDescriptorSample.class.getDeclaredMethods()) { + methodMap.put(method.getName(), method); + } + methodMap = Collections.unmodifiableMap(methodMap); + } + + private Method getSampleMethod(String name) throws NoSuchMethodException { + Method method = methodMap.get(name); + if (method == null) { + throw new NoSuchMethodException(name); + } + return method; + } + + private Field getSampleField(String name) throws NoSuchFieldException { + return AnnotationMemberDescriptorSample.class.getDeclaredField(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testStringFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("stringField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateInputMember(memberDescriptor); + } + + public void testStringFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("stringField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateOutputMember(memberDescriptor); + } + + public void testStringSetter() throws NoSuchMethodException { + Method method = getSampleMethod("setString"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateInputMember(memberDescriptor); + } + + public void testStringGetter() throws NoSuchMethodException { + Method method = getSampleMethod("getString"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateOutputMember(memberDescriptor); + } + + private void validateInputMember(InputBinding memberDescriptor) { + // data type matches member for inputs type *unless* member type is List + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // payload type matches data type, special handling reserved for enumerations + assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isMemberTypeList()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + private void validateOutputMember(OutputBinding memberDescriptor) { + // data type matches member type for outputs + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // data type matches payload type, special handling reserved for enumerations + assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + public void testStringListFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("stringListField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateInputListMember(memberDescriptor); + } + + public void testStringListFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("stringListField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateOutputListMember(memberDescriptor); + } + + public void testStringListSetter() throws NoSuchMethodException { + Method method = getSampleMethod("setStringList"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateInputListMember(memberDescriptor); + } + + public void testStringListGetter() throws NoSuchMethodException { + Method method = getSampleMethod("getStringList"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateOutputListMember(memberDescriptor); + } + + private void validateInputListMember(InputBinding memberDescriptor) { + // we extract the parameterized type of the list for inputs. since member + // type is List we expect String + assertEquals(String.class, memberDescriptor.getType()); + // payload type matches data type, special handling reserved for enumerations + assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); + assertEquals(true, memberDescriptor.isMemberTypeList()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + private void validateOutputListMember(OutputBinding memberDescriptor) { + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + public void testStringExtendListFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("stringExtendListField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateExtendListInputMember(memberDescriptor); + } + + public void testStringExtendListFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("stringExtendListField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateExtendListOutputMember(memberDescriptor); + } + + public void testStringExtendListSetter() throws NoSuchMethodException { + Method method = getSampleMethod("setStringExtendList"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateExtendListInputMember(memberDescriptor); + } + + public void testStringExtendListGetter() throws NoSuchMethodException { + Method method = getSampleMethod("getStringExtendList"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateExtendListOutputMember(memberDescriptor); + } + + private void validateExtendListInputMember(InputBinding memberDescriptor) { + // we extract the parameterized type of the list for inputs. since member + // type is List we expect a WildcardType of + // we need this information later to make sure we can safely assign an + // instance to the list with type safety (fail early behavior) + Type type = memberDescriptor.getType(); + assertTrue(type instanceof WildcardType); + WildcardType typeWildcard = (WildcardType)type; + assertEquals(0, typeWildcard.getLowerBounds().length); + assertEquals(1, typeWildcard.getUpperBounds().length); + assertEquals(String.class, typeWildcard.getUpperBounds()[0]); + // we extract the parameterized type of the list for inputs + assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); + assertEquals(true, memberDescriptor.isMemberTypeList()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + private void validateExtendListOutputMember(OutputBinding memberDescriptor) { + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + public void testStringSuperListFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("stringSuperListField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateSuperListInputMember(memberDescriptor); + } + + public void testStringSuperListFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("stringSuperListField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateSuperListOutputMember(memberDescriptor); + } + + public void testStringSuperListSetter() throws NoSuchMethodException { + Method method = getSampleMethod("setStringSuperList"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateSuperListInputMember(memberDescriptor); + } + + public void testStringSuperListGetter() throws NoSuchMethodException { + Method method = getSampleMethod("getStringSuperList"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateSuperListOutputMember(memberDescriptor); + } + + private void validateSuperListInputMember(InputBinding memberDescriptor) { + // we extract the parameterized type of the list for inputs. since member + // type is List we expect a WildcardType of + // we need this information later to make sure we can safely assign an + // instance to the list with type safety (fail early behavior) + Type type = memberDescriptor.getType(); + assertTrue(type instanceof WildcardType); + WildcardType typeWildcard = (WildcardType)type; + assertEquals(1, typeWildcard.getLowerBounds().length); + assertEquals(String.class, typeWildcard.getLowerBounds()[0]); + assertEquals(1, typeWildcard.getUpperBounds().length); + assertEquals(Object.class, typeWildcard.getUpperBounds()[0]); + // we extract the parameterized type of the list for inputs + assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); + assertEquals(true, memberDescriptor.isMemberTypeList()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + private void validateSuperListOutputMember(OutputBinding memberDescriptor) { + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + public void testUnboundListFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("unboundListField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateUnboundListInputMember(memberDescriptor); + } + + public void testUnboundListFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("unboundListField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateUnboundListOutputMember(memberDescriptor); + } + + public void testUnboundListSetter() throws NoSuchMethodException { + Method method = getSampleMethod("setUnboundList"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateUnboundListInputMember(memberDescriptor); + } + + public void testUnboundListGetter() throws NoSuchMethodException { + Method method = getSampleMethod("getUnboundList"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateUnboundListOutputMember(memberDescriptor); + } + + private void validateUnboundListInputMember(InputBinding memberDescriptor) { +// // we extract the parameterized type of the list for inputs. since member +// // type is List we expect a WildcardType of +// // we need this information later to make sure we can safely assign an +// // instance to the list with type safety (fail early behavior) +// Type type = memberDescriptor.getType(); +// assertTrue(type instanceof WildcardType); +// WildcardType typeWildcard = (WildcardType)type; +// assertEquals(0, typeWildcard.getLowerBounds().length); +// assertEquals(1, typeWildcard.getUpperBounds().length); +// assertEquals(Object.class, typeWildcard.getUpperBounds()[0]); +// // we extract the parameterized type of the list for inputs +// assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); +// assertEquals(true, memberDescriptor.isMemberTypeList()); +// assertEquals(false, memberDescriptor.isTypeEnum()); + // we extract the parameterized type of the list for inputs. since member + // type is List we expect a WildcardType of + // we need this information later to make sure we can safely assign an + // instance to the list with type safety (fail early behavior) + Type type = memberDescriptor.getType(); + assertTrue(type instanceof WildcardType); + WildcardType typeWildcard = (WildcardType)type; + assertEquals(0, typeWildcard.getLowerBounds().length); + assertEquals(1, typeWildcard.getUpperBounds().length); + assertEquals(Object.class, typeWildcard.getUpperBounds()[0]); + // we extract the parameterized type of the list for inputs + assertEquals(memberDescriptor.getType(), memberDescriptor.getPayloadType()); + assertEquals(true, memberDescriptor.isMemberTypeList()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + private void validateUnboundListOutputMember(OutputBinding memberDescriptor) { + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + + public void testEnumFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("enumField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateEnumInputMember(memberDescriptor); + } + + public void testEnumFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("enumField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateEnumOutputMember(memberDescriptor); + } + + public void testEnumSetter() throws NoSuchMethodException { + Method method = getSampleMethod("setEnum"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateEnumInputMember(memberDescriptor); + } + + public void testEnumGetter() throws NoSuchMethodException { + Method method = getSampleMethod("getEnum"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateEnumOutputMember(memberDescriptor); + } + + private void validateEnumInputMember(InputBinding memberDescriptor) { + assertEquals(MockEnum.class, memberDescriptor.getType()); + // for all instances of Class the payload type is Class + // as these will be bound with LiteralStringBinding + assertEquals(String.class, memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isMemberTypeList()); + assertEquals(true, memberDescriptor.isTypeEnum()); + } + + private void validateEnumOutputMember(OutputBinding memberDescriptor) { + assertEquals(MockEnum.class, memberDescriptor.getType()); + // for all instances of Class the payload type is Class + // as these will be bound with LiteralStringBinding + assertEquals(String.class, memberDescriptor.getPayloadType()); + assertEquals(true, memberDescriptor.isTypeEnum()); + } + + public void testEnumListFieldAsInput() throws NoSuchFieldException { + Field field = getSampleField("enumListField"); + InputFieldBinding memberDescriptor = new InputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateEnumListInputMember(memberDescriptor); + } + + public void testEnumListFieldAsOutput() throws NoSuchFieldException { + Field field = getSampleField("enumListField"); + OutputFieldBinding memberDescriptor = new OutputFieldBinding(field); + + assertEquals(field, memberDescriptor.getMember()); + assertEquals(field.getGenericType(), memberDescriptor.getMemberType()); + + validateEnumListOutputMember(memberDescriptor); + } + + public void testEnumListSetter() throws NoSuchFieldException, NoSuchMethodException { + Method method = getSampleMethod("setEnumList"); + InputMethodBinding memberDescriptor = new InputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericParameterTypes()[0], memberDescriptor.getMemberType()); + + validateEnumListInputMember(memberDescriptor); + } + + public void testEnumListGetter() throws NoSuchFieldException, NoSuchMethodException { + Method method = getSampleMethod("getEnumList"); + OutputMethodBinding memberDescriptor = new OutputMethodBinding(method); + + assertEquals(method, memberDescriptor.getMember()); + assertEquals(method.getGenericReturnType(), memberDescriptor.getMemberType()); + + validateEnumListOutputMember(memberDescriptor); + } + + + private void validateEnumListInputMember(InputBinding memberDescriptor) { + assertEquals(MockEnum.class, memberDescriptor.getType()); + assertEquals(String.class, memberDescriptor.getPayloadType()); + assertEquals(true, memberDescriptor.isMemberTypeList()); + assertEquals(true, memberDescriptor.isTypeEnum()); + } + + private void validateEnumListOutputMember(OutputBinding memberDescriptor) { + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getType()); + // no special handling for outputs of member type List, member type matches data type + assertEquals(memberDescriptor.getMemberType(), memberDescriptor.getPayloadType()); + assertEquals(false, memberDescriptor.isTypeEnum()); + } + +} diff --git a/src/test/java/org/n52/wps/algorithm/annotation/AnnotationMemberDescriptorSample.java b/src/test/java/org/n52/wps/algorithm/annotation/AnnotationMemberDescriptorSample.java new file mode 100644 index 0000000..511da98 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/annotation/AnnotationMemberDescriptorSample.java @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +import java.util.List; +import org.n52.test.mock.MockEnum; + +/** + * + * @author tkunicki + */ +public class AnnotationMemberDescriptorSample { + + // TEST IMPLEMENTATION NOTE: no polymorphism in sample methods, the reflection utilities + // used for generic method lookup used in the unit tests (polymorphism is ok in + // actual implementations as the annotations are what drive method lookups) + + // Represents almost all cases for fields: literal or complex data, input or output. with expections noted below. + // We are using String instances, but any literal or complex payload type could be swapped out. + public String stringField; + public List stringListField; + public List stringExtendListField; + public List stringSuperListField; + public List unboundListField; // effectively List + + public void setString(String stringParameter) { + this.stringField = stringParameter; + } + public String getString() { + return stringField; + } + public void setStringList(List stringListParameter) { + this.stringListField = stringListParameter; + } + public List getStringList() { + return this.stringListField; + } + public void setStringExtendList(List stringExtendListParameter) { + this.stringExtendListField = stringExtendListParameter; + } + public List getStringExtendList() { + return this.stringExtendListField; + } + public void setStringSuperList(List stringSuperListParameter) { + this.stringSuperListField = stringSuperListParameter; + } + public List getStringSuperList() { + return this.stringSuperListField; + } + public void setUnboundList(List unboundListParameter) { + this.unboundListField = unboundListParameter; + } + public List getUnboundList() { + return this.unboundListField; + } + + // Special case: enumerations for *inputs* have payload type of String so that + // Enumerations can be bound with LiteralStringBinding instances. Exception + // is List of enums for outputs. + public MockEnum enumField; + public List enumListField; +/* NOT CURRENTLY SUPPORTED, need to be able to infer concrete type by reflection + public List enumExtendsListField; + public List enumSuperListField; +*/ + public MockEnum getEnum() { + return enumField; + } + public void setEnum(MockEnum enumParameter) { + this.enumField = enumParameter; + } + public List getEnumList() { + return enumListField; + } + public void setEnumList(List enumListParameter) { + this.enumListField = enumListParameter; + } +/* NOT CURRENTLY SUPPORTED, need to be able to infer concrete type by reflection + public List getEnumExtendList() { + return enumExtendsListField; + } + public void setEnumExtendList(List enumExtendsListParameter) { + this.enumExtendsListField = enumExtendsListParameter; + } + public List getEnumSuperList() { + return enumSuperListField; + } + public void setEnumSuperList(List enumSuperListParameter) { + this.enumSuperListField = enumSuperListParameter; + } +*/ +} diff --git a/src/test/java/org/n52/wps/algorithm/annotation/ClassWithNoExecuteAnnotation.java b/src/test/java/org/n52/wps/algorithm/annotation/ClassWithNoExecuteAnnotation.java new file mode 100644 index 0000000..1b32972 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/annotation/ClassWithNoExecuteAnnotation.java @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.annotation; + +@Algorithm(version = "1.0.0") +public class ClassWithNoExecuteAnnotation { + + private String output; + + // We forgot to mark this method as @Execute + public void execute() { + + } + + @LiteralDataOutput(identifier = "output") + public String getOutput() { + return output; + } +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/AlgorithmDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/AlgorithmDescriptorTest.java new file mode 100644 index 0000000..c36c602 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/AlgorithmDescriptorTest.java @@ -0,0 +1,332 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import junit.framework.TestCase; +import org.n52.test.mock.MockBinding; + +/** + * + * @author tkunicki + */ +public class AlgorithmDescriptorTest extends TestCase { + + private LiteralDataOutputDescriptor.Builder MOCK_OUPUT1_BUILDER; + + private List> MOCK_INPUT_BUILDERS; + private List> MOCK_OUTPUT_BUILDERS; + + public AlgorithmDescriptorTest(String testName) { + super(testName); + } + + @Override + protected void setUp() { + MOCK_INPUT_BUILDERS = new ArrayList>(); + MOCK_INPUT_BUILDERS.add(LiteralDataInputDescriptor.booleanBuilder("mock_input1")); + MOCK_INPUT_BUILDERS.add(LiteralDataInputDescriptor.booleanBuilder("mock_input2")); + MOCK_INPUT_BUILDERS.add(ComplexDataInputDescriptor.builder("mock_input3", MockBinding.class)); + MOCK_INPUT_BUILDERS.add(ComplexDataInputDescriptor.builder("mock_input4", MockBinding.class)); + MOCK_INPUT_BUILDERS = Collections.unmodifiableList(MOCK_INPUT_BUILDERS); + + MOCK_OUPUT1_BUILDER = LiteralDataOutputDescriptor.booleanBuilder("mock_output1"); + + MOCK_OUTPUT_BUILDERS = new ArrayList>(); +// MOCK_OUTPUT_BUILDERS.add(LiteralDataOutputDescriptor.booleanBuilder("mock_output1")); + MOCK_OUTPUT_BUILDERS.add(LiteralDataOutputDescriptor.booleanBuilder("mock_output2")); + MOCK_OUTPUT_BUILDERS.add(ComplexDataOutputDescriptor.builder("mock_output3", MockBinding.class)); + MOCK_OUTPUT_BUILDERS.add(ComplexDataOutputDescriptor.builder("mock_output4", MockBinding.class)); + MOCK_OUTPUT_BUILDERS = Collections.unmodifiableList(MOCK_OUTPUT_BUILDERS); + + } + + public void testStaticBuilder_String() { + + AlgorithmDescriptor descriptor = null; + + // Test fail-early, exception should be thrown if identifier is 'null'; + boolean thrown = false; + try { + AlgorithmDescriptor.builder((String)null); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + // test that static builder with String parameter + descriptor = AlgorithmDescriptor.builder("mock_identifier"). + addOutputDescriptor(MOCK_OUPUT1_BUILDER). // require one output + build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertTrue(descriptor.hasTitle()); + assertEquals("mock_identifier", descriptor.getTitle()); + + } + + public void testStaticBuilder_Class() { + + AlgorithmDescriptor descriptor = null; + + // Test fail-early, exception should be thrown if idnetifier is 'null'; + boolean thrown = false; + try { + AlgorithmDescriptor.builder((Class)null); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + thrown = true; + } + assertTrue(thrown); + + // test that static builder with a valid class parameter + descriptor = AlgorithmDescriptor.builder(getClass()). + addOutputDescriptor(MOCK_OUPUT1_BUILDER). // require one output + build(); + assertEquals(getClass().getCanonicalName(), descriptor.getIdentifier()); + assertTrue(descriptor.hasTitle()); + assertEquals(getClass().getCanonicalName(), descriptor.getTitle()); + } + + + + public void testVersion() { + AlgorithmDescriptor descriptor = null; + + // test default is "1.0.0" + descriptor = createMinimumCompliantBuilder().build(); + assertEquals("1.0.0", descriptor.getVersion()); + + // test we can change + descriptor = createMinimumCompliantBuilder().version("X.Y.Z").build(); + assertEquals("X.Y.Z", descriptor.getVersion()); + } + + public void testStoreSupported() { + AlgorithmDescriptor descriptor = null; + + // test default is true + descriptor = createMinimumCompliantBuilder().build(); + assertTrue(descriptor.getStoreSupported()); + + // test we can set to true (explicitly) + descriptor = createMinimumCompliantBuilder().storeSupported(true).build(); + assertTrue(descriptor.getStoreSupported()); + + // test we can set to false + descriptor = createMinimumCompliantBuilder().storeSupported(false).build(); + assertFalse(descriptor.getStoreSupported()); + } + + public void testStatusSupported() { + AlgorithmDescriptor descriptor = null; + + // test default is true + descriptor = createMinimumCompliantBuilder().build(); + assertTrue(descriptor.getStatusSupported()); + + // test we can set to true (explicitly) + descriptor = createMinimumCompliantBuilder().statusSupported(true).build(); + assertTrue(descriptor.getStatusSupported()); + + // test we can set to false + descriptor = createMinimumCompliantBuilder().statusSupported(false).build(); + assertFalse(descriptor.getStatusSupported()); + } + + public void testInputDescriptorHandling() { + AlgorithmDescriptor descriptor = null; + + // test default is true + descriptor = createMinimumCompliantBuilder().build(); + assertNotNull(descriptor.getInputDescriptors()); + assertEquals(0, descriptor.getInputDescriptors().size()); + assertNotNull(descriptor.getInputIdentifiers()); + assertEquals(0, descriptor.getInputIdentifiers().size()); + + + // test addInputDescriptor(InputDescriptor.Builder) interface. + AlgorithmDescriptor.Builder builder = createMinimumCompliantBuilder(); + for (InputDescriptor.Builder inputBuilder : MOCK_INPUT_BUILDERS) { + builder.addInputDescriptor(inputBuilder); + } + validateInputDescriptors(builder.build()); + + // test addInputDescriptor(InputDescriptor) interface. + builder = createMinimumCompliantBuilder(); + for (InputDescriptor.Builder inputBuilder : MOCK_INPUT_BUILDERS) { + builder.addInputDescriptor(inputBuilder.build()); + } + validateInputDescriptors(builder.build()); + + // test addInputDescriptor(InputDescriptor) interface. + builder = createMinimumCompliantBuilder(); + List inputDescriptorList = new ArrayList(MOCK_INPUT_BUILDERS.size()); + for (InputDescriptor.Builder inputBuilder : MOCK_INPUT_BUILDERS) { + inputDescriptorList.add(inputBuilder.build()); + } + builder.addInputDescriptors(inputDescriptorList); + validateInputDescriptors(builder.build()); + } + + public void testOutputDescriptorHanding() { + AlgorithmDescriptor descriptor = null; + + // Test fail-early, exception should be thrown if idnetifier is 'null'; + boolean thrown = false; + try { + AlgorithmDescriptor.builder("mock_identifier").build(); + fail("Expected IllegalArgumentException"); + } catch (IllegalStateException e) { + thrown = true; + } + assertTrue(thrown); + + // test assumption that createMinimumCompliantBuilder() returns us 1 output + descriptor = createMinimumCompliantBuilder().build(); + assertNotNull(descriptor.getOutputDescriptors()); + assertEquals(1, descriptor.getOutputDescriptors().size()); + assertNotNull(descriptor.getOutputIdentifiers()); + assertEquals(1, descriptor.getOutputIdentifiers().size()); + + // test addOutputDescriptor(OutputDescriptor.Builder) interface. + AlgorithmDescriptor.Builder builder = createMinimumCompliantBuilder(); + for (OutputDescriptor.Builder outputBuilder : MOCK_OUTPUT_BUILDERS) { + builder.addOutputDescriptor(outputBuilder); + } + validateOutputDescriptors(builder.build()); + + // test addOutputDescriptor(OutputDescriptor) interface. + builder = createMinimumCompliantBuilder(); + for (OutputDescriptor.Builder outputBuilder : MOCK_OUTPUT_BUILDERS) { + builder.addOutputDescriptor(outputBuilder.build()); + } + validateOutputDescriptors(builder.build()); + + // test addOutputDescriptor(OutputDescriptor) interface. + builder = createMinimumCompliantBuilder(); + List outputDescriptorList = new ArrayList(MOCK_OUTPUT_BUILDERS.size()); + for (OutputDescriptor.Builder outputBuilder : MOCK_OUTPUT_BUILDERS) { + outputDescriptorList.add(outputBuilder.build()); + } + builder.addOutputDescriptors(outputDescriptorList); + validateOutputDescriptors(builder.build()); + } + + private AlgorithmDescriptor.Builder createMinimumCompliantBuilder() { + return AlgorithmDescriptor.builder("mock_identifier"). + addOutputDescriptor(MOCK_OUPUT1_BUILDER); + } + + private void validateInputDescriptors(AlgorithmDescriptor algorithmDescriptor) { + assertNotNull(algorithmDescriptor.getInputDescriptors()); + + // Test Collection getInputDescriptors() + Collection collection = algorithmDescriptor.getInputDescriptors(); + // correct size? + assertEquals(4, collection.size()); + // input order preserved? + Iterator iterator = collection.iterator(); + InputDescriptor inputDescriptor = iterator.next(); + assertNotNull(inputDescriptor); + assertEquals("mock_input1", inputDescriptor.getIdentifier()); + inputDescriptor = iterator.next(); + assertNotNull(inputDescriptor); + assertEquals("mock_input2", inputDescriptor.getIdentifier()); + inputDescriptor = iterator.next(); + assertNotNull(inputDescriptor); + assertEquals("mock_input3", inputDescriptor.getIdentifier()); + inputDescriptor = iterator.next(); + assertNotNull(inputDescriptor); + assertEquals("mock_input4", inputDescriptor.getIdentifier()); + assertFalse(iterator.hasNext()); + + // Test InputDescriptor getInputDescriptor(String) + // Can we access by indentifier? + assertNotNull(algorithmDescriptor.getInputDescriptor("mock_input1")); + assertNotNull(algorithmDescriptor.getInputDescriptor("mock_input2")); + assertNotNull(algorithmDescriptor.getInputDescriptor("mock_input3")); + assertNotNull(algorithmDescriptor.getInputDescriptor("mock_input4")); + // Are we getting the correct decriptors returned by identifier? + assertEquals(algorithmDescriptor.getInputDescriptor("mock_input1").getIdentifier(), "mock_input1"); + assertEquals(algorithmDescriptor.getInputDescriptor("mock_input2").getIdentifier(), "mock_input2"); + assertEquals(algorithmDescriptor.getInputDescriptor("mock_input3").getIdentifier(), "mock_input3"); + assertEquals(algorithmDescriptor.getInputDescriptor("mock_input4").getIdentifier(), "mock_input4"); + + // Test List getInputIdentifiers(); + List inputIdentifierList = algorithmDescriptor.getInputIdentifiers(); + // Size ok? + assertEquals(4, inputIdentifierList.size()); + // Order preserved? + assertEquals("mock_input1", inputIdentifierList.get(0)); + assertEquals("mock_input2", inputIdentifierList.get(1)); + assertEquals("mock_input3", inputIdentifierList.get(2)); + assertEquals("mock_input4", inputIdentifierList.get(3)); + } + + private void validateOutputDescriptors(AlgorithmDescriptor algorithmDescriptor) { + assertNotNull(algorithmDescriptor.getOutputDescriptors()); + + // Test Collection getOutputDescriptors() + Collection collection = algorithmDescriptor.getOutputDescriptors(); + // correct size? + assertEquals(4, collection.size()); + // output order preserved? + Iterator iterator = collection.iterator(); + OutputDescriptor outputDescriptor = iterator.next(); + assertNotNull(outputDescriptor); + assertEquals("mock_output1", outputDescriptor.getIdentifier()); + outputDescriptor = iterator.next(); + assertNotNull(outputDescriptor); + assertEquals("mock_output2", outputDescriptor.getIdentifier()); + outputDescriptor = iterator.next(); + assertNotNull(outputDescriptor); + assertEquals("mock_output3", outputDescriptor.getIdentifier()); + outputDescriptor = iterator.next(); + assertNotNull(outputDescriptor); + assertEquals("mock_output4", outputDescriptor.getIdentifier()); + assertFalse(iterator.hasNext()); + + // Test OutputDescriptor getOutputDescriptor(String) + // Can we access by indentifier? + assertNotNull(algorithmDescriptor.getOutputDescriptor("mock_output1")); + assertNotNull(algorithmDescriptor.getOutputDescriptor("mock_output2")); + assertNotNull(algorithmDescriptor.getOutputDescriptor("mock_output3")); + assertNotNull(algorithmDescriptor.getOutputDescriptor("mock_output4")); + // Are we getting the correct decriptors returned by identifier? + assertEquals(algorithmDescriptor.getOutputDescriptor("mock_output1").getIdentifier(), "mock_output1"); + assertEquals(algorithmDescriptor.getOutputDescriptor("mock_output2").getIdentifier(), "mock_output2"); + assertEquals(algorithmDescriptor.getOutputDescriptor("mock_output3").getIdentifier(), "mock_output3"); + assertEquals(algorithmDescriptor.getOutputDescriptor("mock_output4").getIdentifier(), "mock_output4"); + + // Test List getOutputIdentifiers(); + List outputIdentifierList = algorithmDescriptor.getOutputIdentifiers(); + // Size ok? + assertEquals(4, outputIdentifierList.size()); + // Order preserved? + assertEquals("mock_output1", outputIdentifierList.get(0)); + assertEquals("mock_output2", outputIdentifierList.get(1)); + assertEquals("mock_output3", outputIdentifierList.get(2)); + assertEquals("mock_output4", outputIdentifierList.get(3)); + } + + +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/BoundDataDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/BoundDataDescriptorTest.java new file mode 100644 index 0000000..0254bc6 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/BoundDataDescriptorTest.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import junit.framework.TestCase; + +/** + * + * @author tkunicki + */ +public class BoundDataDescriptorTest extends TestCase { + + public BoundDataDescriptorTest(String testName) { + super(testName); + } + + public void testBinding() { + + BoundDescriptor descriptor = null; + + // Test fail-early, exception should be thrown if binding is 'null'; + boolean thrown = false; + try { + descriptor = (new BoundDescriptorImpl.Builder(null)).build(); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + // make sure the class type we build with is the same as the one returned + // by the constucted object + descriptor = (new BoundDescriptorImpl.Builder(Double.class)).build(); + assertEquals(Double.class, descriptor.getBinding()); + + descriptor = (new BoundDescriptorImpl.Builder(MockNumber.class)).build(); + assertEquals(MockNumber.class, descriptor.getBinding()); + } + + public static class BoundDescriptorImpl extends BoundDescriptor> { + private BoundDescriptorImpl(Builder builder) { super(builder); } + public static class Builder extends BoundDescriptor.Builder> { + Builder(Class binding) { + super("mock_identifier", binding); + } + @Override protected Builder self() { return this; } + public BoundDescriptorImpl build() { return new BoundDescriptorImpl(this); } + } + } + + public static class MockNumber extends Number { + @Override public int intValue() { return Integer.MAX_VALUE; } + @Override public long longValue() { return Long.MAX_VALUE; } + @Override public float floatValue() { return Float.MAX_VALUE; } + @Override public double doubleValue() { return Double.MAX_VALUE; }; + } +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/ComplexDataInputDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/ComplexDataInputDescriptorTest.java new file mode 100644 index 0000000..7f70808 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/ComplexDataInputDescriptorTest.java @@ -0,0 +1,145 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import java.math.BigInteger; +import junit.framework.TestCase; +import org.n52.test.mock.MockBinding; + +/** + * + * @author tkunicki + */ +public class ComplexDataInputDescriptorTest extends TestCase { + + public ComplexDataInputDescriptorTest(String testName) { + super(testName); + } + + public void testMaximumMegabytes() { + ComplexDataInputDescriptor descriptor = null; + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class).build(); + assertNull(descriptor.getMaximumMegaBytes()); + assertFalse(descriptor.hasMaximumMegaBytes()); + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(0).build(); + assertNotNull(descriptor.getMaximumMegaBytes()); + assertEquals(BigInteger.valueOf(0), descriptor.getMaximumMegaBytes()); + assertFalse(descriptor.hasMaximumMegaBytes()); + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(BigInteger.valueOf(0)).build(); + assertNotNull(descriptor.getMaximumMegaBytes()); + assertEquals(BigInteger.valueOf(0), descriptor.getMaximumMegaBytes()); + assertFalse(descriptor.hasMaximumMegaBytes()); + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(1).build(); + assertNotNull(descriptor.getMaximumMegaBytes()); + assertEquals(BigInteger.valueOf(1), descriptor.getMaximumMegaBytes()); + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(BigInteger.valueOf(1)).build(); + assertNotNull(descriptor.getMaximumMegaBytes()); + assertEquals(BigInteger.valueOf(1), descriptor.getMaximumMegaBytes()); + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(Integer.MAX_VALUE).build(); + assertNotNull(descriptor.getMaximumMegaBytes()); + assertEquals(BigInteger.valueOf(Integer.MAX_VALUE), descriptor.getMaximumMegaBytes()); + + descriptor = ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(BigInteger.valueOf(Integer.MAX_VALUE)).build(); + assertNotNull(descriptor.getMaximumMegaBytes()); + assertEquals(BigInteger.valueOf(Integer.MAX_VALUE), descriptor.getMaximumMegaBytes()); + + boolean thrown = false; + try { + ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(-1); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(BigInteger.valueOf(-1)); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(Integer.MIN_VALUE); + + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class). + maximumMegaBytes(BigInteger.valueOf(Integer.MIN_VALUE)); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + } + + public void testStaticBuilder() { + ComplexDataInputDescriptor descriptor = + ComplexDataInputDescriptor.builder("mock_identifier", MockBinding.class).build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(MockBinding.class, descriptor.getBinding()); + + boolean thrown = false; + try { + ComplexDataInputDescriptor.builder(null, MockBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataInputDescriptor.builder("", MockBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataInputDescriptor.builder("mock_identifier", null); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + } + +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/ComplexDataOutputDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/ComplexDataOutputDescriptorTest.java new file mode 100644 index 0000000..ec74b7d --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/ComplexDataOutputDescriptorTest.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import junit.framework.TestCase; +import org.n52.test.mock.MockBinding; + +/** + * + * @author tkunicki + */ +public class ComplexDataOutputDescriptorTest extends TestCase { + + public ComplexDataOutputDescriptorTest(String testName) { + super(testName); + } + + public void testBuilder() { + ComplexDataOutputDescriptor descriptor = + ComplexDataOutputDescriptor.builder("mock_identifier", MockBinding.class).build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(MockBinding.class, descriptor.getBinding()); + + boolean thrown = false; + try { + ComplexDataOutputDescriptor.builder(null, MockBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataOutputDescriptor.builder("", MockBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + ComplexDataOutputDescriptor.builder("mock_identifier", null); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + } + +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/DescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/DescriptorTest.java new file mode 100644 index 0000000..919fe51 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/DescriptorTest.java @@ -0,0 +1,113 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import junit.framework.TestCase; + +/** + * + * @author tkunicki + */ +public class DescriptorTest extends TestCase { + + public DescriptorTest(String testName) { + super(testName); + } + + public void testIdentifier() { + + DescriptorImpl descriptor = null; + + boolean thrown = false; + try { + new DescriptorImpl.Builder(null); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + new DescriptorImpl.Builder(""); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + // set case: set to 'an_identifier' + descriptor = (new DescriptorImpl.Builder("mock_identifier")).build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + + } + + public void testTitle() { + + DescriptorImpl descriptor = null; + + // default case: title is not initialized and therefore is null; + descriptor = (new DescriptorImpl.Builder("mock_identifier")).build(); + assertNull(descriptor.getTitle()); + assertFalse(descriptor.hasTitle()); + + // unset annotation case: 'title' default/unset value for annotations + // is empty string as 'null' is not a valid annotation value; + descriptor = (new DescriptorImpl.Builder("mock_identifier")).title("").build(); + assertEquals("", descriptor.getTitle()); + assertFalse(descriptor.hasTitle()); + + // set case: set to 'an_title' + descriptor = (new DescriptorImpl.Builder("mock_identifier")).title("mock_title").build(); + assertEquals("mock_title", descriptor.getTitle()); + assertTrue(descriptor.hasTitle()); + } + + public void testAbstract() { + + DescriptorImpl descriptor = null; + + // default case: abstrakt is not initialized and therefore is null; + descriptor = (new DescriptorImpl.Builder("mock_identifier")).build(); + assertNull(descriptor.getAbstract()); + assertFalse(descriptor.hasAbstract()); + + // unset annotation case: 'abstrakt' default/unset value for annotations + // is empty string as 'null' is not a valid annotation value; + descriptor = (new DescriptorImpl.Builder("mock_identifier")).abstrakt("").build(); + assertEquals("", descriptor.getAbstract()); + assertFalse(descriptor.hasAbstract()); + + // set case: set to 'an_abstrakt' + descriptor = (new DescriptorImpl.Builder("mock_identifier")).abstrakt("an_abstract").build(); + assertEquals("an_abstract", descriptor.getAbstract()); + assertTrue(descriptor.hasAbstract()); + + } + + // Dummy implementation, Descriptor and Builder classes are abstract + // so we need to provide an concrete implementation to test. + public static class DescriptorImpl extends Descriptor { + private DescriptorImpl(Builder builder) { super(builder); } + public static class Builder extends Descriptor.Builder { + Builder(String identifier) { super(identifier); } + @Override protected Builder self() { return this; } + public DescriptorImpl build() { return new DescriptorImpl(this); } + } + + } +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/InputDataDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/InputDataDescriptorTest.java new file mode 100644 index 0000000..8c5e8c2 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/InputDataDescriptorTest.java @@ -0,0 +1,175 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import java.math.BigInteger; +import junit.framework.TestCase; +import org.n52.test.mock.MockEnum; +import org.n52.wps.io.data.IData; + +/** + * + * @author tkunicki + */ +public class InputDataDescriptorTest extends TestCase { + + public InputDataDescriptorTest(String testName) { + super(testName); + } + + public void testMinOccurs() { + InputDescriptor inputDescriptor = null; + + // test default minOccurs is 1 + inputDescriptor = (new InputDescriptorImpl.Builder()).build(); + assertEquals(BigInteger.valueOf(1), inputDescriptor.getMinOccurs()); + + // test default minOccurs is 1, that we set it again doesn't matter + inputDescriptor = (new InputDescriptorImpl.Builder()).minOccurs(1).build(); + assertEquals(BigInteger.valueOf(1), inputDescriptor.getMinOccurs()); + // the other API + inputDescriptor = (new InputDescriptorImpl.Builder()).minOccurs(BigInteger.valueOf(1)).build(); + assertEquals(BigInteger.valueOf(1), inputDescriptor.getMinOccurs()); + + // test that 0 is OK + inputDescriptor = (new InputDescriptorImpl.Builder()).minOccurs(0).build(); + assertEquals(BigInteger.valueOf(0), inputDescriptor.getMinOccurs()); + // the other API + inputDescriptor = (new InputDescriptorImpl.Builder()).minOccurs(BigInteger.valueOf(0)).build(); + assertEquals(BigInteger.valueOf(0), inputDescriptor.getMinOccurs()); + + // test fail early on < 0 + boolean thrown = false; + try { + (new InputDescriptorImpl.Builder()).minOccurs(-1); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + // The other API + thrown = false; + try { + (new InputDescriptorImpl.Builder()).minOccurs(BigInteger.valueOf(-1)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + // test that minOccurs can't be > maxOccurs + thrown = false; + try { + (new InputDescriptorImpl.Builder()).minOccurs(2).build(); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + thrown = true; + } + assertTrue(thrown); + // The other API + thrown = false; + try { + (new InputDescriptorImpl.Builder()).minOccurs(BigInteger.valueOf(2)).build(); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + thrown = true; + } + assertTrue(thrown); + } + + public void testMaxOccurs() { + + InputDescriptor inputDescriptor = null; + + // test default maxOccurs is 1 + inputDescriptor = (new InputDescriptorImpl.Builder()).build(); + assertEquals(BigInteger.valueOf(1), inputDescriptor.getMaxOccurs()); + + // test default maxOccurs is 1, that we set it again doesn't matter + inputDescriptor = (new InputDescriptorImpl.Builder()).maxOccurs(1).build(); + assertEquals(BigInteger.valueOf(1), inputDescriptor.getMaxOccurs()); + // the other API + inputDescriptor = (new InputDescriptorImpl.Builder()).maxOccurs(BigInteger.valueOf(1)).build(); + assertEquals(BigInteger.valueOf(1), inputDescriptor.getMaxOccurs()); + + // test that we can set maxOccurs value > 1 + inputDescriptor = (new InputDescriptorImpl.Builder()).maxOccurs(2).build(); + assertEquals(BigInteger.valueOf(2), inputDescriptor.getMaxOccurs()); + // the other API + inputDescriptor = (new InputDescriptorImpl.Builder()).maxOccurs(BigInteger.valueOf(2)).build(); + assertEquals(BigInteger.valueOf(2), inputDescriptor.getMaxOccurs()); + + // test that we set maxOccurs to number of enum constants + inputDescriptor = (new InputDescriptorImpl.Builder()).maxOccurs(MockEnum.class).build(); + assertEquals(BigInteger.valueOf(MockEnum.values().length), inputDescriptor.getMaxOccurs()); + + // test fail-early for maxOccurs < 1; + boolean thrown = false; + try { + (new InputDescriptorImpl.Builder()).maxOccurs(0); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + // the other API + thrown = false; + try { + (new InputDescriptorImpl.Builder()).maxOccurs(BigInteger.valueOf(0)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + // test maxOccurs can be < minOccurs even if both are non-default + thrown = false; + try { + (new InputDescriptorImpl.Builder()). + minOccurs(3). + maxOccurs(2). + build(); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + thrown = true; + } + assertTrue(thrown); + // the other API + thrown = false; + try { + (new InputDescriptorImpl.Builder()). + minOccurs(BigInteger.valueOf(3)). + maxOccurs(BigInteger.valueOf(2)). + build(); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + thrown = true; + } + assertTrue(thrown); + } + + public static class InputDescriptorImpl extends InputDescriptor> { + private InputDescriptorImpl(Builder builder) { super(builder); } + public static class Builder extends InputDescriptor.Builder> { + Builder() { + super("mock_identifier", IData.class); + } + @Override protected Builder self() { return this; } + @Override public InputDescriptorImpl build() { return new InputDescriptorImpl(this); } + } + } +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/LiteralDataInputDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/LiteralDataInputDescriptorTest.java new file mode 100644 index 0000000..de10b1e --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/LiteralDataInputDescriptorTest.java @@ -0,0 +1,242 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import java.util.List; +import junit.framework.TestCase; +import org.n52.wps.algorithm.util.ClassUtil; +import org.n52.wps.io.BasicXMLTypeFactory; +import org.n52.wps.io.data.binding.literal.LiteralAnyURIBinding; +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; + +/** + * + * @author tkunicki + */ +public class LiteralDataInputDescriptorTest extends TestCase { + + public final static String MOCK_UNALLOWED = "MOCK_UNALLOWED"; + + public enum MOCK_ALLOWED_VALUES { + MOCK_ALLOWED1, + MOCK_ALLOWED2, + MOCK_ALLOWED3, + } + + public LiteralDataInputDescriptorTest(String testName) { + super(testName); + } + + public void testDefaultValue() { + LiteralDataInputDescriptor descriptor = null; + + // test default for defaultValue + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class).build(); + assertNull(descriptor.getDefaultValue()); + assertFalse(descriptor.hasDefaultValue()); + + // test "" for defaultValue (unset annotation case) + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + defaultValue(""). + build(); + assertEquals("", descriptor.getDefaultValue()); + assertFalse(descriptor.hasDefaultValue()); + + // test with a valid defaultValue (unset annotation case) + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + defaultValue("mock_default"). + build(); + assertEquals("mock_default", descriptor.getDefaultValue()); + assertTrue(descriptor.hasDefaultValue()); + } + + public void testAllowedValues() { + LiteralDataInputDescriptor descriptor = null; + + // test default for allowedValues + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class).build(); + assertNotNull(descriptor.getAllowedValues()); + assertEquals(0, descriptor.getAllowedValues().size()); + assertFalse(descriptor.hasAllowedValues()); + + // test allowedValues(String[]) + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + allowedValues(ClassUtil.convertEnumToStringArray(MOCK_ALLOWED_VALUES.class)).build(); + validateAllowValues(descriptor); + + // test allowedValues(List) + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + allowedValues(ClassUtil.convertEnumToStringList(MOCK_ALLOWED_VALUES.class)).build(); + validateAllowValues(descriptor); + + // test allowedValues(Class) + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + allowedValues(MOCK_ALLOWED_VALUES.class).build(); + validateAllowValues(descriptor); + + // test allowedValues() + descriptor = LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + defaultValue(MOCK_ALLOWED_VALUES.MOCK_ALLOWED1.name()). + allowedValues(MOCK_ALLOWED_VALUES.class).build(); + validateAllowValues(descriptor); + + boolean thrown = false; + try { + LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class). + defaultValue(MOCK_UNALLOWED). + allowedValues(MOCK_ALLOWED_VALUES.class). + build(); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + thrown = true; + } + assertTrue(thrown); + } + + public void testBuilder() { + LiteralDataInputDescriptor descriptor = + LiteralDataInputDescriptor.builder("mock_identifier", LiteralStringBinding.class).build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralStringBinding.class, descriptor.getBinding()); + + boolean thrown = false; + try { + LiteralDataInputDescriptor.builder(null, LiteralStringBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + LiteralDataInputDescriptor.builder("", LiteralStringBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + LiteralDataInputDescriptor.builder("mock_identifier", null); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + } + + public void testAnyURIBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.anyURIBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralAnyURIBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.ANYURI_URI, descriptor.getDataType()); + } + + public void testBase64BinaryBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.anyURIBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralAnyURIBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.ANYURI_URI, descriptor.getDataType()); + } + + public void testBooleanBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.booleanBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralBooleanBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.BOOLEAN_URI, descriptor.getDataType()); + } + + public void testByteBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.byteBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralByteBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.BYTE_URI, descriptor.getDataType()); + } + + public void testDateTimeBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.dateTimeBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralDateTimeBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.DATETIME_URI, descriptor.getDataType()); + } + + public void testDoubleBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.doubleBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralDoubleBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.DOUBLE_URI, descriptor.getDataType()); + } + + public void testFloatBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.floatBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralFloatBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.FLOAT_URI, descriptor.getDataType()); + } + + public void testIntBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.intBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralIntBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.INT_URI, descriptor.getDataType()); + } + + public void testLongBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.longBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralLongBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.LONG_URI, descriptor.getDataType()); + } + + public void testShortBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.shortBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralShortBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.SHORT_URI, descriptor.getDataType()); + } + + public void testStringBuilder() { + LiteralDataInputDescriptor descriptor = LiteralDataInputDescriptor.stringBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralStringBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.STRING_URI, descriptor.getDataType()); + } + + private void validateAllowValues(LiteralDataInputDescriptor descriptor) { + assertTrue(descriptor.hasAllowedValues()); + + List allowedValueList = descriptor.getAllowedValues(); + + assertNotNull(allowedValueList); + assertEquals(MOCK_ALLOWED_VALUES.values().length, allowedValueList.size()); + for (int index = 0; index < allowedValueList.size(); ++index) { + assertNotNull(allowedValueList.get(index)); + assertEquals(MOCK_ALLOWED_VALUES.values()[index].name(), allowedValueList.get(index)); + } + } +} diff --git a/src/test/java/org/n52/wps/algorithm/descriptor/LiteralDataOutputDescriptorTest.java b/src/test/java/org/n52/wps/algorithm/descriptor/LiteralDataOutputDescriptorTest.java new file mode 100644 index 0000000..4b4a8f2 --- /dev/null +++ b/src/test/java/org/n52/wps/algorithm/descriptor/LiteralDataOutputDescriptorTest.java @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.algorithm.descriptor; + +import junit.framework.TestCase; +import org.n52.wps.io.BasicXMLTypeFactory; +import org.n52.wps.io.data.binding.literal.LiteralAnyURIBinding; +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; + +/** + * + * @author tkunicki + */ +public class LiteralDataOutputDescriptorTest extends TestCase { + + public LiteralDataOutputDescriptorTest(String testName) { + super(testName); + } + + public void testStaticBuilder() { + LiteralDataOutputDescriptor descriptor = + LiteralDataOutputDescriptor.builder("mock_identifier", LiteralStringBinding.class).build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralStringBinding.class, descriptor.getBinding()); + + boolean thrown = false; + try { + LiteralDataOutputDescriptor.builder(null, LiteralStringBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + LiteralDataOutputDescriptor.builder("", LiteralStringBinding.class); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + thrown = false; + try { + LiteralDataOutputDescriptor.builder("mock_identifier", null); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + thrown = true; + } + assertTrue(thrown); + + } + + public void testAnyURIBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.anyURIBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralAnyURIBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.ANYURI_URI, descriptor.getDataType()); + } + + public void testBase64BinaryBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.anyURIBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralAnyURIBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.ANYURI_URI, descriptor.getDataType()); + } + + public void testBooleanBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.booleanBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralBooleanBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.BOOLEAN_URI, descriptor.getDataType()); + } + + public void testByteBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.byteBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralByteBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.BYTE_URI, descriptor.getDataType()); + } + + public void testDateTimeBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.dateTimeBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralDateTimeBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.DATETIME_URI, descriptor.getDataType()); + } + + public void testDoubleBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.doubleBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralDoubleBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.DOUBLE_URI, descriptor.getDataType()); + } + + public void testFloatBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.floatBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralFloatBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.FLOAT_URI, descriptor.getDataType()); + } + + public void testIntBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.intBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralIntBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.INT_URI, descriptor.getDataType()); + } + + public void testLongBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.longBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralLongBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.LONG_URI, descriptor.getDataType()); + } + + public void testShortBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.shortBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralShortBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.SHORT_URI, descriptor.getDataType()); + } + + public void testStringBuilder() { + LiteralDataOutputDescriptor descriptor = LiteralDataOutputDescriptor.stringBuilder("mock_identifier").build(); + assertEquals("mock_identifier", descriptor.getIdentifier()); + assertEquals(LiteralStringBinding.class, descriptor.getBinding()); + assertEquals(BasicXMLTypeFactory.STRING_URI, descriptor.getDataType()); + } + +} diff --git a/src/test/java/org/n52/wps/server/AbstractSelfDescribingAlgorithmTest.java b/src/test/java/org/n52/wps/server/AbstractSelfDescribingAlgorithmTest.java new file mode 100644 index 0000000..1cc905b --- /dev/null +++ b/src/test/java/org/n52/wps/server/AbstractSelfDescribingAlgorithmTest.java @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.HashMap; +import org.n52.test.mock.MockUtil; +import junit.framework.TestCase; +import net.opengis.wps.x100.ProcessDescriptionType; +import org.apache.xmlbeans.XmlOptions; + +/** + * + * @author tkunicki + */ +public class AbstractSelfDescribingAlgorithmTest extends TestCase { + + public AbstractSelfDescribingAlgorithmTest(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + MockUtil.getMockConfig(); + } + + public void testComplexSelfDescribingAlgorithmUsingDescriptor() { + IAlgorithm algorithm = new ComplexSelfDescribingAlgorithmUsingDescriptor(); + printAlgorithmProcessDescription(algorithm); + } + + public void testComplexAnnotatedAlgorithm() { + IAlgorithm algorithm = new ComplexAnnotatedAlgorithm(); + printAlgorithmProcessDescription(algorithm); + } + + public void testStringReverseSelfDescribingAlgorithm() { + IAlgorithm algorithm = new StringReverseSelfDescribingAlgorithm(); + printAlgorithmProcessDescription(algorithm); + } + + public void testStringReverseAnnotatedAlgorithm() { + IAlgorithm algorithm = new StringReverseAnnotatedAlgorithm(); + printAlgorithmProcessDescription(algorithm); + } + + public void testStringJoinSelfDescribingAlgorithm() { + IAlgorithm algorithm = new StringJoinSelfDescribingAlgorithm(); + printAlgorithmProcessDescription(algorithm); + } + + public void testStringJoinAnnotatedAlgorithm() { + IAlgorithm algorithm = new StringJoinAnnotatedAlgorithm(); + printAlgorithmProcessDescription(algorithm); + } + + private void printAlgorithmProcessDescription(IAlgorithm algorithm) { + System.out.println(); + System.out.println(" ### DescribeProcess for " + algorithm.getClass().getName() + " ###"); + System.out.println(getXMLAsStringFromDescription(algorithm.getDescription())); + System.out.println(); + } + + private String getXMLAsStringFromDescription(ProcessDescriptionType decription) { + XmlOptions options = new XmlOptions(); + options.setSavePrettyPrint(); + options.setSaveOuter(); + HashMap ns = new HashMap(); + ns.put("http://www.opengis.net/wps/1.0.0", "wps"); + ns.put("http://www.opengis.net/ows/1.1", "ows"); + options.setSaveNamespacesFirst(). + setSaveSuggestedPrefixes(ns). + setSaveAggressiveNamespaces(); + return decription.xmlText(options); + } + +} diff --git a/src/test/java/org/n52/wps/server/ComplexAnnotatedAlgorithm.java b/src/test/java/org/n52/wps/server/ComplexAnnotatedAlgorithm.java new file mode 100644 index 0000000..dbf83aa --- /dev/null +++ b/src/test/java/org/n52/wps/server/ComplexAnnotatedAlgorithm.java @@ -0,0 +1,236 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.net.URI; +import java.util.Date; +import java.util.List; +import org.n52.test.mock.MockBinding; +import org.n52.test.mock.MockEnum; +import org.n52.test.mock.MockComplexObject; +import org.n52.wps.algorithm.annotation.Algorithm; +import org.n52.wps.algorithm.annotation.ComplexDataInput; +import org.n52.wps.algorithm.annotation.ComplexDataOutput; +import org.n52.wps.algorithm.annotation.LiteralDataInput; +import org.n52.wps.algorithm.annotation.LiteralDataOutput; +import org.n52.wps.algorithm.annotation.Execute; +import org.n52.wps.io.data.binding.literal.LiteralByteBinding; +import org.n52.wps.io.data.binding.literal.LiteralShortBinding; + +/** + * + * @author tkunicki + */ +@Algorithm( +// identifier="SampleIdentifier" fully qualified class name is used when this is missing + version="0.0.1", // default is "1.0.0" + title="Sample Algorithm Title", // identifier is used if title is not set + abstrakt="Sample Algortihm Abstract", // default is null (not output) + storeSupported=false, // default is true + statusSupported=true) // default is true +public class ComplexAnnotatedAlgorithm extends AbstractAnnotatedAlgorithm { + + + // EXAMPLES FOR OUTPUTS + + @LiteralDataInput( + // binding=LiteralDoubleBinding.class, not needed, can infer type from field type + identifier=Constants.LITERAL_DOUBLE, + title="Sample Input Title", + abstrakt="Sample Input Abstract", + minOccurs=0, + maxOccurs=2) + public List inputLiteralDouble; + // public List inputLiteralDouble; OK, but needs explicit binding + // public List inputLiteralDouble; OK, but needs explicit binding + // public List inputLiteralDouble; OK, but needs explicit binding + // public List inputLiteralDouble; OK, but needs explicit binding + // public List inputLiteralDouble; FAIL, [dD]ouble not superclass of Number + // public [dDouble] inputLiteralDouble; FAIL, maxOccurs is 2, need List! + // + // not an exhaustive list, point is we can autobind literal types. For types + // where autobinding can't be inferred one can explicitly set binding but parser + // will check types to validate assignability between binding payload and field or + // method argument. Methods or fields must be public! + + // primitive field + @LiteralDataInput(identifier=Constants.LITERAL_FLOAT) + public float inputLiteralFloat; + + + private Long inputLiteralLong; + // Primitive wrapper as method argument + @LiteralDataInput(identifier=Constants.LITERAL_LONG) + public void setInputLiteralLong(Long inputLiteralLong) { + this.inputLiteralLong = inputLiteralLong; + } + + + private int inputLiteralInt; + // Primitive as method argument + @LiteralDataInput(identifier=Constants.LITERAL_INT) + public void setInputLiteralLong(int inputLiteralInt) { + this.inputLiteralInt = inputLiteralInt; + } + + private Number inputLiteralShort; + // Number as method argument type + @LiteralDataInput( + binding=LiteralShortBinding.class, // REQUIRED since literal type can't be inferred from method parameter type + identifier=Constants.LITERAL_SHORT) + public void setInputLiteralLong(Number inputLiteralShort) { + this.inputLiteralShort = inputLiteralShort; + } + + + // Object as field type + @LiteralDataInput( + binding=LiteralByteBinding.class, // REQUIRED since literal type can't be inferred from field type + identifier=Constants.LITERAL_BYTE) + public Object inputLiteralByte; + + + private boolean inputeLiteralBoolean; + // don't care what method name is, only check it's annotation... + @LiteralDataInput(identifier=Constants.LITERAL_BOOLEAN) + public void setWithSomeRandomName(boolean inputeLiteralBoolean) { + this.inputeLiteralBoolean = inputeLiteralBoolean; + } + + private String inputLiteralString; + @LiteralDataInput( + identifier=Constants.LITERAL_STRING, + defaultValue="Some Default Value", // annotation parser will validate this is allowedValues if set + allowedValues= { "Some Default Value", "Not the default Value" } ) + public void setInputLiteralString(String inputLiteralString) { + this.inputLiteralString = inputLiteralString; + } + + private MockEnum inputLiteralEnum; + @LiteralDataInput( +// binding=LiteralStringBinding.class Not needed! Enums are auto-bound to strings! + identifier=Constants.LITERAL_ENUM, +// defaultValue=MockEnum.VALUE1, Argh, Can't do this! +// defaultValue=MockEnum.VALUE1.name(), Argh, Can't do this either!, only literals or constants! So we settle for String constants + defaultValue="VALUE1", // must be string but annotation parser will validate this is valid constant for MockEnum +// allowedValues= { ... } Already set to Enum constants for MockEnum! + maxOccurs=LiteralDataInput.ENUM_COUNT // special case, set maxOccurs to number of MockEnum constants + ) + public void setInputEnumType(MockEnum inputLiteralEnum) { + this.inputLiteralEnum = inputLiteralEnum; + } + + // then the rest of the inputs... + private Date inputLiteralDateTime; + private byte[] inputLiteralBase64Binary; + private URI inputLiteralAnyURI; + private MockComplexObject inputComplex; + + @LiteralDataInput(identifier=Constants.LITERAL_DATETIME) + public void setInputLiteralDateTime(Date inputLiteralDateTime) { + this.inputLiteralDateTime = inputLiteralDateTime; + } + + @LiteralDataInput(identifier=Constants.LITERAL_BASE64BINARY) + public void setInputLiteralDateTime(byte[] inputLiteralBase64Binary) { + this.inputLiteralBase64Binary = inputLiteralBase64Binary; + } + + @LiteralDataInput(identifier=Constants.LITERAL_ANYURI) + public void setInputLiteralDateTime(URI inputLiteralAnyURI) { + this.inputLiteralAnyURI = inputLiteralAnyURI; + } + + @ComplexDataInput( + identifier=Constants.COMPLEX, + binding=MockBinding.class, // Binding required for complex types! + maximumMegaBytes=16) + public void setInputComplex(MockComplexObject inputComplex) { + this.inputComplex = inputComplex; + } + + + // EXAMPLES FOR OUTPUTS + @LiteralDataOutput( + identifier=Constants.LITERAL_DOUBLE, + title="Sample Output Title", // identifier is used if title is not set + abstrakt="Sample Output Abstract") // defaults to null (not output) + public Double outputLiteralDouble; + + @LiteralDataOutput(identifier=Constants.LITERAL_FLOAT) + public float outputLiteralFloat; + + + private Long outputLiteralLong; + @LiteralDataOutput(identifier=Constants.LITERAL_LONG) + public Long getOutputLiteralLong() { return outputLiteralLong; } + + private int outputLiteralInt; + @LiteralDataOutput(identifier=Constants.LITERAL_INT) + public int getOutputLiteralInt() { return outputLiteralInt; } + + + @LiteralDataOutput( + binding=LiteralShortBinding.class, // REQUIRED, can't infer binding for type Number + identifier=Constants.LITERAL_SHORT) + public Number outputLiteralShort; + + // and the rest... + + private byte outputLiteralByte; + private boolean outputLiteralBoolean; + private String outputLiteralString; + private Date outputLiteralDateTime; + private byte[] outputLiteralBase64Binary; + private URI outputLiteralAnyURI; + private MockComplexObject outputComplex; + + @LiteralDataOutput(identifier=Constants.LITERAL_BYTE) + public byte getOutputLiteralByte() { return outputLiteralByte; } + + @LiteralDataOutput(identifier=Constants.LITERAL_BOOLEAN) + public boolean getOutputLiteralBoolean() { return outputLiteralBoolean; } + + @LiteralDataOutput(identifier=Constants.LITERAL_STRING) + public String getOutputLiteralString() { return outputLiteralString; } + + @LiteralDataOutput(identifier=Constants.LITERAL_DATETIME) + public Date getOutputLiteralDateTime() { return outputLiteralDateTime; } + + @LiteralDataOutput(identifier=Constants.LITERAL_BASE64BINARY) + public byte[] getOutputLiteralBase64Binary() { return outputLiteralBase64Binary; } + + @LiteralDataOutput(identifier=Constants.LITERAL_ANYURI) + public URI getOutputLiteralAnyURI() { return outputLiteralAnyURI; } + + @ComplexDataOutput( + binding=MockBinding.class, // Binding required for complex types! + identifier=Constants.COMPLEX) + public MockComplexObject getComplex() { return outputComplex; } + + + @Execute + public void doStuff() { + + // values already unbound by setting fields or calling methods + + // just access your variables directly... + + // output fields or methods will be called and bound after this method call returns + } + +} diff --git a/src/test/java/org/n52/wps/server/ComplexSelfDescribingAlgorithmUsingDescriptor.java b/src/test/java/org/n52/wps/server/ComplexSelfDescribingAlgorithmUsingDescriptor.java new file mode 100644 index 0000000..6ea6004 --- /dev/null +++ b/src/test/java/org/n52/wps/server/ComplexSelfDescribingAlgorithmUsingDescriptor.java @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import org.n52.test.mock.MockBinding; +import java.util.List; +import java.util.Map; +import org.n52.test.mock.MockEnum; +import org.n52.wps.io.data.IData; +import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor; +import org.n52.wps.algorithm.descriptor.ComplexDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.ComplexDataOutputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor; + +/** + * + * @author tkunicki + */ +public class ComplexSelfDescribingAlgorithmUsingDescriptor extends AbstractDescriptorAlgorithm { + + // NOTE: AbstractSelfDescribingAlgorithm.initializeDescription(), is called + // in the constructor of AbstractAlgorithm. This creates a situation where + // initializeDescription() and getAlgorithmDescriptor() can be called before + // the implementing classes have their constructors called... + + // The descriptors do sanity checking and will throw Exceptions with illegal + // arguments or state (i.e. null identifier or maxOccurs < minOccurs). If + // you decide to instantiate the DESCRIPTOR using a static constructor be + // sure to wrap it in a try/catch with logging otherwise class load will fail, + // this is a difficult state to debug + + + private static AlgorithmDescriptor DESCRIPTOR; + protected synchronized static AlgorithmDescriptor getAlgorithmDescriptorStatic() { + if (DESCRIPTOR == null) { + DESCRIPTOR = + // Adding a lot of fields in order to provide example of AlgorithmDescriptor + // chaining with the different literal and complex types. + // most process descriptions would be much simpler... + // Show use of chaining enabled by use of Builder pattern... + AlgorithmDescriptor.builder(ComplexSelfDescribingAlgorithmUsingDescriptor.class). + version("0.0.1"). // default is "1.0.0" + title("Sample Algorithm Title"). // identifier is used if title is not set + abstrakt("Sample Algortihm Abstract"). // default is null (not output) + statusSupported(false). // default is true + storeSupported(false). // default is true + addInputDescriptor( + LiteralDataInputDescriptor.doubleBuilder(Constants.LITERAL_DOUBLE). + title("Sample Input Title"). // identifier is used if title is not set + abstrakt("Sample Input Abstract").// defaults to null (not output) + minOccurs(0). // defaults to 1 + maxOccurs(2)). // defaults to 1 + addInputDescriptor( + LiteralDataInputDescriptor.floatBuilder(Constants.LITERAL_FLOAT)). + addInputDescriptor( + LiteralDataInputDescriptor.longBuilder(Constants.LITERAL_LONG)). + addInputDescriptor( + LiteralDataInputDescriptor.intBuilder(Constants.LITERAL_INT)). + addInputDescriptor( + LiteralDataInputDescriptor.shortBuilder(Constants.LITERAL_SHORT)). + addInputDescriptor( + LiteralDataInputDescriptor.byteBuilder(Constants.LITERAL_BYTE)). + addInputDescriptor( + LiteralDataInputDescriptor.booleanBuilder(Constants.LITERAL_BOOLEAN)). + addInputDescriptor( + LiteralDataInputDescriptor.stringBuilder(Constants.LITERAL_STRING). + defaultValue("Some Default Value"). // can set a default value. + allowedValues(new String[] {"Some Default Value", "Not the default Value"})). // can set allowed values. + addInputDescriptor( + LiteralDataInputDescriptor.stringBuilder(Constants.LITERAL_ENUM). + defaultValue(MockEnum.VALUE1.name()).// you can pass in an enum to set allowed values + allowedValues(MockEnum.class). // you can pass in an enum to set allowed values + maxOccurs(MockEnum.class)). // you can pass in an enum to set maxOccurs + addInputDescriptor( + LiteralDataInputDescriptor.dateTimeBuilder(Constants.LITERAL_DATETIME)). + addInputDescriptor( + LiteralDataInputDescriptor.base64BinaryBuilder(Constants.LITERAL_BASE64BINARY)). + addInputDescriptor( + LiteralDataInputDescriptor.anyURIBuilder(Constants.LITERAL_ANYURI)). + addInputDescriptor( + ComplexDataInputDescriptor.builder(Constants.COMPLEX, MockBinding.class). + maximumMegaBytes(16)). // can set maximumMegaBytes for ComplexInput types + addOutputDescriptor( + LiteralDataOutputDescriptor.doubleBuilder(Constants.LITERAL_DOUBLE). + title("Sample Output Title"). // identifier is used if title is not set + abstrakt("Sample Output Abstract")). // defaults to null (not output) + addOutputDescriptor( + LiteralDataOutputDescriptor.floatBuilder(Constants.LITERAL_FLOAT)). + addOutputDescriptor( + LiteralDataOutputDescriptor.longBuilder(Constants.LITERAL_LONG)). + addOutputDescriptor( + LiteralDataOutputDescriptor.intBuilder(Constants.LITERAL_INT)). + addOutputDescriptor( + LiteralDataOutputDescriptor.shortBuilder(Constants.LITERAL_SHORT)). + addOutputDescriptor( + LiteralDataOutputDescriptor.byteBuilder(Constants.LITERAL_BYTE)). + addOutputDescriptor( + LiteralDataOutputDescriptor.booleanBuilder(Constants.LITERAL_BOOLEAN)). + addOutputDescriptor( + LiteralDataOutputDescriptor.stringBuilder(Constants.LITERAL_STRING)). + addOutputDescriptor( + LiteralDataOutputDescriptor.dateTimeBuilder(Constants.LITERAL_DATETIME)). + addOutputDescriptor( + LiteralDataOutputDescriptor.base64BinaryBuilder(Constants.LITERAL_BASE64BINARY)). + addOutputDescriptor( + LiteralDataOutputDescriptor.anyURIBuilder(Constants.LITERAL_ANYURI)). + addOutputDescriptor( + ComplexDataOutputDescriptor.builder(Constants.COMPLEX, MockBinding.class)). + build(); + } + return DESCRIPTOR; + } + + // Ideally this would be an abstract method in AbstractSelfDescribingAlgorithm, + // this would break backwards compatibility with the Old API as it would + // force migration to the new AlgorithmDescriptor based API. + @Override + public AlgorithmDescriptor createAlgorithmDescriptor() { + // read note in static constructor... + return getAlgorithmDescriptorStatic(); + } + + @Override + public Map run(Map> inputData) { + // unbind and do stuff... + return null; + } + +} diff --git a/src/test/java/org/n52/wps/server/Constants.java b/src/test/java/org/n52/wps/server/Constants.java new file mode 100644 index 0000000..7d18d63 --- /dev/null +++ b/src/test/java/org/n52/wps/server/Constants.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +/** + * + * @author tkunicki + */ +public class Constants { + + public final static String LITERAL_DOUBLE = "LITERAL_DOUBLE"; + public final static String LITERAL_FLOAT = "LITERAL_FLOAT"; + public final static String LITERAL_LONG = "LITERAL_LONG"; + public final static String LITERAL_INT = "LITERAL_INT"; + public final static String LITERAL_SHORT = "LITERAL_SHORT"; + public final static String LITERAL_BYTE = "LITERAL_BYTE"; + public final static String LITERAL_BOOLEAN = "LITERAL_BOOLEAN"; + public final static String LITERAL_STRING = "LITERAL_STRING"; + public final static String LITERAL_DATETIME = "LITERAL_DATETIME"; + public final static String LITERAL_BASE64BINARY = "LITERAL_BASE64BINARY"; + public final static String LITERAL_ANYURI = "LITERAL_ANYURI"; + public final static String LITERAL_ENUM = "LITERAL_ENUM"; + public final static String COMPLEX = "COMPLEX"; + + + +} diff --git a/src/test/java/org/n52/wps/server/ServiceLoaderAlgorithmTest.java b/src/test/java/org/n52/wps/server/ServiceLoaderAlgorithmTest.java new file mode 100644 index 0000000..ed82e0c --- /dev/null +++ b/src/test/java/org/n52/wps/server/ServiceLoaderAlgorithmTest.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import net.opengis.wps.x100.ProcessDescriptionType; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.n52.wps.algorithm.annotation.Algorithm; +import org.n52.wps.algorithm.annotation.Execute; +import org.n52.wps.algorithm.annotation.LiteralDataInput; +import org.n52.wps.algorithm.annotation.LiteralDataOutput; + +import static org.hamcrest.CoreMatchers.*; + +public class ServiceLoaderAlgorithmTest { + + private ServiceLoaderAlgorithmRepository repo; + + @Before + public void init() { + this.repo = new ServiceLoaderAlgorithmRepository(); + Assert.assertNotNull(this.repo); + } + + @Test + public void shouldFindAlgorithms() { + String identified = "dummy-test-identifier"; + Assert.assertThat(repo.containsAlgorithm(identified), is(true)); + Assert.assertThat(repo.getAlgorithm(identified), + is(notNullValue())); + Assert.assertThat(repo.getAlgorithm(DummyAnnotatedAlgorithm.class + .getCanonicalName()), is(notNullValue())); + } + + @Test + public void shouldNotFindAlgorithm() { + String identified = "not-in-there"; + Assert.assertThat(repo.containsAlgorithm(identified), is(not(true))); + Assert.assertThat(repo.getAlgorithm(identified), is(nullValue())); + } + + @Test + public void shouldFindTwoRegisteredAlgorithms() { + Assert.assertThat(this.repo.getAlgorithmNames().size(), is(2)); + } + + @Test + public void shouldResolveProcessDescription() { + ProcessDescriptionType description = repo.getProcessDescription(DummyAnnotatedAlgorithm.class.getCanonicalName()); + Assert.assertThat(description, is(notNullValue())); + Assert.assertThat(description, is(instanceOf(ProcessDescriptionType.class))); + } + + @Algorithm(version = "0.1") + public static class DummyAnnotatedAlgorithm extends + AbstractAnnotatedAlgorithm { + + private String output; + + @LiteralDataInput(identifier = "input") + public String input; + + @LiteralDataOutput(identifier = "output") + public String getOutput() { + return this.output; + } + + @Execute + public void myRunMethodFollowingNoSyntaxNoArgumentsAllowed() { + this.output = "works like a charm."; + } + + } + + @Algorithm(version = "0.1", identifier = "dummy-test-identifier") + public static class DummyIdentifiedAnnotatedAlgorithm extends + DummyAnnotatedAlgorithm { + + private String output; + + @LiteralDataInput(identifier = "input") + public String input; + + @LiteralDataOutput(identifier = "output") + public String getOutput() { + return this.output; + } + + @Execute + public void myRunMethodFollowingNoSyntaxNoArgumentsAllowed() { + this.output = "works like a charm."; + } + + } +} diff --git a/src/test/java/org/n52/wps/server/StringJoinAnnotatedAlgorithm.java b/src/test/java/org/n52/wps/server/StringJoinAnnotatedAlgorithm.java new file mode 100644 index 0000000..65bb35d --- /dev/null +++ b/src/test/java/org/n52/wps/server/StringJoinAnnotatedAlgorithm.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import com.google.common.base.Joiner; +import java.util.List; +import org.n52.wps.algorithm.annotation.Algorithm; +import org.n52.wps.algorithm.annotation.LiteralDataInput; +import org.n52.wps.algorithm.annotation.LiteralDataOutput; +import org.n52.wps.algorithm.annotation.Execute; + +/** + * + * @author tkunicki + */ +@Algorithm( + version="0.0.1", + title="String Join Algorithm (Annotated)", + abstrakt="This is an example algorithm implementation described using annotations that joins strings using the specified delimiter.", + statusSupported=false, + storeSupported=false) +public class StringJoinAnnotatedAlgorithm extends AbstractAnnotatedAlgorithm { + + public enum Delimiter { + SPACE(' '), + TAB('\t'), + PIPE('|'), + COMMA(','), + SEMI_COLON(';'), + COLON(':'); + public final char value; + Delimiter(char value) { + this.value = value; + } + } + + private List inputStrings; + private Delimiter inputDelimiter; + private String outputString; + + @LiteralDataInput( + identifier="INPUT_STRINGS", + title="Input Strings", + abstrakt="The strings you want joined.", + minOccurs=2, + maxOccurs=32) + public void setInputString(List inputStrings) { + this.inputStrings = inputStrings; + } + + @LiteralDataInput( + identifier="INPUT_DELIMITER", + title="Delimiter", + abstrakt="The value to use when joining strings") + public void setInputDelimiter(Delimiter inputDelimiter) { + this.inputDelimiter = inputDelimiter; + } + + + @LiteralDataOutput( + identifier="OUTPUT_STRING", + title="Output String", + abstrakt="The strings joined with the delimiter") + public String getOutputString() { + return outputString; + } + + @LiteralDataInput(identifier="yourMom") + public String yourMom; + + @Execute + public void reverse() { + // don't need to do any parameter bounds checking that is specified + // as part of the DescribeProcess, it's already done before this + // method is called... + outputString = Joiner.on(inputDelimiter.value).join(inputStrings); + } + +} diff --git a/src/test/java/org/n52/wps/server/StringJoinSelfDescribingAlgorithm.java b/src/test/java/org/n52/wps/server/StringJoinSelfDescribingAlgorithm.java new file mode 100644 index 0000000..4c78754 --- /dev/null +++ b/src/test/java/org/n52/wps/server/StringJoinSelfDescribingAlgorithm.java @@ -0,0 +1,143 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import com.google.common.base.Joiner; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.n52.wps.io.data.IData; +import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor; +import org.n52.wps.io.data.binding.literal.LiteralStringBinding; + +/** + * + * @author tkunicki + */ +public class StringJoinSelfDescribingAlgorithm extends AbstractDescriptorAlgorithm { + + public final static String INPUT_STRINGS = "INPUT_STRINGS"; + public final static String INPUT_DELIMITER = "INPUT_DELIMITER"; + public final static String OUTPUT_STRING = "OUTPUT_STRING"; + + public enum Delimiter { + SPACE(' '), + TAB('\t'), + PIPE('|'), + COMMA(','), + SEMI_COLON(';'), + COLON(':'); + public final char value; + Delimiter(char value) { + this.value = value; + } + } + + private static AlgorithmDescriptor DESCRIPTOR; + protected synchronized static AlgorithmDescriptor getAlgorithmDescriptorStatic() { + if (DESCRIPTOR == null) { + DESCRIPTOR = + // passing in a class to the AlgorithmDescriptor.builder will set + // set the identity to the the fully qualified class name. If this + // is not desired use the String constructor. + AlgorithmDescriptor.builder(StringJoinSelfDescribingAlgorithm.class). + version("0.0.1"). // default is "1.0.0" + title("String Join Algorithm (Self Describing)"). // identifier is used if title is not set + abstrakt("This is an example algorithm implementation described using a chained builder that joins strings using the specified delimiter."). // default is null (not output) + statusSupported(false). // default is true + storeSupported(false). // default is true + addInputDescriptor( + LiteralDataInputDescriptor.stringBuilder(INPUT_STRINGS). + title("Input Strings"). // identifier is used if title is not set + abstrakt("The strings you want joined.").// defaults to null (not output) + minOccurs(2). // defaults to 1 + maxOccurs(32)). + addInputDescriptor( + LiteralDataInputDescriptor.stringBuilder(INPUT_DELIMITER). + title("Delimiter"). // identifier is used if title is not set + abstrakt("The value to use when joining strings"). // defaults to null (not output) + allowedValues(Delimiter.class)). + addOutputDescriptor( + LiteralDataOutputDescriptor.stringBuilder(OUTPUT_STRING). + title("Output String"). // identifier is used if title is not set + abstrakt("The strings joined with the delimiter")). // defaults to null (not output). + build(); + } + return DESCRIPTOR; + } + + @Override + public AlgorithmDescriptor createAlgorithmDescriptor() { + return getAlgorithmDescriptorStatic(); + } + + @Override + public Map run(Map> inputMap) { + // unwrap input strings and error check + List inputBoundStringList = inputMap.get(INPUT_STRINGS); + if (inputBoundStringList == null || inputBoundStringList.size() < 2) { + addError("Invalid parameter count for" + INPUT_STRINGS); + return null; + } + List inputStringList = new ArrayList(); + for (IData boundString : inputBoundStringList) { + if (boundString == null || !(boundString instanceof LiteralStringBinding)) { + addError("unexpected binding ecountered unbinding " + INPUT_STRINGS + " parameter list"); + return null; + } + String inputString = ((LiteralStringBinding)boundString).getPayload(); + if (inputString == null || inputString.length() == 0) { + addError("invalid value encounterd in " + INPUT_STRINGS + " parameter list"); + } + inputStringList.add(inputString); + } + + // unwrap input delimiter and error check + List inputBoundDelimiterList = inputMap.get(INPUT_DELIMITER); + if (inputBoundDelimiterList == null || inputBoundDelimiterList.size() != 1) { + addError("Invalid parameter count for" + INPUT_DELIMITER); + return null; + } + + IData inputBoundDelimiterData = inputBoundDelimiterList.get(0); + if (inputBoundDelimiterData == null || !(inputBoundDelimiterData instanceof LiteralStringBinding)) { + addError("Something wierd happened with the request parser!"); + return null; + } + String inputDelimiterString = ((LiteralStringBinding)inputBoundDelimiterData).getPayload(); + Delimiter inputDelimiter = null; + try { + inputDelimiter = Delimiter.valueOf(inputDelimiterString); + } catch (IllegalArgumentException e) { + addError("invalid value encounterd for " + INPUT_DELIMITER + " parameter"); + return null; + } + + // do work + String outputString = Joiner.on(inputDelimiter.value).join(inputStringList); + + // wrap output + Map outputMap = new HashMap(); + outputMap.put(OUTPUT_STRING, new LiteralStringBinding(outputString)); + + return outputMap; + } + +} diff --git a/src/test/java/org/n52/wps/server/StringReverseAnnotatedAlgorithm.java b/src/test/java/org/n52/wps/server/StringReverseAnnotatedAlgorithm.java new file mode 100644 index 0000000..6d6af58 --- /dev/null +++ b/src/test/java/org/n52/wps/server/StringReverseAnnotatedAlgorithm.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import org.n52.wps.algorithm.annotation.Algorithm; +import org.n52.wps.algorithm.annotation.LiteralDataInput; +import org.n52.wps.algorithm.annotation.LiteralDataOutput; +import org.n52.wps.algorithm.annotation.Execute; + +/** + * + * @author tkunicki + */ +@Algorithm( + version="0.0.1", + title="String Reverse Algorithm (Annotated)", + abstrakt="This is an example algorithm implementation described using annotations that reverses a string.", + statusSupported=false, + storeSupported=false) +public class StringReverseAnnotatedAlgorithm extends AbstractAnnotatedAlgorithm { + + private String inputString; + private String outputString; + + @LiteralDataInput( + identifier="INPUT_STRING", + title="Input String", + abstrakt="The input string you want reversed.") + public void setInputString(String inputString) { + this.inputString = inputString; + } + + + @LiteralDataOutput( + identifier="OUTPUT_STRING", + title="Output String", + abstrakt="The reverse of the input string.") + public String getOutputString() { + return outputString; + } + + @Execute + public void reverse() { + outputString = (new StringBuffer(inputString)).reverse().toString(); + } + +} diff --git a/src/test/java/org/n52/wps/server/StringReverseSelfDescribingAlgorithm.java b/src/test/java/org/n52/wps/server/StringReverseSelfDescribingAlgorithm.java new file mode 100644 index 0000000..d61037f --- /dev/null +++ b/src/test/java/org/n52/wps/server/StringReverseSelfDescribingAlgorithm.java @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source + * Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.n52.wps.server; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.n52.wps.io.data.IData; +import org.n52.wps.algorithm.descriptor.AlgorithmDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataInputDescriptor; +import org.n52.wps.algorithm.descriptor.LiteralDataOutputDescriptor; +import org.n52.wps.io.data.binding.literal.LiteralStringBinding; + +/** + * + * @author tkunicki + */ +public class StringReverseSelfDescribingAlgorithm extends AbstractDescriptorAlgorithm { + + public final static String INPUT_STRING = "INPUT_STRING"; + public final static String OUTPUT_STRING = "OUTPUT_STRING"; + + private static AlgorithmDescriptor DESCRIPTOR; + protected synchronized static AlgorithmDescriptor getAlgorithmDescriptorStatic() { + if (DESCRIPTOR == null) { + DESCRIPTOR = + // passing in a class to the AlgorithmDescriptor.builder will set + // set the identity to the the fully qualified class name. If this + // is not desired use the String constructor. + AlgorithmDescriptor.builder(StringReverseSelfDescribingAlgorithm.class). + version("0.0.1"). // default is "1.0.0" + title("String Reverse Algorithm (Self Describing)"). // identifier is used if title is not set + abstrakt("This is an example algorithm implementation described using a chained builder that reverses a string."). // default is null (not output) + statusSupported(false). // default is true + storeSupported(false). // default is true + addInputDescriptor( + LiteralDataInputDescriptor.stringBuilder(INPUT_STRING). + title("Input String"). // identifier is used if title is not set + abstrakt("The input string you want reversed.").// defaults to null (not output) + minOccurs(1). // defaults to 1 + maxOccurs(1)). // defaults to 1 + addOutputDescriptor( + LiteralDataOutputDescriptor.stringBuilder(OUTPUT_STRING). + title("Output String"). // identifier is used if title is not set + abstrakt("The reverse of the input string.")). // defaults to null (not output). + build(); + } + return DESCRIPTOR; + } + + @Override + public AlgorithmDescriptor createAlgorithmDescriptor() { + return getAlgorithmDescriptorStatic(); + } + + @Override + public Map run(Map> inputMap) { + // unwrap input(s) + List inputDataList = inputMap.get(INPUT_STRING); + if (inputDataList == null || inputDataList.isEmpty()) { + addError("Missing input string!"); + return null; + } + IData inputData = inputDataList.get(0); + if (inputData == null || !(inputData instanceof LiteralStringBinding)) { + addError("Something wierd happened with the request parser!"); + return null; + } + String inputString = ((LiteralStringBinding)inputData).getPayload(); + + // do work + String outputString = (new StringBuffer(inputString)).reverse().toString(); + + // wrap output + Map outputMap = new HashMap(); + outputMap.put(OUTPUT_STRING, new LiteralStringBinding(outputString)); + + return outputMap; + } + +} 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..3e0c6a3 --- /dev/null +++ b/src/test/resources/META-INF/services/org.n52.wps.server.IAlgorithm @@ -0,0 +1,2 @@ +org.n52.wps.server.ServiceLoaderAlgorithmTest$DummyAnnotatedAlgorithm +org.n52.wps.server.ServiceLoaderAlgorithmTest$DummyIdentifiedAnnotatedAlgorithm diff --git a/src/test/resources/org/n52/test/mock/wps_config.xml b/src/test/resources/org/n52/test/mock/wps_config.xml new file mode 100644 index 0000000..72dd872 --- /dev/null +++ b/src/test/resources/org/n52/test/mock/wps_config.xml @@ -0,0 +1,41 @@ + + + + + + + http://a.mock.schema/apple + http://a.mock.schema/orange + test/mock + UTF-8 + + + + + + http://a.mock.schema/apple + http://a.mock.schema/orange + test/mock + UTF-8 + + + + + + + + org.n52.wps.server.SelfDescribingAlgorithmUsingDeprecatedAPI + org.n52.wps.server.SelfDescribingAlgorithmUsingDescriptor + + + + + + + + + + + +