Creating your first ZOO Service

Introduction

In this part, you will create and publish a simple ZOO-Service named Hello which will simply return a hello message containing the input value provided. It will be usefull to present in deeper details general concept on how ZOO-Kernel works and handles Execute requests.

Service and publication process overview

Before starting developing a ZOO Service, you should remember that in ZOO-Project, a Service is a couple made of:

  • a metadata file: a ZOO Service Configuration File (ZCFG) containing metadata informations about a Service (providing informations about default / supported inputs and outputs for a Service)

  • a Services Provider: it depends on the programming language used, but for Python it is a module and for JavaScript a script file.

To publish your Service, which means make your ZOO Kernel aware of its presence, you should copy a ZCFG file in the directory where zoo_loader.cgi is located (in this workshop, /usr/lib/cgi-bin) or in any subdirectory.

Warning

only the ZCFG file is required for the Service to be considerate as available. So if you don’t get the Service Provider, obviously your Execute request will fail as we will discuss later.

Before publication, you should store your ongoing work, so you’ll start by creating a directory to store the files of your Services Provider:

mkdir -p /home/user/zoo-ws/ws_sp/cgi-env

Once the ZCFG and the Python module are both ready, you can publish simply by copying the corresponding files in the same directory as the ZOO-Kernel.

Creating your first ZCFG file

You will start by creating the ZCFG file for the Hello Service. Edit the /home/user/zoo-ws/ws_sp/cgi-env/Hello.zcfg file and add the following content:

 1[Hello]
 2 Title = Return a hello message.
 3 Abstract = Create a welcome string.
 4 processVersion = 2
 5 storeSupported = true
 6 statusSupported = true
 7 serviceProvider = test_service
 8 serviceType = Python
 9 <DataInputs>
10  [name]
11   Title = Input string
12   Abstract = The string to insert in the hello message.
13   minOccurs = 1
14   maxOccurs = 1
15   <LiteralData>
16       dataType = string
17       <Default />
18   </LiteralData>
19 </DataInputs>
20 <DataOutputs>
21  [Result]
22   Title = The resulting string
23   Abstract = The hello message containing the input string
24   <LiteralData>
25       dataType = string
26       <Default />
27   </LiteralData>
28 </DataOutputs>

Note

the name of the ZCFG file and the name between braket (here [Hello]) should be the same and correspond to the function name you will define in your Services provider.

As you can see in the ZOO Service Configuration File presented above it is divided into three distinct sections:

  1. Main Metadata information (from line 2 to 8)

  2. List of Inputs metadata information (from 9 line to 19)

  3. List of Outputs metadata information (from line 20 to 28)

You can get more informations about ZCFG from the reference documentation.

If you copy the Hello.zcfg file in the same directory as your ZOO Kernel then you will be able to request for DescribeProcess using the Hello Identifier. The Hello service should also be listed from Capabilities document.

Test requests

In this section you will tests each WPS requests : GetCapabilities, DescribeProcess and Execute. Note that only GetCapabilities and DescribeProcess should work at this step.

Test the GetCapabilities request

If you run the GetCapabilities request:

http://localhost/cgi-bin/zoo_loader.cgi?request=GetCapabilities&service=WPS

Now, you should find your Hello Service in a Process node in ProcessOfferings:

<wps:Process wps:processVersion="2">
 <ows:Identifier>Hello</ows:Identifier>
 <ows:Title>Return a hello message.</ows:Title>
 <ows:Abstract>Create a welcome string.</ows:Abstract>
</wps:Process>

Using the 2.0.0 version of WPS, you should be able to use the following URL.

http://localhost/cgi-bin/zoo_loader.cgi?request=GetCapabilities&service=WPS&AcceptVersion=2.0.0

Test the DescribeProcess request

You can access the ProcessDescription of the Hello service using the following DescribeProcess request:

http://localhost/cgi-bin/zoo_loader.cgi?request=DescribeProcess&service=WPS&version=1.0.0&Identifier=Hello

You should get the following response:

<wps:ProcessDescriptions xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_response.xsd" service="WPS" version="1.0.0" xml:lang="en-US">
  <ProcessDescription wps:processVersion="2" storeSupported="true" statusSupported="true">
    <ows:Identifier>Hello</ows:Identifier>
    <ows:Title>Return a hello message.</ows:Title>
    <ows:Abstract>Create a welcome string.</ows:Abstract>
    <DataInputs>
      <Input minOccurs="1" maxOccurs="1">
        <ows:Identifier>name</ows:Identifier>
        <ows:Title>Input string</ows:Title>
        <ows:Abstract>The string to insert in the hello message.</ows:Abstract>
        <LiteralData>
          <ows:DataType ows:reference="http://www.w3.org/TR/xmlschema-2/#string">string</ows:DataType>
          <ows:AnyValue/>
        </LiteralData>
      </Input>
    </DataInputs>
    <ProcessOutputs>
      <Output>
        <ows:Identifier>Result</ows:Identifier>
        <ows:Title>The resulting string</ows:Title>
        <ows:Abstract>The hello message containing the input string</ows:Abstract>
        <LiteralOutput>
          <ows:DataType ows:reference="http://www.w3.org/TR/xmlschema-2/#string">string</ows:DataType>
        </LiteralOutput>
      </Output>
    </ProcessOutputs>
  </ProcessDescription>
</wps:ProcessDescriptions>

Using the 2.0.0 version of WPS, you should be able to use the following URL.

http://localhost/cgi-bin/zoo_loader.cgi?request=DescribeProcess&service=WPS&version=2.0.0&Identifier=Hello

Test the Execute request

