"Seegrid will be due for a migration to confluence on the 1st of August. Any update on or after the 1st of August will NOT be migrated"

GeoServer Webservice Module User Guide

Contents

Related pages


Introduction

The webservices module allows an xml datasource to be queried and return data to geoserver. It extends and leverages the app-schema code to do this.

Properties of other datastores

The webservice module is an alternative to other geoserver datastores, mostly database based. A key property of all these datastores is that they are queryable. In other words a query (like sql) can be passed to the backend, and they return results based on this query.

Looking even closer, ALL mapped attributes are queryable, although certain filter operations such as equals, less than, AND, OR etc may not be supported. If these operations are not supported then a simplified query is sent to the backend for the parts that are supported, and then the results under go further filtering by geoserver itself so that only valid rows are return.

Requirements of the backend "webservice"

  • protocol is HTTP.
  • POST is used to send the request
  • response is XML (does not have to be a webservice)
  • All mapped attributes are queryable
  • Text based request file (need not be xml).
  • Webservice can take the query information in the request and make sense of it.
  • All the required data for a complex type is returned in one request.
Just to make clear a very important point made above, the response needs to be xml, however it does not have to be a webservice. XML over HTTP is a more accurate description. Wherever the term webservice is used in this guide, understand it is used loosely to mean 'xml over http'.

Configuring the webservice

The webservice module is designed to be used with the App-Schema module and cannot be used on its own. An App-schema configuration file must be created to connect to the webservice and map the ws response to the output schema (refer to the app-Schema documentation on how to do this). There are a couple of differences and interesting points that will be covered here however.

Datastore section

In the datastore section the following needs to be set, along with appropiate values:

<DataStore>
  <id>ws</id>
  <parameters>
    <Parameter><name>WSDataStoreFactory:GET_CONNECTION_URL</name><value>http://www.request.url.com </value></Parameter>
    <Parameter><name>WSDataStoreFactory:TIMEOUT</name><value>300000</value></Parameter>
    <Parameter><name>WSDataStoreFactory:TEMPLATE_DIRECTORY</name><value>file:/var/s-gedis/software/wfs/geoserver/geoserver-gsv-geosciml-config/workspaces/gsml/gsml_GeologicUnit1</value></Parameter>
    <Parameter><name>WSDataStoreFactory:TEMPLATE_NAME</name><value>request.ftl</value></Parameter>
    <Parameter><name>WSDataStoreFactory:CAPABILITIES_FILE_LOCATION</name><value>file:/var/s-gedis/software/wfs/geoserver/geoserver-gsv-geosciml-config/workspaces/gsml/gsml_GeologicUnit1/ws_capabilities.xml</value></Parameter>
  </parameters>
</DataStore>

Property Meaning
GET_CONNECTION_URL url to send a webservice request to
TIMEOUT time to wait for a response before timing out. Measured in milliseconds. Default value is unlimited.
TEMPLATE_DIRECTORY directory holding the template to create the request, must include protocol
TEMPLATE_NAME name of the file, in the template directory to use to create requests from
CAPABILITIES_FILE_LOCATION file location for the file specifying the capabilities of the webservice. ie What operations it can handle

Request template

