Fork me on GitHub

SOS Contributor Guide


Introduction

This is the developer guide for the SOS.

HELP To all contributors:

If you contributing code, then please follow these best practice documents:

Build status

Jenkins:
Travis:

Code

GitHub URLs

Glossary

ALERT! Reference to SensorWebForDummies.
Phenomenon
see Observed Property
Observed Property
a property that is observed by a sensor at a feature of interest
Procedure
see Sensor
Sensor
something that can perform observations

Coordinates

All coordinates within the SOS shall have the specified order. To get the axis order for your srs, your can use this registry http://www.epsg-registry.org/. If the datasource requires a different order, this shall be established within the Handlers or DAOs, e.g. for features in the FeatureQueryHandler implementation.

Admin/Installer interface

ALERT! work in progress

During the 2012 Google Summer of Code an Installer and Administrator Webinterface was introduced to the 52N SOS (for previous developments see SosInstallAdminTestbed)

Architecture

  • The webapp uses the Spring MVC framework.
  • All configurations settings are moved to the database table global_settings. Only the database configuration is persisted in a properties file at WEB-INF/config/datasource.properties.
  • The application uses WEB-INF/config/datasource.properties to check whether or not the service is installed
  • The controller classes are located in org.n52.sos.web and it's subpackages
  • The JSP/JSTL views are located in WEB-INF/views
  • Spring configuration (and security configuration) is done in WEB-INF/applicationContext.xml and WEB-INF/dispatcher-servlet.xml

Installer

  • Installation is a 4 step process
    1. Index page: welcoming words, ability to upload a previos configuration (as JSON). The extracted properties are loaded into the session and can be revised in the following steps. Database settings and administrator credentials are not loaded
    2. Database configuration: the database connections settings are chosen and tested. This includes the following:
      • existence of driver class
      • existence of connection pool class
      • existence of JDBC dialect class
      • connection testing using a plain JDBC connection
        • existence of server and database
        • check for valid datasbase credentials
      • existence of PostGIS extension
      • existence and rights of spatial_ref_sys table
      • check for exising SOS tables
        • currently only the observation table is checked
        • validation of create/overwrite parameters
          • check for ability to create tables
        • check for the version of the installed SOS
          • currently only a warning is logged if versions differ
    3. Settings: Service Identification/Service Provider/miscellaneous settings can be altered
      • allows the user to configure the SOS
      • settings will only be roughly validated (existence and format (e.g. integer values))
    4. Finish: Administrator credentials are chosen
      • The previous steps didn't modify anything
      • Every action takes place here:
        • Validate credentials
        • Create tables (if necessary)
        • Insert test data (if necessary)
        • Insert settings
        • Save version to database
        • Save admin user
        • Instantiate Configurator
        • Save datasource.properties
        • Save installation date to meta.properties
  • Every access to the installer is blocked by org.n52.sos.web.install.InstallFilter if the datasource.properties file is written
  • Default installation settings are read from
    • WEB-INF/static/conf/default-database-values.json
    • WEB-INF/static/conf/sos-settings.json

Administrator

Test Client

  • The test client was completly rewritten to embed it into the new webapp
  • Example requests are defined in WEB-INF/static/conf/client-requests.json
    • The file has the following format:
            {
               "service":,
               "version":,
               "binding": ,
               "operation":,
               "title":,
               "headers":{
                     "Accept":
               },
               "method":
            }
            
    • binding values are : application/x-kvp, application/soap+xml, application/xml, application/json
    • To define a POST request the request property is used. The value is a path pointing to a file that contains the request.
            {
               "request": "static/examples/sos_v20/requests_soap/Transactional/InsertObservation_Category.xml",
               "service": ...
            }  
    • To define a GET request the param property is used. Every JSON property is used as a query parameters (and getting URL encoded). Arrays will be merged to a comma seperated list. The folling results in the query /kvp?service=SOS&version=2.0.0&request=GetFeatureOfInterest&featureOfInterest=test_feature_1,test_feature_2
       
               {
                  "param": {
                     "service": "SOS",
                     "version": "2.0.0",
                     "request": "GetFeatureOfInterest",
                     "featureOfInterest": [
                        "test_feature_1", 
                        "test_feature_2"
                     ]
                  },
                  "service": ...
               } 
  • Requests are disabled/enabled depending on which versions/bindings (identified by the path) and operations are supported by the SOS. This is accomplished by requesting the corresponding operators from the Configurator.

Extension Points

Datasources

The Installer is able to perform configurations based on any datasource. Every datasource has to implement the interface org.n52.sos.ds.Datasource (or for Hibernate based datasources extend the class org.n52.sos.ds.datasource.AbstractHibernateDatasource in hibernate-common). Datasources are loaded with the Java ServiceLoader API and are used to configure and validate the datasource backend of the SOS and to provide functionalities for the administrator interface. It is possible to create datasources for any type of backend, ranging from databases to SOAP services.
  • Declaration of settings that the admin/installer will present to the user
  • Validation of settings and prerequisites or a schema if needed
  • Validation of changes to a existing configuration
  • Creation of a datasource.properties to initialiize the SOS
  • Creation and removal of a schema (if needed)
  • Creation and removal of a test data set (if supported)
  • Removal of all data (if supported)

Database

DBMS

PostgreSQL/PostGIS

pgAdmin

If you display the values from the observation table, the timestamps (without time zone) are located in UTC, as they stored in the database.

MySQL

MySQL Workbench

If you display the values from the observation table, the timestamps are converted to system/default time zone, and they are not shown as they are stored in the database (UTC).

Extensions

  • Encoders return XmlBeans *Type and not *Document objects
  • HibernateException extends RuntimeException!!!

