[FrontPage] [TitleIndex] [WordIndex

HowTo: Write a deegree 3 WPS process provider

This is a tutorial for writing process providers that plug into the deegree 3 processingService. Basically, a process provider allows to add processes programmatically, rather than requiring to write process code and process descriptions for single processes manually.

For general information on the deegree 3 processingService including setup and configuration, please have a look at the WPS documentation. It is assumed that you're familiar with HowToCreateWPSProcesses and the concepts of processlets and process descriptions.

1. Setting up the example process provider

This how-to explains the relevant concepts by describing a very simple example process provider. The main purpose is to showcase the necessary steps for implementing a process provider, therefore the actually created processes have only very limited functionality -- they don't have any inputs and return a single literal output parameter with a constant value. The identifiers and returned values can be adapted in the XML configuration file of the provider.

For the following steps, it's required to have the system requirements installed on your machine.

1.1. Checkout the example provider module from SVN

You may use your favorite SVN client (e.g. TortoiseSVN) or just use the SVN command line client:

svn co http://svn.wald.intevation.org/svn/deegree/deegree3/tags/3.0.2/deegree-wps/deegree-wps-example-provider

Make sure to checkout out into a reasonable work directory. If you plan to work with this module in Eclipse, it's usually best to check out into the workspace folder.

1.2. Build / start the module

For these steps, it's required to have Maven 2 installed (2.2.1 is tested/recommended). On the command line, switch to the checkout directory.

For building the WAR, execute the following command (in the checkout directory):

mvn package

Afterwards, you will find the WAR in the target folder.

You may now deploy the WAR into your web servlet container installation, or just fire it up using Maven's Tomcat plugin (in this case, shut down any running web servlet containers beforehand, so the default port 8080 is available):

mvn tomcat:run

Point your web browser to the start page of the WPS webapp: http://localhost:8080/deegree-wps-example-provider. Select "send requests" for an interface that allows you to send example requests. You may stop the embedded Tomcat by pressing CTRL+C on the command line.

1.3. Set up the module in Eclipse (tested with 3.5)

On the command line:

mvn -Dwtpversion=2.0 eclipse:eclipse

This will create the necessary build path and project information needed by Eclipse. Afterwards (in Eclipse), create a new project (simply choose "Java Project", and enter deegree-wps-example-provider as the project name. Click "Finish". If you checked out into the Eclipse's workspace folder, the project will be set up correctly and should compile fine (if this is the first time your using a Maven project inside this Eclipse installation, you will need to set the M2_REPO variable.

Note that Eclipse recognizes the project as a "Dynamic web project", and thus you may just right-click on the project name and select "Run As" -> "Run on Server". This allows to start up the deegree WPS inside Eclipse, using it's embedded server configurations. HINT: To add a suitable server configuration, go to "Window" -> "Preferences" -> "Server" -> "Runtime Environment".

2. How it works

If you started Maven's embedded Tomcat as described above (or are using the embedded Tomcat in Eclipse), you will find a pre-configured client at http://127.0.0.1:8080/deegree-wps-example-provider/console/client/client.xhtml. The example requests reveal that the WPS serves two processes named 'Hello' and 'Answer'. These two processes and their descriptions are provided by the example process provider implementation and its configuration file.

2.1. Provider configuration / WPS integration

The following files are relevant for the integration and activation of the provider. As described for the standard WPS demo, the configuration files (the deegree workspace) reside in src/main/webapp/WEB-INF/conf. The relevant configuration difference is the file example.xml:

`-- services
|   |-- ...
|   |-- wps.xml
|   `-- metadata.xml
|-- processes
|   `-- example.xml

The file example.xml contains the configuration for the provider (a custom XML format defined to suite the needs of the provider implementation):

<?xml version="1.0" encoding="UTF-8"?>
<ExampleProvider xmlns="http://www.deegree.org/services/wps/example">
  <Process id="hello">Hello World!</Process>
  <Process id="answer">42</Process>
</ExampleProvider>

As you may have guessed, this configuration defines two processes. The first one (with identifier hello) returns the literal output with value "Hello World!", while the second one (answer) returns "42". If you're feeling curious, just add a new Process element with id and text and restart the WPS -- a new process will be offered as the DescribeProcess request in the client will reveal. If you wonder: the drop-down box of the client won't offer an example request for the new process, as these requests have been manually defined for the default configuration.

Of course, this configuration format is pretty much made up, but of course you're free to define your own custom format for your own process provider implementation.

2.2. Implementation

Technically, the plugging of the example provider implementation requires that the respective classes are found on the classpath during WPS startup and that the provider is registered via Java's Service Provider Interface (SPI). Don't get confused by the two meanings of the word provider here: the process provider is the component used by the WPS that creates the processes -- and then there's the SPI provider: this is the technical means of making Java/the deegree WPS aware of a new process provider implementation.

Relevant for the implementation are the following files below directory src/main:

.
|-- java
|   `-- org
|       `-- deegree
|           `-- services
|               `-- wps
|                   `-- provider
|                       |-- ExampleProcessProvider.java
|                       `-- ExampleProcessProviderProvider.java
`-- resources
    `-- META-INF
        `-- services
            `-- org.deegree.services.wps.provider.ProcessProviderProvider

2.2.1. ExampleProcessProviderProvider.java

Let's first have a look at the ExampleProcessProviderProvider class -- it's responsible for creating ExampleProcessProvider instances from the XML configuration documents.

...
public class ExampleProcessProviderProvider implements ProcessProviderProvider {

    private static final Logger LOG = LoggerFactory.getLogger( ExampleProcessProviderProvider.class );

    private static final String CONFIG_NAMESPACE = "http://www.deegree.org/processes/example";

    @Override
    public String getConfigNamespace() {
        return CONFIG_NAMESPACE;
    }

    @Override
    public ProcessProvider createProvider( URL configURL ) {

        LOG.info( "Configuring example process provider using file '" + configURL + "'." );

        Map<String, String> processIdToReturnValue = new HashMap<String, String>();

        try {
            XMLStreamReader xmlStream = XMLInputFactory.newInstance().createXMLStreamReader( configURL.openStream() );
            while ( xmlStream.getEventType() != XMLStreamConstants.END_DOCUMENT ) {
                if ( xmlStream.isStartElement() && "Process".equals( xmlStream.getLocalName() ) ) {
                    String processId = xmlStream.getAttributeValue( null, "id" );
                    String returnValue = xmlStream.getElementText();
                    processIdToReturnValue.put( processId, returnValue );
                } else {
                    xmlStream.next();
                }
            }
        } catch ( Exception e ) {
            e.printStackTrace();
            throw new RuntimeException( "Error parsing example process provider configuration '" + configURL + "': "
                                        + e.getMessage() );
        }

        return new ExampleProcessProvider( processIdToReturnValue );
    }
}

The two implemented methods are defined by deegree's ProcessProviderProvider interface and used by the deegree WPS to integrate process provider configurations and implementations:

  1. #getConfigNamespace(): This method returns the namespace for the root element of the XML configuration file that this provider claims to be responsible for. Note that the returned value ("http://www.deegree.org/services/wps/example") is identical to the namespace of the root element of the actual configuration document. When the WPS scans the processes directory for *.xml files, it determines the namespace and looks up the ProcessProviderProvider implementation that claims to be responsible for handling it. For this implementation, the method #createProvider( URL ) is invoked to create the actual process provider instance.

  2. #createProvider( URL ): Called by the WPS to create a process provider instance for the given XML configuration document. The actual format and parsing of the XML file is totally up to the implementation -- it could be any XML base technology (DOM, SAX, StAX, ...) or XML binding technology (JAXB, Castor, etc.). In this case, the configuration is parsed using StAX. After extracting the configuration, the method returns a new and configured instance of the ExampleProcessProvider class.

2.2.2. ExampleProcessProvider.java

Here's the code for the ExampleProcessProvider class. This class is responsible for actually providing the process instances to the WPS.

...
public class ExampleProcessProvider implements ProcessProvider {

    private final Map<CodeType, WPSProcess> idToProcess = new HashMap<CodeType, WPSProcess>();

    ExampleProcessProvider( Map<String, String> processIdToReturnValue ) {
        for ( Entry<String, String> entry : processIdToReturnValue.entrySet() ) {
            String processId = entry.getKey();
            String returnValue = entry.getValue();
            WPSProcess process = createProcess( processId, returnValue );
            idToProcess.put( new CodeType( processId ), process );
        }
    }

    private WPSProcess createProcess( String processId, String returnValue ) {

        // create Processlet instance dynamically
        ConstantProcesslet processlet = new ConstantProcesslet( returnValue );

        // create process definition dynamically
        ProcessDefinition definition = createProcessDefinition( processId );

        // build WPSProcess from processlet and process definition
        return new GenericWPSProcess( definition, processlet );
    }

    private ProcessDefinition createProcessDefinition( String processId ) {

        ProcessDefinition definition = new ProcessDefinition();
        ...
        return definition;
    }

    @Override
    public void init()
                            throws ServiceInitException {
        for ( WPSProcess process : idToProcess.values() ) {
            process.getProcesslet().init();
        }
    }

    @Override
    public void destroy() {
        for ( WPSProcess process : idToProcess.values() ) {
            process.getProcesslet().destroy();
        }
    }

    @Override
    public WPSProcess getProcess( CodeType id ) {
        return idToProcess.get( id );
    }

    @Override
    public Map<CodeType, WPSProcess> getProcesses() {
        return idToProcess;
    }
}

The following methods are defined by deegree's ProcessProvider interface and used by the WPS to interact with the provider:

  1. #getProcess(CodeType): Returns the process with the specified identifier.

  2. #getProcesses(): Returns all currently available processes.

  3. #init(): Called by the container to indicate that this ProcessProvider instance is being placed into service.

  4. #destroy(): Called by the container to indicate that this ProcessProvider instance is being taken out of service.

The actual magic happens in the constructor when the provider creates the WPSProcess instances. Each of these instances represents a single WPS process, i.e. the process code (as a Processlet) instance and a process description. NOTE: The programmatical creation of the process description is rather tedious at the moment, as it uses the JAXB beans generated from the ProcessDefinition format for hand-written processes. This is still subject to changes / improvements.

To completely understand the example, you will need to have a look at the ConstantProcesslet class, which is custom-tailored to the needs of the example provider (it has a constructor parameter that allows to select the return value that the created instance will return during execution):

class ConstantProcesslet implements Processlet {

    private String returnValue;

    /**
     * Creates a new {@link ConstantProcesslet} instance that will always return the given value in the
     * {@link #process(ProcessletInputs, ProcessletOutputs, ProcessletExecutionInfo)} method.
     *
     * @param returnValue
     *            value to be returned by the execution
     */
    ConstantProcesslet( String returnValue ) {
        this.returnValue = returnValue;
    }

    @Override
    public void destroy() {
        // nothing to do
    }

    @Override
    public void init() {
        // nothing to do
    }

    @Override
    public void process( ProcessletInputs in, ProcessletOutputs out, ProcessletExecutionInfo info )
                            throws ProcessletException {
        LiteralOutput literalOutput = (LiteralOutput) out.getParameter( "LiteralOutput" );
        literalOutput.setValue( returnValue );
    }
}

That's it! You've now seen every nut, bolt and screw of the example process provider.

3. Customizing the example to your own needs

In order to derive your own provider, you will need to change the following files:

.
|-- java
|   `-- org
|       `-- deegree
|           `-- services
|               `-- wps
|                   `-- provider
|                       |-- ExampleProcessProvider.java
|                       `-- ExampleProcessProviderProvider.java
`-- resources
    `-- META-INF
        `-- services
            `-- org.deegree.services.wps.provider.ProcessProviderProvider

3.1. Class names

First, you will probably want to change the names and packages for the ExampleProcessProviderProvider and ExampleProcessProvider. Let's say you wanted to use org.acme.ACMEProcessProviderProvider and org.acme.ACMEProcessProvider instead.

This requires a change in the file src/main/java/resources/META-INF/services/org.deegree.services.wps.provider.ProcessProviderProvider:

Replace:

org.deegree.services.wps.provider.ExampleProcessProviderProvider

With:

org.acme.ACMEProcessProviderProvider

3.2. Configuration file format

Next, you most probably want to use a different configuration file format. This requires to adapt ACMEProcessProviderProvider.java (former ExampleProcessProviderProvider.java)

...
public class ACMEProcessProviderProvider implements ProcessProviderProvider {

    private static final Logger LOG = LoggerFactory.getLogger( ACMEProcessProviderProvider.class );

    private static final String CONFIG_NAMESPACE = "http://www.acme.org/wps";

    @Override
    public String getConfigNamespace() {
        return CONFIG_NAMESPACE;
    }

    @Override
    public ProcessProvider createProvider( URL configURL ) {

        LOG.info( "Configuring ACME process provider using file '" + configURL + "'." );

        ...

        return new ACMEProcessProvider( processIdToReturnValue );
    }
}

Note that CONFIG_NAMESPACE has been changed. This means that the WPS will invoke ACMEProcessProviderProvider#createProvider(...) for all *.xml configuration files with namespace http://www.acme.org/wps. Also, the #createProvider(...) method needs to be adapted to the desired configuration format and to return ACMEProcessProvider instances.

3.3. Process logic

Last, but not least, the acctual work begins! The ACMEProcessProvider class must be adapted to actually create the WPSProcess instances that you want it to offer. If you need help with this, don't hesitate to ask for assistance.

Thanks for reading, and please drop a line to the deegree mailing lists when you created a new WPS process provider that may be of interest to the community.


CategoryDeegree3 CategoryHowTo


2018-04-20 12:05