implementing.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. .. _wps_services_implementing:
  2. Implementing a WPS Process
  3. ==========================
  4. This section describes how to implement a WPS process for use in GeoServer.
  5. It demonstrates the development artifacts and build steps
  6. necessary to create a WPS process, deploy it in GeoServer,
  7. and test it.
  8. The example process used is a simple "Hello World" process
  9. which accepts a single input parameter and returns a single text output.
  10. .. note:: See also GeoTools :geotools:`process tutorial <tutorial/process.html>`
  11. Prerequisites
  12. -------------
  13. Before starting, GeoServer must be built on the local system. See
  14. the :ref:`source` and :ref:`quickstart` sections for details.
  15. GeoServer must be built with WPS support as described in the
  16. :ref:`maven_guide` section.
  17. Specifically, make sure GeoServer is built using the ``-Pwps`` profile.
  18. Alternatively, the custom WPS plug-in can be deployed into an existing GeoServer
  19. instance (which must have the WPS extension installed).
  20. Create the process module
  21. -------------------------
  22. To create a new WPS process plug-in module the first step is to create a Maven project.
  23. For this example the project will be called "hello_wps".
  24. #. Create a new directory named ``hello_wps`` somewhere on the file system.
  25. #. Add the following ``pom.xml`` to the root of the new module in the ``hello_wps`` directory:
  26. .. code-block:: xml
  27. <project xmlns="http://maven.apache.org/POM/4.0.0"
  28. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  29. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  30. http://maven.apache.org/maven-v4_0_0.xsd ">
  31. <modelVersion>4.0.0</modelVersion>
  32. <groupId>org.geoserver</groupId>
  33. <artifactId>hello_wps</artifactId>
  34. <packaging>jar</packaging>
  35. <version>2.8-SNAPSHOT</version>
  36. <name>hello_wps</name>
  37. <properties>
  38. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  39. <gt.version>14-SNAPSHOT</gt.version> <!-- change to GeoTools version -->
  40. <gs.version>2.8-SNAPSHOT</gs.version> <!-- change to GeoServer version -->
  41. </properties>
  42. <dependencies>
  43. <dependency>
  44. <groupId>org.geotools</groupId>
  45. <artifactId>gt-process</artifactId>
  46. <version>${gt.version}</version>
  47. </dependency>
  48. <dependency>
  49. <groupId>org.geoserver.extension</groupId>
  50. <artifactId>gs-wps-core</artifactId>
  51. <version>${gs.version}</version>
  52. </dependency>
  53. <dependency>
  54. <groupId>org.geoserver</groupId>
  55. <artifactId>gs-main</artifactId>
  56. <version>${gs.version}</version>
  57. <classifier>tests</classifier>
  58. <scope>test</scope>
  59. </dependency>
  60. <dependency>
  61. <groupId>junit</groupId>
  62. <artifactId>junit</artifactId>
  63. <version>4.11</version>
  64. <scope>test</scope>
  65. </dependency>
  66. <dependency>
  67. <groupId>com.mockrunner</groupId>
  68. <artifactId>mockrunner</artifactId>
  69. <version>0.3.6</version>
  70. <scope>test</scope>
  71. </dependency>
  72. </dependencies>
  73. <build>
  74. <plugins>
  75. <plugin>
  76. <artifactId>maven-compiler-plugin</artifactId>
  77. <configuration>
  78. <source>1.8</source>
  79. <target>1.8</target>
  80. </configuration>
  81. </plugin>
  82. </plugins>
  83. </build>
  84. <repositories>
  85. <repository>
  86. <id>boundless</id>
  87. <name>Boundless Maven Repository</name>
  88. <url>https://repo.boundlessgeo.com/main</url>
  89. </repository>
  90. <repository>
  91. <id>osgeo</id>
  92. <name>Open Source Geospatial Foundation Repository</name>
  93. <url>https://download.osgeo.org/webdav/geotools</url>
  94. </repository>
  95. </repositories>
  96. </project>
  97. #. Create the directory ``src/main/java`` under the root of the new module::
  98. [hello_wps]% mkdir -p src/main/java
  99. The project should now have the following structure::
  100. hello_wps/
  101. + pom.xml
  102. + src/
  103. + main/
  104. + java/
  105. Create the process class
  106. ------------------------
  107. #. Create the package that will contain the custom WPS process.
  108. For this example, create a package named ``org.geoserver.hello.wps`` inside the
  109. *src/main/java* directory structure.
  110. [hello_wps]% mkdir -p src/main/java/org/geoserver/hello/wps
  111. #. Create the Java class that implements the custom WPS process.
  112. Create a Java class called ``HelloWPS.java`` inside the created package (make sure you are in the 'src/main/java' folder and not in the 'src/test/java' folder):
  113. .. code-block:: java
  114. package org.geoserver.hello.wps;
  115. import org.geotools.process.factory.DescribeParameter;
  116. import org.geotools.process.factory.DescribeProcess;
  117. import org.geotools.process.factory.DescribeResult;
  118. import org.geoserver.wps.gs.GeoServerProcess;
  119. @DescribeProcess(title="helloWPS", description="Hello WPS Sample")
  120. public class HelloWPS implements GeoServerProcess {
  121. @DescribeResult(name="result", description="output result")
  122. public String execute(@DescribeParameter(name="name", description="name to return") String name) {
  123. return "Hello, " + name;
  124. }
  125. }
  126. Register the process in GeoServer
  127. ---------------------------------
  128. GeoServer uses the `Spring Framework <http://www.springsource.org/spring-framework/>`_ to manage
  129. instantiation of components. This mechanism is used to register the process with GeoServer when it
  130. starts, which will make it discoverable via the WPS service interface.
  131. #. Create a directory ``src/main/resources`` under the root of the new module::
  132. [hello_wps]% mkdir -p src/main/resources
  133. The project should now have the following directory structure::
  134. hello_wps/
  135. + pom.xml
  136. + src/
  137. + main/
  138. + java/
  139. + resources/
  140. #. Create an ``applicationContext.xml`` in the ``src/main/resources`` directory with the following contents:
  141. .. code-block:: xml
  142. <?xml version="1.0" encoding="UTF-8"?>
  143. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
  144. <beans>
  145. <bean id="helloWPS" class="org.geoserver.hello.wps.HelloWPS"/>
  146. </beans>
  147. .. note:: A process registered in the GeoServer spring context will be assigned to the "gs"
  148. process namespace.
  149. Build and Deploy
  150. ----------------
  151. To build the custom process, run the following command from the root of the project:
  152. .. code-block:: console
  153. mvn clean install
  154. This cleans the build area, compiles the code, and creates a JAR file in the ``target`` directory.
  155. The JAR file name is determined by the name and version given to the project in the ``pom.xml`` file.
  156. (for this example it is ``hello_wps-2.6-SNAPSHOT.jar``).
  157. To deploy the process module, copy this JAR file into the ``/WEB-INF/lib`` directory of GeoServer and then restart the instance.
  158. .. note::
  159. For alternative deployment options (i.e. running from source), see the *Trying it out*
  160. section inside :ref:`ows_services_implementing`
  161. Test
  162. ----
  163. You can verify that the new process was deployed successfully by using
  164. the **WPS Request Builder**. The WPS Request Builder is a utility that allows invoking WPS processes
  165. through the GeoServer UI. Access this utility by navigating to the *WPS Request Builder* in the *Demos*
  166. section of the GeoServer Web Admin Interface.
  167. In the WPS Request Builder select the process called ``gs:helloWPS`` from the **Choose process** dropdown.
  168. The request builder displays an interface which allows calling the process, based on the
  169. parameters and outputs described in the capabilities of the process
  170. (which are defined by the process class annotations).
  171. The following image shows the WPS Request Builder running the ``gs:helloWPS`` process.
  172. Enter the desired parameter and click on **Execute process** to run it. A window with the expected result should appear.
  173. .. figure:: img/helloWPS.png
  174. *WPS Request Builder, showing gs:HelloWPS process parameters*
  175. Accepting or returning raw data
  176. -------------------------------
  177. The basic GeoServer WPS architecture is meant to offload and centralize input decoding and output encoding, leaving
  178. the processes to work against Java objects, and automatically creating new input and output types for all processes
  179. as soon as a new matching PPIO is registered.
  180. It is however also possible to leave the process to accept both raw inputs and outputs, and do the parsing encoding itself.
  181. This suits well binding to external network or command line tools that are already doing parsing and encoding as their
  182. normal activities.
  183. Raw inputs and outputs are represented by the RawData interface:
  184. .. code-block:: java
  185. public interface RawData {
  186. /**
  187. * Returns the mime type of the stream's contents
  188. *
  189. * @return
  190. */
  191. public String getMimeType();
  192. /**
  193. * Gives access to the raw data contents.
  194. *
  195. * @return
  196. * @throws FileNotFoundException
  197. */
  198. public InputStream getInputStream() throws IOException;
  199. /**
  200. * Optional field for output raw data, used by
  201. * WPS to generate a file extension
  202. *
  203. * @return
  204. */
  205. public String getFileExtension();
  206. }
  207. As an input, the RawData will be provided to the process, that will discover the mimeType chosen by the user,
  208. and will get access to the raw input stream of the data.
  209. As an output, the process will return a RawData and the WPS will see what mimeType the result will be in, get access
  210. to the raw contents, and grab a file extension to build file names for the user file downloads.
  211. The process using RawData will also have to provide some extra metadata in the annotations, in order to declare
  212. which mime types are supported and to allow the process to know which output mime types were chosen in the Execute request.
  213. The extra annotations ``mimeTypes`` and ``chosenMimeType`` are placed in the ``meta`` section of the result and parameter annotations:
  214. .. code-block:: java
  215. @DescribeResult(name = "result", description = "Output raster",
  216. meta = {"mimeTypes=application/json,text/xml",
  217. "chosenMimeType=outputMimeType" })
  218. public RawData execute(
  219. @DescribeParameter(name = "data",
  220. meta = { "mimeTypes=text/plain" })
  221. final RawData input,
  222. @DescribeParameter(name = "outputMimeType", min = 0)
  223. final String outputMimeType) {
  224. The above instructs GeoServer WPS about raw data handling:
  225. * The ``result`` output can be returned in ``application/json`` or ``text/xml``, with ``application/json`` as the default one
  226. * The mime type chosen by the user for the output will be provided to the process as the ``outputMimeType`` parameter (and this parameter will be
  227. hidden from the DescribeProcess output)
  228. * The ``input`` parameter will be advertised as supporting the ``text/plain`` mime type
  229. In terms of building a ``RawData``, the process is free to create its own class if needed,
  230. or it can use one of the existing ``FileRawData``, ``StringRawData``, ``StreamRawData`` implementations.