Binding

This section provides some starting points for developing your own binding by implementing the required interfaces from 52n-sos-api module. But first, what is a binding:

"[A binding] describe[s] how SOS [...] clients and servers can communicate with each other." (OGC#12-006 Sec. 13)

The specification defines two default bindings:
  • KVP - using KVP encoded HTTP GET requests
  • SOAP - using XML encoded SOAP requests with HTTP POST
In addition, we have implemented two other bindings:
  • a RESTful binding
  • POX - using plain old XML encoded requests via HTTP POST
If you want to develop your own binding, the starting point is the abstract class org.n52.sos.binding.Binding with the following interface:
Binding.png

Depending on your binding you need to implement different methods. Within the 52°North SOS, the different bindings are identified by an URL pattern (e.g. soap → SOAP, kvp → KVP binding). Hence, you can execute a GetCapabilities request using the KVP binding via [...]WEB_APP/sos/kvp?service=SOS&request=GetCapabilities. The getUrlPattern() method provides this pattern and requires your implementation. Currently, the following patterns are already in use:
  • kvp
  • soap
  • pox
  • rest
HELP It helps us to maintain this list, if you tell us when implementing another binding as open source.

Now, some functionality. It is foreseen to use at least one of following HTTP methods in your binding:
  • DELETE
  • GET
  • OPTIONS
  • POST
  • PUT
The abstract Binding class offers already an implementation for each of these methods returning a HTTP 501 - Not implemented for all methods you don't override. For each HTTP method, a check method can be implemented to tell the SOS logic, that you are handling the current service-version-operation combination with the according HTTP method. Here, the default implementation is return false. For more details, checkout the source code and javadoc.

[...] What is missing here?

If you want to provide your own example requests for the integrated test client, just follow these steps.

TODO: Writer's Notes
  • exceptions -> own section <- used in Operation section, too.
  • exceptions need to be encoded by each binding -> examples, which protected methods are available in abstract class Binding

Operation

This section provides some starting points for developing your own operation by implementing some interfaces of the 52n-sos-api module. When doing it right, your new operation shall be usable by all already existing bindings.

ALERT!For each interface of the 52n-sos-api module that you implement directly (or indirectly via any Abstract* type), you need to provide a so called ServiceLoader configuration file, having the same name of the implemented interface and containing a list of qualified names of types implementing this interface (one per line). This file must be located in src/main/resources/META-INF/services in the according maven module.

RequestOperator

An request operator is called by the internal service logic to handle a specific received request. In OGC services, the functionality is available in terms of requests (:= operations, e.g. GetCapabilities, GetObservation). First of all, you need to implement the org.n52.sos.request.operator.RequestOperator interface which requires the implementation of the following methods: RequestOperator.png

You shall implement this interface by extending the abstract class org.n52.sos.request.operator.AbstractRequestOperator<D extends OperationDAO, R extends AbstractServiceRequest>.

AbstractRequestOperator.png

This results in implementing only the receiveRequest(AbstractServiceRequest request) method. A generic workflow could look like this

   42	public ServiceResponse receiveRequest(MyOperationRequest sosRequest) throws OwsExceptionReport
   43	{
   44	    // doing some parameter checks and throwing a subtype of OwsExceptionReport in the case of any errors
   45	    checkRequestedParameter(sosRequest);
   46	    
   47	    // process the request by the DAO
   48	    MyOperationResponse sosResponse = getDao().myOperation(sosRequest);
   49	    
   50	    // the next two steps are only required of the content of the SOS is changed by the new operation
   51	    SosEventBus.fire(new MyOperationEvent(sosRequest, sosResponse));
   52	    getConfigurator().getCacheController().updateAfterMyOperation();
   53	    
   54	    // method is trying to get an encoder for the type of the sosResponse object which returns an SosResponse object
   55	    return encode(sosResponse);
   56	}

You might have recognized, that four additional interfaces are now added to our "list of interfaces to be implemented":
  • org.n52.sos.request.operator.RequestOperator
  • org.n52.sos.ds.OperationDAO
  • org.n52.sos.request.AbstractServiceRequest → super type of MyOperationRequest
  • org.n52.sos.response.AbstractServiceResponse → super type of MyOperationResponse
  • optional: org.n52.sos.event.SosEvent → super type of MyOperationEvent

OperationDAO

An OperationDAO (DAO := DataAccessObject) provides means to interact with the underlying data source. Hence, the implementation of the OperationDAO is data source vendor specific and results in the definition of an operation specific extension of the OperationDAO interface, e.g. MyOperationDAO. The interface is implemented in vendor specific modules (here: maven module). This makes them easy to exchange (You might make an abstract implementation of the OperationDAO to share the implementation of the OperationDAO.setOperationsMetadata(opsMeta,service,version) method).

The interface OperationDAO defines the following methods:

OperationDAO.png

Using the abstract super type org.n52.sos.ds.AbstractOperationDAO for your implementation will reduce the number of methods to implement:

AbstractOperationDAO.png

   23	public abstract class MyOperationDAO extends AbstractOperationDAO
   24	{
   25	    
   26	    public MyOperationDAO()
   27	    {
   28	        super(MyOperationConstants.OPERATION_NAME);
   29	    }
   30	
   31	    @Override
   32	    protected void setOperationsMetadata(OWSOperation opsMeta, String service, String version) throws OwsExceptionReport
   33	   {
   34	        // adding some allowed values to the operations metadata section of the GetCapabilities response
   35	        opsMeta.addPossibleValuesParameter(PARAMETER_NAME, getCache().getObservationIdentifiers());
   36	        [...]
   37	    }
   38	
   39	    /**
   40	      * My operation does something urgently required by each SOS user
   41	      * [...]
   42	      * Only this method needs to be implemented by the data source vendor specific implementations.
   43	      */
   44	    public abstract MyOperationResponse myOperation(MyOperationRequest myOperationRequest) throws OwsExceptionReport;
   45	}

Our "list of interfaces to be implemented" looks like this (I am leaving out the implementation of !MyOperationDAO, you might remember it on your own):
  • org.n52.sos.request.operator.RequestOperator
  • org.n52.sos.ds.OperationDAO
  • org.n52.sos.request.AbstractServiceRequest → super type of MyOperationRequest
  • org.n52.sos.response.AbstractServiceResponse → super type of MyOperationResponse
  • optional: org.n52.sos.event.SosEvent → super type of MyOperationEvent

MyOperation{Request|Response}

The *Request and *Response objects are very generic data holding types. The abstract super types require very less methods to be implemented. You need to think about what properties are required for performing the new operation and encoding a valuable response. Therefore the abstract type org.n52.sos.request.AbstractServiceRequest requires only one method:

   42	/**
   43	  * @return the operationName
   44	  */
   45	public abstract String getOperationName();

A little bit easier is the implementation of the abstract type org.n52.sos.response.AbstractServiceResponse. It requires no method at all. Remember, it's your turn to think, here.

Our "list of interfaces to be implemented" looks like this (I am leaving out the implementation of !MyOperationDAO, you might remember it on your own):
  • org.n52.sos.request.operator.RequestOperator
  • org.n52.sos.ds.OperationDAO
  • org.n52.sos.request.AbstractServiceRequest → super type of MyOperationRequest
  • org.n52.sos.response.AbstractServiceResponse → super type of MyOperationResponse
  • optional: org.n52.sos.event.SosEvent → super type of MyOperationEvent → optional, therefore not part of this guide.

{De|En}coder

In the previous sections, we were talking about requests and response being java types. In addition, in the context of the SOS there are other types of requests and responses: HTTP-. The internal logic is only capable of the AbstractService{Request|Response} types. Hence, we need to provide something that is able to translate between those two: an Encoder and a Decoder. The interfaces are:
  • org.n52.sos.encode.Encoder
  • org.n52.sos.decode.Decoder
HELP In both cases, S in the source type and T is the target or resulting type.

To identify the correct decoder for the correct request, a DecoderKey is required. See here an example for handling incoming MyOperationDocument objects. *Document types are often used by XMLbeans, for example. Therefor, the decoder with the following key will be able to decode XML encoded MyOperation requests:

   42	@SuppressWarnings("unchecked")
   43	private static final Set<DecoderKey> DECODER_KEYS = CollectionHelper.union(
   44	    CodingHelper.decoderKeysForElements(MyOperationConstants.NAME_SPACE, MyOperationDocument.class),
   45	    CodingHelper.xmlDecoderKeysForOperation(SosConstants.SOS, Sos2Constants.SERVICEVERSION, MyOperationConstants.Operations.MyOperation));

This key tells the SOS, that this decoder will handle SOS 2.0 MyOperation requests of type !MyOperationDocument. T is MyOperationRequest and S org.apache.xmlbeans.XmlObject. The interface requires the following methods to be implemented:

Decoder.png

After processing the request, we have to return a response. In the case of exceptions being thrown the SOS logic will do all the required business. Hence, we need to think about the encoding of good responses, only.

The encoding of the MyOperationResponse is done by implementing a org.n52.sos.encode.Encoder<SosResponse, MyOperationResponse> including the methods of the interface.

An example for the ENCODER_KEYS:

   42	@SuppressWarnings("unchecked")
   43	private static final Set<EncoderKey> ENCODER_KEYS = 
   44	    CodingHelper.encoderKeysForElements(MyOperationConstants.NAME_SPACE, MyOperationResponse.class);

HELP In the case of implementing a KVP decoder for SOS 2.0, you shall implement the abstract class org.n52.sos.decode.kvp.v2.AbstractKvpDecoder and know its interfaces.

Encoder

This section describes the development of a new encoder. Next to the base encoder interface the specialised sub interfaces and the most important abstract encoder classes implementations (for JSON and XML) are described. The informatio which Object an encoder can handle are provided in the encoder keys which are described in last section.

An encoder shall implement the org.n52.sos.encode.Encoder interface which defines the required methods for an encoder implementation.

A helpful class for XML encoding is the Object.CodingHelper. This class provides some methods to create EncoderKey or to encode an Object to an XmlObject.

The UML of the org.n52.sos.encode.Encoder interface:

Encoder.png

Specialized sub interfaces

This section describes specialized sub interfaces of the org.n52.sos.encode.Encoder interface. The sub interfaces provide further methods which are used by the service.

ObservationEncoder

The org.n52.sos.encode.ObservationEncoder interface defines additional methods for observation encoder and each observation encoder shall implement this interface.

Additional methods are for example to get the responseFormats which is supported by this observation encoder. This interface extends org.n52.sos.encode.Encoder with the following methods:
  • boolean isObservationAndMeasurmentV20Type()
    • necessary for SOS 2.0 GetObservationResponse encoding
  • boolean shallObservationsWithSameXBeMerged()
    • flag to merge observations with the same procedure, observableProperty and featureOfInterest
  • Set<String> getSupportedResponseFormats(String service, String version)
    • Get the responseFormats supported by this encoder depending on Service and Version.

The UML of the org.n52.sos.encode.ObservationEncoder interface:

ObservationEncoder.png

ProcedureEncoder

The org.n52.sos.encode.ProcedureEncoder interface identifies a procedure encoder and inherits additional methods from the org.n52.sos.coding.ProcedureCoder. Each procedure encoder shall implement this interface.

Additional methods are for example to get the procedure description formats which are supported by this encoder.

The UML of the org.n52.sos.encode.ProcedureCoder and org.n52.sos.coding.ProcedureCoder interface:

ProcedureCoder.png

The UML of the org.n52.sos.encode.ProcedureEncoder interface:

ProcedureEncoder.png

StreamingEncoder

The org.n52.sos.encode.StreamingEncoder interface defines additional methods for streaming encoders. With a streaming encoder the 52N SOS writes the data into an OutputStream. For example in the case of XML encoding, the 52N SOS can write the response directly into the output stream without creating a full XML document before sending the response.

This interface extends org.n52.sos.encode.Encoder with the following methods:
  • void encode(S objectToEncode, OutputStream outputStream) throws OwsExceptionReport;
    • To encode the objectToEncode directly to the OutputStream
  • void encode(S objectToEncode, OutputStream outputStream, EncodingValues encodingValues) throws OwsExceptionReport;
    • To encode the objectToEncode directly to the OutputStream and provide some EncodingValues which are required for the encoding.
  • boolean forceStreaming(); * Force streaming of the encoding.

The UML of the org.n52.sos.encode.StreamingEncoder interface:

StreamingEncoder.png

Abstract classes

For some encodings the 52N SOS provides abstract classes which implement the Encoder interface. The abstract class implements methods which are default for all sub classes and defines the returned object. Examples are encoders for JSON and XML.

JSONEncoder

The abstract org.n52.sos.encode.json.JSONEncoder shall be extended for all concrete JSON encoders. It implements methods which provide information that are equal for all JSON encoders, e.g. the contentType, and defines that the returned object is a JsonNode.

The UML of the org.n52.sos.encode.json.JSONEncoder class:

JSONEncoder.png

AbstractSpecificXmlEncoder

The abstract org.n52.sos.encode.AbstractSpecificXmlEncoder implements methods which provide information which are equal for all XML encoders and defines some further methods that are helpful in the sub classes.

The UML of the org.n52.sos.encode.AbstractSpecificXmlEncoder class:

AbstractSpecificXmlEncoder.png

AbstractXmlEncoder

The abstract org.n52.sos.encode.AbstractXmlEncoder shall be extended for all concrete XML encoder and implements methods that provide information which are equal for all XML encoder, e.g. the content type, and defines the returned object is a XmlObject.

The UML of the org.n52.sos.encode.AbstractXmlEncoder class:

AbstractXmlEncoder.png

To concrete object encoder

Instead of implementing an encoder that extends from the AbstractXmlEncoder you can implement an encoder for a concrete XML type that is returned, e.g. XML Document or PropertyType.

EncoderKey

The information which Object an encoder can handle are provided in the org.n52.sos.encode.EncoderKey. Each Encoder has the getEncoderKeyType() method to get the EncoderKeys

The UML of the EncoderKey interfaces and implemented classes:

EncoderKeyOverview.png

JSONEncoderKey

The org.n52.sos.encode.json.JSONEncoderKey is used by JSONEncoder and defines which Object can be encoded by the specific JSONEncoder.

An example for the ENCODER_KEYS of a JSONEncoder:

@SuppressWarnings("unchecked")
private static final Set<EncoderKey> ENCODER_KEYS = 
    Sets.newHashSet(new JSONEncoderKey(MyClass.class));

ClassToClassEncoderKey

The org.n52.sos.encode.ClassToClassEncoderKey is used to define which Object can be encoded to which Object, e.g. OmObservation (52N SOS) to OMObservationType (XmlBeans).

An example for the ENCODER_KEYS:

@SuppressWarnings("unchecked")
private static final Set<EncoderKey> ENCODER_KEYS = 
    Sets.newHashSet(new ClassToClassEncoderKey(ToEncodeClass.class, MyClass.class));

XmlEncoderKey

The org.n52.sos.encode.XmlEncoderKey is used to define which Object can be encoded by the XML enocder to XmlObject (XmlBeans).

An example for the ENCODER_KEYS:

@SuppressWarnings("unchecked")
private static final Set<EncoderKey> ENCODER_KEYS = 
    CodingHelper.encoderKeysForElements(namespace, MyClass.class, MyClass2.class);

XmlDocumentEncoderKey

The org.n52.sos.encode.XmlDocumentEncoderKey is used to define which Object can be encoded to a XML document, e.g. OmObservation (52N SOS) to OMObservationDocument (XmlBeans).

An example for the ENCODER_KEYS:

@SuppressWarnings("unchecked")
private static final Set<EncoderKey> ENCODER_KEYS = 
    Sets.newHashSet(new XmlDocumentEncoderKey(namespace, MyClass.class));

XmlPropertyTypeEncoderKey

The org.n52.sos.encode.XmlPropertyTypeEncoderKey is used to define which Object can be encoded to XML propertyType, e.g. OmObservation (52N SOS) to OMObservationPropertyType (XmlBeans).

An example for the ENCODER_KEYS:

@SuppressWarnings("unchecked")
private static final Set<EncoderKey> ENCODER_KEYS = 
    Sets.newHashSet(new XmlPropertyTypeEncoderKey(namespace, MyClass.class));

New Observation types

This chapter describes how to add the support for a new observation type to the 52N SOS.

There are two options:
  1. Usine an additional database table to store the new result type.
  2. Use the existing tables and convert the observation to the requested type.

Add Value to API

Observation value class

Define a new observation value class which shall implement the org.n52.sos.ogc.om.values.Value interface.

The UML of the org.n52.sos.ogc.om.values.Value interface:

Value.png

For single observation values, your new value class can directly implement the org.n52.sos.ogc.om.values.Value interface. If your observation value shall contain multiple single values, you can use the org.n52.sos.ogc.om.values.MultiValue interface which extends from org.n52.sos.ogc.om.values.Value and contains a list of values. An exmaple for a MultiValue is the org.n52.sos.ogc.om.values.TVPValue class.

Value visitors

Now you have to apply your new observation value class to the value visitor interfaces org.n52.sos.ogc.om.values.visitor.ValueVisitor and the org.n52.sos.ogc.om.values.visitor.VoidValueVisitor and implement the methods in the classes which implements these visitors.

The UML of the org.n52.sos.ogc.om.values.visitor.ValueVisitor interface and org.n52.sos.ogc.om.values.visitor.VoidValueVisitor class:

ValueVisitor.png

Database tables and Hibernate entities

This section describes how to define a new database table in the Hibernate mapping files and how to add a new Hibernate entity class to the SOS.

Changes to the database tables would be required if it is not possible to store the new observation type in the existing value tables.

Database tables

To add a new observation type to the database model, you have to enhance the Observation.hbm.xml and the ValuedObservation.hbm.xml files in the src/main/resources/mapping/series/observation folder of the hibernate-mapping module.

Since the 52N SOS provides further database concepts you shall also enhance the mappings for the other concepts. The following table defines the folder for the other database concepts.

The default concept is the series concept which has a separate series table which is related to the obsevation table. The series table is used to identify all observation for the combination of procedure, observedProperty and featureOfInterest.

The ereporting concept is an enhanced series concepts that fulfills the requirements for the AQD e-Reporting.

The old concept is deprecated and holds the procedure, observedProperty and featureOfInterest information directly in the observation table.

database concept mapping path
old src/main/resources/mapping/old/observation
series src/main/resources/mapping/series/observation
ereporting src/main/resources/mapping/ereporting
There you have to add a new similar to the existing. The new definition shall extends the org.n52.sos.ds.hibernate.entities.observation.series.AbstractSeriesObservation

As an example for a single valued observation the numericValue table definition can be used for orientation. For a multi value observation we recommend to have a look at the complexValue table definition. In this case the single values of the complex observation are stored as separate observation.

Important notes
  • A name of table, column, key, ... shall not be longer than 30 characters.
  • A table name shall be in lowercase.

Observation.hbm.xml

The name of the shall be something like this org.n52.sos.ds.hibernate.entities.observation.series.full.SeriesMyTypeObservation in the Observation.hbm.xml.

Here you can find the example for your new observation.

...
<joined-subclass name="org.n52.sos.ds.hibernate.entities.observation.series.full.SeriesMyTypeObservation"
                       extends="org.n52.sos.ds.hibernate.entities.observation.series.AbstractSeriesObservation"
                       table="mytypevalue">
  <comment>Value table for MyType observation</comment>
  <key foreign-key="observationMyTypValueFk">
    <column name="observationId" >
     <comment>Foreign Key (FK) to the related observation from the observation table. Contains "observation".observationid</comment>
    </column>
  </key>
  <property name="value" 
                type="MyType">
    <column name="value">
      <comment>MyType observation value</comment>
    </column>
  </property>
</joined-subclass>
...

ValuedObservation.hbm.xml

The name of the shall be something like this org.n52.sos.ds.hibernate.entities.observation.series.valued.MyTypeValuedSeriesObservation in the ValuedObservation.hbm.xml.

Here you can find the example for your new valued observation.

...
<joined-subclass name="org.n52.sos.ds.hibernate.entities.observation.series.valued.MyTypeValuedSeriesObservation"
                       extends="org.n52.sos.ds.hibernate.entities.observation.series.AbstractValuedSeriesObservation"
                       table="mytypevalue">
  <comment>Value table for MyType  observation</comment>
  <key foreign-key="observationMyType ValueFk">
    <column name="observationId" >
      <comment>Foreign Key (FK) to the related observation from the observation table. Contains "observation".observationid</comment>
    </column>
  </key>
  <property name="value" 
                type="MyType ">
    <column name="value">
      <comment>MyType observation value</comment>
    </column>
  </property>
</joined-subclass>
...

Hibernate entities

The corresponding Hibernate entities shall be added to the hibernate-common module. As a minimum you have two add two interfaces and two classes that implement the interfaces. One interface and class is for ValuedObservation interface and the others for the Observation interface.

ValuedObservation interface and class

For your new observation value you have to implement a ValuedObservation interface and a class for the new observation type.

The UML of the org.n52.sos.ds.hibernate.entities.observation.valued.ValuedObservation interface:

ValuedObservation.png

The definition of the interface for the new observation value shall look like this:
package org.n52.sos.ds.hibernate.entities.observation.valued;

public interface MyTypeValuedObservation extends ValuedObservation<MyType> {
 // add here the additional method definition for your type
}

The class implementation of the new observation shall look like this:
package org.n52.sos.ds.hibernate.entities.observation.series.valued;

public class MyTypeValuedSeriesObservation extends AbstractValuedSeriesObservation<MyType>
        implements MyTypeValuedObservation {

}

The UML of the org.n52.sos.ds.hibernate.entities.observation.series.valued.AbstractValuedSeriesObservation class:

AbstracValuedSeriesObservation.png

The class implementation example is for the series database concept. In the following table you can find the relevant classes for the other database concepts.

database concept super class
old org.n52.sos.ds.hibernate.entities.observation.legacy.AbstractValuedLegacyObservation
series org.n52.sos.ds.hibernate.entities.observation.series.AbstractValuedSeriesObservation
ereporting org.n52.sos.ds.hibernate.entities.observation.ereporting.AbstractValuedEReportingObservation

Observation interface and class

For your new observation value you have to implement an Observation interface and a class for the new observation type.

The definition of the interface for the new observation shall look like this:
package org.n52.sos.ds.hibernate.entities.observation.full;

public interface MyTypeObservation extends MyTypeValuedObservation, Observation<MyType> {

}

The class implementation of the new observation shall look like this:
package org.n52.sos.ds.hibernate.entities.observation.series.full;

public class SeriesMyTypeObservation extends AbstractSeriesObservation<MyType>
        implements MyTypeObservation {

}

The UML of the org.n52.sos.ds.hibernate.entities.observation.series.AbstractSeriesObservation class:

AbstractSeriesObservation.png

The class implementation example is for the series database concept. In the following table you can find the relevant classes for the other database concepts.

database concept super class
old org.n52.sos.ds.hibernate.entities.observation.legacy.AbstractLegacyObservation
series org.n52.sos.ds.hibernate.entities.observation.series.AbstractSeriesObservation
ereporting org.n52.sos.ds.hibernate.entities.observation.ereporting.AbstractEReportingObservation

Visitor interfaces

Finally, you have to add your new ValuedObservation and Observation to the following interfaces:
  • org.n52.sos.ds.hibernate.entities.observation.ValuedObservationVisitor
  • org.n52.sos.ds.hibernate.entities.observation.VoidValuedObservationVisitor
  • org.n52.sos.ds.hibernate.entities.observation.ObservationVisitor
  • org.n52.sos.ds.hibernate.entities.observation.VoidObservationVisitor

And in the implementations of these interfaces you have to implement the new methods. Currently, the following classes implements these interfaces:

  • org.n52.sos.ds.hibernate.util.observation.ObservationValueCreator
  • org.n52.sos.ds.hibernate.util.observation.SweAbstractDataComponentCreator

Converter

Another approach, without creating new database tables, is to convert the created observation to the requested. Here the 52N SOS creates the default internal representation of the observation from the database tables and in the converter the observation is converted to the requested observation type.

RequestResponseModifier

The converter shall be an implementation of the org.n52.sos.convert.RequestResponseModifier interface which is listed below. To load this converter, the ServiceLoader concept is also used and you have to define a org.n52.sos.convert.RequestResponseModifier file in the src/main/resources/META-INF/services that contains the package and class name of the implemented converter.

The UML of the org.n52.sos.convert.RequestResponseModifier interface:

RequestResponeModifier.png

RequestResponseModifierFacilitator

The org.n52.sos.convert.RequestResponseModifierFacilitator is an indicator for the logic of the 52N SOS in which order the RequestResponseModifier shall be executed.

The UML of the org.n52.sos.convert.RequestResponseModifierFacilitator class:

RequestResponeModifierFacilitator.png

The indicators are:

indicator description
merger Merges single observations to an observation that contains the values, e.g. single values to OM_SweArrayObservation
splitter Splits the values of an observation into single observation, e.g. a OM_SweArrayObservation into single OM_Measurements
adderRemover Adds or removes values to/from the response/request. E.g. the FlexibleIdentifierModifier

ObservationMerger

The org.n52.sos.ogc.om.ObservationMerger class merges single observations to an observation with teh same indicators which are defined in the org.n52.sos.ogc.om.ObservationMergeIndicator.

The UML of the org.n52.sos.ogc.om.ObservationMergeIndicator class:

ObservationMergeIndicator.png

ObservationMergeIndicator

The org.n52.sos.ogc.om.ObservationMergeIndicator defines the objects which shall be the same in the observations that shall be merged.

The UML of the org.n52.sos.ogc.om.ObservationMergeIndicator class:

ObservationMergeIndicator.png

indicator description
procedure Merges all observations with the same procedure
observableProperty Merges all observations with the same observableProperty
featureOfInterest Merges all observations with the same featureOfInterest
offerings Merges all observations with the same offering
phenomenonTime Merges all observations with the same phenomenonTime
resultTime Merges all observations with the same resultTime
samplingGeometry Merges all observations with the same samplingGeometry
observationConstellation Merges all observations with the same combination of procedure, observableProperty, featureOfInterest and offerings. This is the default of the static method defaultObservationMergerIndicator()

Example implementation of a converter

Since the 52N SOS 4.4 you can find the org.n52.sos.converter.InspireObservationResponseConverter in the inspire-code module.

Implement a new Encoder

See Encoder documentation

Features

Exceptions

If you need to throw own exceptions, please take a look at the org.n52.sos.ogc.ows.OwsExceptionReport hierarchie and according packages in the api module:
  • org.n52.sos.exception
    • ows
      • concrete
    • sos
      • concrete
    • swes

Capabilities Cache

  • Cache creation and updating is managed by CapabilitiesCacheController
  • At startup, the cache controller initializes the cache creation
  • PICK The cache stores the following information
    • Offerings
    • Resources
    • Relations between resources
Updating
  • Current procedure uses always the database for updating and creation
  • Updates are caused by
    • Timer (@see: configuration property CAPABILITIES_CACHE_UPDATE_INTERVAL [in minutes])
    • Transactional operations:
      • InsertObservation
      • InsertSensor
      • DeleteSensor
      • DeleteObservation (→ is an not published extension)
      • InsertResultTemplate
  • DB cache update actions class hierarchy:
    Actions.png

Setting Management

Declaring Settings

To let the SOS recognize Settings the interface org.n52.sos.config.SettingDefinitionProvider has to be implemented. It provides SettingDefinitions that are used to generate the user interface and to save settings in a type safe manner. Currently settings for File, Boolean, Integer, Double, String and URI are implemented (see the corresponding SettingDefintion subclasses in org.n52.sos.config.settings. Settings are identified by a key which has to be unique. If you're developing an extension use a vendor prefix to be sure you don't get a conflict with other settings. The =SettingDefinitionProvider=s are loaded with the Java ServiceLoader API, so be sure to declare your provider. It is important that providers are not classes that are loaded by the configurator as they have to be instantiated earlier in the initialization process of the service.

Example for a setting provider:

package org.n52.sos.extension;

import java.util.Collections;
import java.util.Set;

import org.n52.sos.config.SettingDefinition;
import org.n52.sos.config.SettingDefinitionProvider;
import org.n52.sos.config.settings.IntegerSettingDefinition;
import org.n52.sos.service.ServiceSettings;

public class ExtensionSettings implements SettingDefinitionProvider {

   public static final String SETTING_KEY = "extension.aSetting";

   public static final IntegerSettingDefinition SETTING_DEFINITION = new IntegerSettingDefinition()
      .setGroup(ServiceSettings.GROUP)
      .setOrder(8)
      .setKey(SETTING_KEY)
      .setMinimum(1)
      .setDefaultValue(5)
      .setTitle("The readable title of the setting")
      .setDescription("This is a description text for the user.");

   @Override
   public Set<SettingDefinition<?, ?>> getSettingDefinitions() {
      return Collections.singleton(SETTING_DEFINITION)
   }
}

Settings are grouped in tabs for the UI. These groups can be configured by setting the group property of the setting definition to a appropriate SettingDefinitionGroup. You can use the predefined groups or create a new group. Existing groups are:
  • org.n52.sos.ogc.ows.SosServiceIdentificationFactorySettings.GROUP
  • org.n52.sos.ogc.ows.SosServiceProviderFactorySettings.GROUP
  • org.n52.sos.service.MiscSettings.GROUP
  • org.n52.sos.service.ServiceSettings.GROUP
Groups and settings are orderd in the UI to provide a consistent view of the settings. Both SettingDefinition and SettingDefinitionGroup are implenting Ordered to provode such a ordering based on floats. This way additional settings can be inserted at any place in the UI.

Using settings

Settings can be requested, modified and deleted with the corresponding methods in org.n52.sos.config.SettingsManager. But most classes shalln't use the SettingsManager directly as settings can be injected directly into your instances. This works for all classes loaded by the Configurator, a subclass of AbstractConfiguringServiceLoaderRepository or a ConfiguringSingletonServiceLoader. These classes are automatically registered at the SettingsManager but you also can register your object instance manually by calling SettingsManager.getInstance().configure(object).

The injecting of settings is annotation based. Classes that want to receive settings have to be annotated with org.n52.sos.config.annotation.Configurable. Setters for settings have to be annotated with org.n52.sos.config.annotation.Setting with the corresponding setting key as an argument. Setters are expected to throw an ConfigurationException if the supplied value is invalid, be sure to include a human readable error message as the exception is forwarded to the user. If an exception is thrown the old value of the setting will be reapplied.

Example for a setting receiving class:
package org.n52.sos.extension;

import org.n52.sos.config.annotation.Configurable;
import org.n52.sos.config.annotation.Setting;
import org.n52.sos.service.ConfigurationException;
import org.n52.sos.util.Validation;

@Configurable
public abstract class SettingReceiver {

   private int setting;

   @Setting(ExtensionSettings.SETTING_KEY)
   public void setUpdateInterval(int setting) throws ConfigurationException {
      Validation.greaterZero("Extension Setting", setting);
      this.setting = setting;
   }
}

Once registered the instance receives updates of the setting everytime it changes but it won't prevent the finalization of the instance by the garbage collector.

Testing

This section describes how to write tests (JUnit) and use the SOS concepts in the tests.

De-/Encoder

To initiate the de-/encoders you have to add this to your test class:
    1	@BeforeClass
    2	public final static void initDecoders() {
    3	   CodingRepository.getInstance();
    4	}

Settings Managment

For some tests a SettingsManager implementation is required. The sqlite-config SOS module/arartifact provides a SettingsManager for testing ( org.n52.sos.config.sqlite.SQLiteSettingsManagerForTesting) which can be used.

If the a SettingsManager implementation is required, add these dependencies to the module pom.xml file:
    1	<dependency>
    2	   <groupId>${project.groupId}</groupId>
    3	   <artifactId>test</artifactId>
    4	   <scope>test</scope>
    5	</dependency>
    6	<dependency>
    7	   <groupId>${project.groupId}</groupId>
    8	   <artifactId>sqlite-config</artifactId>
    9	   <scope>test</scope>
   10	</dependency>
   11	<dependency>
   12	   <groupId>${project.groupId}</groupId>
   13	   <artifactId>sqlite-config</artifactId>
   14	   <type>test-jar</type>
   15	   <scope>test</scope>
   16	</dependency>

In some cases it is required to instantiate (BeforeClass) and cleanup (AfterClass) the SettingsManager implementation:
    1	@BeforeClass
    2	public static void initSettingsManager() {
    3	   SettingsManager.getInstance();
    4	}
    5	
    6	@AfterClass
    7	public static void cleanupSettingManager() {
    8	   SettingsManager.getInstance().cleanup();
    9	}

Extra JSP content for custom builds

In certain cases custom builds of the SOS may want to include extra content in the webapp (for instance extra admin menu items or title content). To facilitate this, the main build can check for the existence of optional jsp files and include them if present. Custom builds can create these custom jsp files to add additional content.

Path Content
WEB-INF/views/common/extra-admin-menu-items.jsp Add extra menu items to the admin menu (use <li> list items)
WEB-INF/views/common/exta-title.jsp Add extra title content (on every page) above normal title

Development environment

Tomcat context

Instead of building and deploying a .war-file to test your SOS you can also set the context of a webapp to the target folder of the 52n-sos-webapp module within a Tomcat installation of the development machine. Then you only have to package the project to update the application.

  • Run mvn package in the SOS project root
  • Stop your Tomcat
  • If you use Eclisep: Disable "automatic build".
  • Open \conf\Catalina\localhost
  • Create a file 52n-sos-webapp.xml (as administrator)
  • Copy and paste the following contents to the file and insert the full path to the target directory of your development workspace:
    <Context docBase="<your path>\SOS\webapp\target" />
  • Start the Tomat
  • Go to http://localhost:8080/52n-sos-webapp
During the development, the following steps will update the application.

  • Stop Tomcat
  • Run mvn package (optionally with -DskipTests and -Pdevelop)
  • Start Tomcat

Remote Debugging

In the documentation wiki you can find Eclipse tips and including remote debugging
I Attachment Action Size Date Who Comment
AbstracValuedSeriesObservation.pngpng AbstracValuedSeriesObservation.png manage 8 K 10 May 2016 - 08:12 CarstenHollmann  
AbstractOperationDAO.pngpng AbstractOperationDAO.png manage 21 K 04 Apr 2013 - 11:49 EikeJuerrens org.n52.sos.ds.AbstractOperationDAO
AbstractRequestOperator.pngpng AbstractRequestOperator.png manage 77 K 04 Apr 2013 - 11:45 EikeJuerrens org.n52.sos.request.operator.AbstractRequestOperator
AbstractSeriesObservation.pngpng AbstractSeriesObservation.png manage 10 K 10 May 2016 - 08:12 CarstenHollmann  
AbstractSpecificXmlEncoder.pngpng AbstractSpecificXmlEncoder.png manage 10 K 10 May 2016 - 08:12 CarstenHollmann  
AbstractXmlEncoder.pngpng AbstractXmlEncoder.png manage 8 K 10 May 2016 - 08:12 CarstenHollmann  
Actions.pngpng Actions.png manage 154 K 06 Feb 2013 - 13:52 EikeJuerrens DB cache update actions class hierarchy
Binding.pngpng Binding.png manage 58 K 04 Apr 2013 - 07:10 EikeJuerrens abstract class org.n52.sos.binding.Binding
DB_Modell_20.pngpng DB_Modell_20.png manage 286 K 12 Apr 2013 - 12:09 CarstenHollmann SOS 4.0.0 database model
DB_Modell_20.xmlxml DB_Modell_20.xml manage 119 K 12 Apr 2013 - 12:10 CarstenHollmann SOS 4.0.0 database model xml description for DBDesigner.
Decoder.pngpng Decoder.png manage 11 K 04 Apr 2013 - 11:52 EikeJuerrens org.n52.sos.decode.Decoder
Encoder.pngpng Encoder.png manage 16 K 04 Apr 2013 - 11:59 EikeJuerrens org.n52.sos.encode.Encoder
EncoderKeyOverview.pngpng EncoderKeyOverview.png manage 91 K 10 May 2016 - 08:12 CarstenHollmann  
JSONEncoder.pngpng JSONEncoder.png manage 17 K 10 May 2016 - 08:12 CarstenHollmann  
ObservationEncoder.pngpng ObservationEncoder.png manage 6 K 10 May 2016 - 08:12 CarstenHollmann  
ObservationMergeIndicator.pngpng ObservationMergeIndicator.png manage 20 K 10 May 2016 - 08:12 CarstenHollmann  
ObservationMerger.pngpng ObservationMerger.png manage 18 K 10 May 2016 - 08:12 CarstenHollmann  
OperationDAO.pngpng OperationDAO.png manage 9 K 04 Apr 2013 - 11:32 EikeJuerrens org.n52.sos.ds.OperationDAO
ProcedureCoder.pngpng ProcedureCoder.png manage 4 K 10 May 2016 - 08:12 CarstenHollmann  
ProcedureEncoder.pngpng ProcedureEncoder.png manage 1 K 10 May 2016 - 08:13 CarstenHollmann  
RequestOperator.pngpng RequestOperator.png manage 16 K 04 Apr 2013 - 11:25 EikeJuerrens org.n52.sos.request.operator.RequestOperator
RequestResponeModifier.pngpng RequestResponeModifier.png manage 6 K 10 May 2016 - 08:13 CarstenHollmann  
RequestResponeModifierFacilitator.pngpng RequestResponeModifierFacilitator.png manage 9 K 10 May 2016 - 08:13 CarstenHollmann  
RequestResponeModifierKeyType.pngpng RequestResponeModifierKeyType.png manage 31 K 10 May 2016 - 08:13 CarstenHollmann  
SOS_4_db_core.pngpng SOS_4_db_core.png manage 102 K 18 Jul 2013 - 13:36 CarstenHollmann SOS 4.0.0 Core Database Model
SOS_4_db_transactional.pngpng SOS_4_db_transactional.png manage 134 K 18 Jul 2013 - 13:37 CarstenHollmann SOS 4.0.0 Transactional Database Model
StreamingEncoder.pngpng StreamingEncoder.png manage 2 K 10 May 2016 - 08:13 CarstenHollmann  
Value.pngpng Value.png manage 4 K 10 May 2016 - 08:13 CarstenHollmann  
ValueVisitor.pngpng ValueVisitor.png manage 22 K 10 May 2016 - 08:13 CarstenHollmann  
ValuedObservation.pngpng ValuedObservation.png manage 2 K 10 May 2016 - 08:13 CarstenHollmann  
Topic revision: r42 - 06 Mar 2020, CarstenHollmann
Legal Notice | Privacy Statement


This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki? Send feedback