Obviously, you cannot run your Service because the Python file was not published yet. If you try the following Execute request:

http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Hello&DataInputs=name=toto

You should get an ExceptionReport similar to the one provided in the following, which is normal behavior:

<ows:ExceptionReport xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/ows/1.1 http://schemas.opengis.net/ows/1.1.0/owsExceptionReport.xsd" xml:lang="en-US" version="1.1.0">
  <ows:Exception exceptionCode="NoApplicableCode">
    <ows:ExceptionText>Python module test_service cannot be loaded.</ows:ExceptionText>
  </ows:Exception>
</ows:ExceptionReport>

Implementing the Python Service

General Principles

The most important thing you must know when implementing a new ZOO-Services using the Python language is that the function corresponding to your Service returns an integer value representing the status of execution (SERVICE_FAILED [1] or SERVICE_SUCCEEDED [2]) and takes three arguments (Python dictionaries):

  • conf : the main environment configuration (corresponding to the main.cfg content)

  • inputs : the requested / default inputs (used to access input values)

  • outputs : the requested / default outputs (used to store computation result)

Note

when your service return SERVICE_FAILED you can set conf["lenv"]["message"] to add a personalized message in the ExceptionReport returned by the ZOO Kernel in such case.

You get in the following a sample conf value based on the main.cfg file you saw before.

 1{
 2  "main": {
 3    language: "en-US",
 4    lang: "fr-FR,ja-JP",
 5    version: "1.0.0",
 6    encoding: "utf-8",
 7    serverAddress: "http://localhost/cgi-bin/zoo_loader.cgi",
 8    dataPath: "/var/data",
 9    tmpPath: "/var/www/temp",
10    tmpUrl: "../temp",
11    cacheDir: "/var/www/temp/"
12  },
13  "identification": {
14    title: "ZOO-Project Workshop - FOSS4G 2017",
15    keywords: "WPS,GIS,buffer",
16    abstract: "Deploying Web Processing Services using ZOO-Project – Examples of Python based WPS using PgRouting",
17    accessConstraints: "none",
18    fees: "None"
19  },
20  "provider": {
21    positionName: "Developer",
22    providerName: "ZOO-Project",
23    addressAdministrativeArea: "Lattes",
24    addressCountry: "fr",
25    phoneVoice: "False",
26    addressPostalCode: "34970",
27    role: "Dev",
28    providerSite: "http://www.zoo-project.org",
29    phoneFacsimile: "False",
30    addressElectronicMailAddress: "gerald.fenoy@geolabs.fr",
31    addressCity: "Denver",
32    individualName: "Gérald FENOY"
33  }

In the following you get a sample outputs value passed to a Python or a JavaScript Service:

1{
2  'Result': {
3    'mimeType': 'application/json',
4    'inRequest': 'true',
5    'encoding': 'UTF-8'
6  }
7}

Note

the inRequest value is set internally by the ZOO-Kernel and can be used to determine from the Service if the key was provided in the request.

ZOO-Project provide a ZOO-API which was originally only available for JavaScript services, but thanks to the work of the ZOO-Project community, now you have also access to a ZOO-API when using the Python language. Thanks to the Python ZOO-API you don’t have to remember anymore the value of SERVICE_SUCCEDED and SERVICE_FAILED, you have the capability to translate any string from your Python service by calling the _ function (ie: zoo._('My string to translate')) or to update the current status of a running service by using the update_status [3] function the same way you use it from JavaScript or C services.

The Hello Service

You can copy and paste the following into the /home/user/zoo-ws/ws_sp/cgi-env/test_service.py file.

import zoo
def Hello(conf,inputs,outputs):
    outputs["Result"]["value"]=\
            "Hello "+inputs["name"]["value"]+" from the ZOO-Project Python world !"
    return zoo.SERVICE_SUCCEEDED

Once you finish editing the file, you should copy it in the /usr/lib/cgi-bin directory:

sudo cp /home/user/zoo-ws/ws_sp/cgi-env/* /usr/lib/cgi-bin

Interracting with your service using Execute requests

Now, you can request for Execute using the following basic url:

http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Hello&DataInputs=name=toto

You can request the WPS Server to return a XML WPS Response containing the result of your computation, requesting for ResponseDocument or you can access the data directly requesting for RawDataOutput.

  • Sample request using the RawDataOutput parameter:

http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Hello&DataInputs=name=toto&RawDataOutput=Result
  • Sample request using the default ResponseDocument parameter:

http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Hello&DataInputs=name=toto&ResponseDocument=Result

When you are using ResponseDocument there is specific attribut you can use to ask the ZOO Kernel to store the result: asReference. You can use the following example:

http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Hello&DataInputs=name=toto&ResponseDocument=Result@asReference=true

When computation take long time, the client should request the execution of a Service by setting both storeExecuteResponse and status parameter to true to force asynchronous execution. This will make the ZOO-Kernel return, without waiting for the Service execution completion but after starting another ZOO-Kernel process responsible of the Service execution, a ResponseDocument containing a statusLocation attribute which can be used to access the status of an ongoing service or the result when the process ended [4].

http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Hello&DataInputs=name=toto&ResponseDocument=Result&storeExecuteResponse=true&status=true

Conclusion

Even if this first service was really simple it was useful to illustrate how the ZOO-Kernel fill conf, inputs and outputs parameter prior to load and run your function service, how to write a ZCFG file, how to publish a Services Provider by placing the ZCFG and Python files in the same directory as the ZOO-Kernel, then how to interract with your service using both GetCapabilities, DescribeProcess and Execute requests. We will see in the next section how to write similar requests using the XML syntax.

Footnotes