A template engine called freemarker (http://freemarker.org/) is used to create the http request. A template file needs to be defined. At runtime freemarker takes the query information passed in and subsitutes the values at the indicated spots in the template. It is then sent out as an HTTP POST request. Here is the contents of an example template file:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:geod="http://www.dpi.vic.gov.au/minpet/GeoDataVicWebService/">
   <soapenv:Header/>
   <soapenv:Body>
      <geod:getGeologicalFeaturesByFilterString>
         <filter>${filterCql}</filter>
         <maxfeatures>${maxFeatures}</maxfeatures>         
      </geod:getGeologicalFeaturesByFilterString>
   </soapenv:Body>
</soapenv:Envelope>

Here the placeholders ${filterCql} and ${maxFeatures} are replaced by the appropiate values. Other possible variables are ${filterString} and ${query}.

Capabilities file

The capabilities file specified what operations, such as EqualTo, Like, Between, LessThan, NullCheck etc are supported by the backend. It uses the standard WFS_Capabilities format. Here is an example:

<?xml version="1.0" encoding="UTF-8"?>
<wfs:WFS_Capabilities version="1.1.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wfs"
  xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"   
  xmlns:gml="http://www.opengis.net/gml"
  xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"   
  updateSequence="0">
  <ogc:Filter_Capabilities>
    <!-- dummy spatial capabilities. Included only because it a compulsory field. -->   
    <ogc:Spatial_Capabilities>
      <ogc:GeometryOperands><ogc:GeometryOperand>gml:Envelope</ogc:GeometryOperand> </ogc:GeometryOperands>
      <ogc:SpatialOperators><ogc:SpatialOperator/></ogc:SpatialOperators>
    </ogc:Spatial_Capabilities>
    <ogc:Scalar_Capabilities>
      <ogc:Logical_Operators/>
        <ogc:ComparisonOperators>
          <ogc:ComparisonOperator>LessThan</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>GreaterThan</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>LessThanEqualTo</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>GreaterThanEqualTo</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>EqualTo</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>NotEqualTo</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>Like</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>Between</ogc:ComparisonOperator>
          <ogc:ComparisonOperator>NullCheck</ogc:ComparisonOperator>             
       </ogc:ComparisonOperators>
    </ogc:Scalar_Capabilities>
    <ogc:Id_Capabilities><ogc:EID/></ogc:Id_Capabilities>
  </ogc:Filter_Capabilities>
</wfs:WFS_Capabilities>

This file has all the comparison operates listed, which means all operations are supported. If an operation is not supported then either comment it out or remove the entry from the file.

If an operation is not supported by the backend then geoserver will send a request through that IS supported that returns a superset of the data required. It will then filter this down further to give the correct response. Care needs to be taken, as the filter sent through may well be 'get everything'. This has obvious performance implications.

Also note that if you say an operator is supported by the backend, then geoserver assumes it works for ALL attributes. There is no ability to say that a comparision operator works for some attributes, but not others.

Mapping the data

Data is mapped from the xml response to the output schema, in the same way as the rest of App-Schema, with some important differences.

The first difference is that because the source of data is not a database, the 'from values' do not contain references to db columns but to xpaths instead.

The next major difference is that a complex object is built up in the configuration into a tree like structure. Special tags are used to define this in the one document. An example is given below.

FeatureTypeMapping section

Note the flag isXmlDataStore. This indicates to App-Schema that the datastore is a webservices module.

<FeatureTypeMapping>
  <sourceDataStore>ws</sourceDataStore>
  <sourceType>gsv:GeologicalUnitFeature</sourceType>
  <targetElement>gsml:GeologicUnit</targetElement>
  <itemXpath>/soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item</itemXpath>
  <isXmlDataStore>true</isXmlDataStore>

Mapping the attributes

Here is an example of some mapped attributes:
<AttributeMapping>
  <label>gsml:GeologicUnit</label>
  <targetAttribute>gsml:GeologicUnit</targetAttribute>
  <idExpression><OCQL>asXpath('@gmlID')</OCQL></idExpression>
  <instancePath>/soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item</instancePath>
</AttributeMapping>

<AttributeMapping>
  <parentLabel>gsml:GeologicUnit</parentLabel>
  <targetAttribute>gml:name</targetAttribute>
  <sourceExpression><inputAttribute>gsv:formattedName</inputAttribute></sourceExpression>
  <ClientProperty><name>codeSpace</name><value>'urn:cgi:party:CGI:GSV'</value></ClientProperty>
</AttributeMapping>

The first attribute mapping has a label attribute. All nodes that have children need to have a label so they can be refered to. The contents of the label are arbitary. The children then have a parentLabel node pointing to the parent. The second attribute mapping shows this. It has a parentLabel refering to the label of the first element. As the first attribute does not have a parent label tag, it must be the root element of the tree. There can only be one root element in the configuration file.

Also illustrated here, the first tag creates a gsml:GeologicUnit object. The id of the object is obtained from the xml by the xpath /soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item/@gmlID. This is just from instancePath + idExpression. Please also see asXpath section.

The next tag sets the gml:name attribute on the gsml:GeologicUnit object (since its parent is gsml:GeologicUnit). The value is obtained via the xpath /soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item/gsv:formattedName. This is instancePath of parent + instance path of this attribute (not specified and hence blank in this case) + inputAttribute.

This element also has a client property, which is a constant in this case. Constants are indicated with single quotes. These quotes will be striped off by the time the data is displayed.

<AttributeMapping>
  <parentLabel>gsml:GeologicUnit</parentLabel>
  <targetAttribute>gsml:observationMethod/gsml:CGI_TermValue/gsml:value</targetAttribute>
  <sourceExpression><OCQL>'urn:ogc:def:nil:OGC::missing'</OCQL></sourceExpression>
  <ClientProperty><name>codeSpace</name><value>'http://urn.opengis.net'</value></ClientProperty>
</AttributeMapping>

Here is another mapping (above), again pointing to gsml:GeologicUnit as the parent. In this case both the value and clientProperty are constants.

<AttributeMapping>
  <parentLabel>gsml:GeologicUnit</parentLabel>
  <targetAttribute>gsml:geologicUnitType</targetAttribute>
  <sourceExpression><OCQL>''</OCQL></sourceExpression>
  <ClientProperty>
    <name>xlink:href</name>
    <value>asXpath('gsv:geologicalUnit/gsv:GeologicalUnit/gsv:type/erd:TermValue/erd:value/erd:ClassificationCode/erd:urn[@domain=''CGI'']')</value>
  </ClientProperty>
</AttributeMapping>

The above mapping has an empty value for the tag gsml:geologicUnit, however it has an xlink:href tag which is populated via an xpath. The full xpath will be instance path of the parent + instance path of the element (not set and hence blank in this case) + xpath of element (since it is a constant, not set) + client property xpath.

Finally here is is an example of an nested complex type:
<AttributeMapping>
  <label>gsml:GeologicUnit/gsml:composition</label>
  <parentLabel>gsml:GeologicUnit</parentLabel>
  <targetAttribute>gsml:composition</targetAttribute>
  <targetQueryString>gsml:composition/gsml:CompositionPart</targetQueryString>
  <idExpression><OCQL>asXpath('../@id')</OCQL></idExpression>
  <instancePath>gsv:component</instancePath>
  <targetAttributeNode>gsml:CompositionPartPropertyType</targetAttributeNode>
</AttributeMapping>

<AttributeMapping>
  <label>gsml:GeologicUnit/gsml:composition/gsml:CompositionPart</label>
  <parentLabel>gsml:GeologicUnit/gsml:composition</parentLabel>
  <targetAttribute>gsml:CompositionPart</targetAttribute>
  <targetQueryString>gsml:CompositionPart</targetQueryString>
  <idExpression><OCQL>asXpath('@id')</OCQL></idExpression>
  <instancePath>gsv:GUComponentPart</instancePath>
  <targetAttributeNode>gsml:CompositionPartType</targetAttributeNode>
</AttributeMapping>

<AttributeMapping>
  <parentLabel>gsml:GeologicUnit/gsml:composition/gsml:CompositionPart</parentLabel>
  <targetAttribute>gsml:role</targetAttribute>
  <sourceExpression><inputAttribute>gsv:role/erd:TermValue/erd:value/erd:ClassificationCode/erd:name</inputAttribute></sourceExpression>
  <ClientProperty><name>codeSpace</name><value>asXpath('../erd:classificationScheme/@p145:href')</value></ClientProperty>
</AttributeMapping>

This might look complex but basically on the gsml:GeologicUnit object (root element) we are setting the value gsml:composition. This value is set to a new object of type gsml:CompositionPartPropertyType. This new object then has a property called gsml:CompositionPart which is set, with a new object of type gsml:CompositionPartType. And finally the gsml:role property of this object is set, along with a client property called codeSpace. Note the use of relative paths in the xpath. The two objects created also have an id value set. The xpath is worked out using the formula specified in the previous examples.

asXpath Function

This is a special function written specific for the webservice module. This is to prevent parser exception when using xpath with special characters or functions.

The implied xpath value follows the same rule with inputAttribute, i.e. it adds itemXpath + instancePath of the attribute mappings referred by the parentLabels + instancePath of the attribute mapping itself.

E.g.
  • asXpath used to escape '@' in xpath used in idExpression. Please note that <OCQL> is used instead of <inputAttribute>, since asXpath is a function. In this case, xpath = itemXpath + "/soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item" (instancePath) + '@gmlID'.

<AttributeMapping>
  <label>gsml:GeologicUnit</label>
  <targetAttribute>gsml:GeologicUnit</targetAttribute>
  <idExpression><OCQL>asXpath('@gmlID')</OCQL></idExpression>
  <instancePath>/soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item</instancePath>
</AttributeMapping>

  • asXpath used to escape '[@domain='CGI']' in ClientProperty value. Please note the extra quotes around 'CGI' to escape quotes as special characters themselves. In this case, xpath = itemXpath + 'soapenv:Envelope/soapenv:Body/p246:getGeologicalFeaturesByFilterStringResponse/out/item' (instancePath of gsml:GeologicUnit as the parentLabel) + 'gsv:geologicalUnit/gsv:GeologicalUnit/gsv:type/erd:TermValue/erd:value/erd:ClassificationCode/erd:urn[@domain='CGI']'.

<AttributeMapping>
  <parentLabel>gsml:GeologicUnit</parentLabel>
  <targetAttribute>gsml:geologicUnitType</targetAttribute>
  <sourceExpression><OCQL>''</OCQL></sourceExpression>
  <ClientProperty>
    <name>xlink:href</name>
    <value>asXpath('gsv:geologicalUnit/gsv:GeologicalUnit/gsv:type/erd:TermValue/erd:value/erd:ClassificationCode/erd:urn[@domain=''CGI'']')</value>
  </ClientProperty>
</AttributeMapping>

  • asXpath used to express xpath parameter to a function. Please note that <OCQL> is used instead of <inputAttribute>, since asXpath is a function. In this case, xpath = 'http://geology.data.vic.gov.au/feature/gsv/sheardisplacementstructure/' (strConcat first parameter) + itemXpath + '@id' (assuming there's no parentLabel and instancePath for this attribute mapping).

 <sourceExpression><OCQL>strConcat('http://geology.data.vic.gov.au/feature/gsv/sheardisplacementstructure/', asXpath('@id'))</OCQL></sourceExpression>

Multi-valued Attributes

Multi-valued attributes are grouped by a labelled attribute mapping that acts as the root of each multi-valued node. For example:
<AttributeMapping>
    <label>component</label>
    <parentLabel>root</parentLabel>
    <targetAttribute>gsml:composition</targetAttribute>
    <idExpression><OCQL>strConcat('gsml.compositionpart.', asXpath('gsv:GUComponentPart/@id'))</OCQL></idExpression>
    <instancePath>gsv:component</instancePath>
</AttributeMapping>

If gsv:component (from the input side) has 3 instances, all the nodes will be created and each will be mapped for one gsml:composition (targetAttribute). The above mapping will create:
<gsml:composition>
<gsml:composition>
<gsml:composition>

The children nodes are mapped separately, grouped by the parentLabel. For example:
<AttributeMapping>   
    <parentLabel>component</parentLabel>         
    <targetAttribute>gsml:composition/gsml:CompositionPart/gsml:role</targetAttribute>
    <sourceExpression>
   <inputAttribute>gsv:GUComponentPart/gsv:role/erd:TermValue/erd:value/erd:ClassificationCode/erd:name</inputAttribute>
    </sourceExpression>
</AttributeMapping>

The subsequent children attribute mapping will create each attribute in the parent attribute:
<gsml:composition>
    <gsml:CompositionPart>
        <gsml:role>trace component</gsml:role>
   <gsml:CompositionPart>
</gsml:composition>
<gsml:composition>
    <gsml:CompositionPart>
        <gsml:role>interbedded component</gsml:role>
   <gsml:CompositionPart>
</gsml:composition>
<gsml:composition>
    <gsml:CompositionPart>
        <gsml:role>sole component</gsml:role>
   <gsml:CompositionPart>
</gsml:composition>

Feature chaining a webservice module

Feature chaining is supported to map the webservice object. There are two cases to consider:
  • Another App-Schema based feature containing a webservice feature inside it. eg An app-schema MappedFeature could embed a webservice based geologicUnit. In this case the MappedFeature is the 'parent' feature.
  • A webservice based feature could embed another normal app-schema feature, or another webservice based app-schema feature. eg a webservice based geologicUnit with MappedFeatures embedded inside. In this case the webservice module is the parent.
Both cases would work with feature chaining as normal. Please refer to the Feature Chaining User Guide for more details.
Topic revision: r7 - 20 Jun 2011, RiniAngreani
 

Current license: All material on this collaboration platform is licensed under a Creative Commons Attribution 3.0 Australia Licence (CC BY 3.0).