Configuration

section under revision/update

The Web interface is intended and prepared for straight integration with Spring MVC. However, using Spring as Web framework is not required but leaves open the last meters of integrating the endpoint controllers.

make links to Spring MVC and to endpoint controllers.

All endpoints are annotated with Spring’s annotations Connector, RequestMethod, RequestMapping, etc. What is needed is a configured DispatcherServlet in the web.xml and proper beans injections.

Check if/how we can provide a pre-configured Configuration lib or class which can be changed/extended by Web application implementors.

Configuration Locations

  • WEB-INF/classes/config-general.properties
  • WEB-INF/spring/
  • WEB-INF/classes/application.properties
  • WEB-INF/web.xml (cors etc.)

General Configuration

Query Parameter Defaults

Query parameter defaults can be changed under WEB-INF/classes/config-general.json. For example

{
  "timeformat": "YYYY-MM-dd, HH:mm",
  "generalizer": {
    "defaultGeneralizer": "lttb",
    "noDataGapThreshold": 5
  },
  "grid": false
}

Changing API defaults may lead to unexpected results to Web clients. Only change defaults if you know what you are doing.

Enabling CORS

CORS can be enabled via a third party filter (to stay independend from a concrete Servlet container which may not ship such filter) within a Web application’s web.xml file. A simple example (which allows all requests) may look like this:

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
    <init-param>
        <param-name>cors.allowOrigin</param-name>
        <param-value>*</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowGenericHttpRequests</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>cors.supportedMethods</param-name>
        <param-value>GET, POST, HEAD, PUT, DELETE, OPTIONS</param-value>
    </init-param>
    <init-param>
        <param-name>cors.supportedHeaders</param-name>
        <param-value>Content-Type, Content-Encoding, Accept</param-value>
    </init-param>
    <init-param>
        <param-name>cors.exposedHeaders</param-name>
        <param-value>Content-Type, Content-Encoding</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CORS</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

You can test your CORS config if configured correctly.

Managing Configuration

Separate Properties

It is helpful to separate all properties values from XML configuration for several reasons. First, it may be tedious to find all single properties within verbose XML. However, more it also very important to keep sensitive information (like database configuration) from the project itself.

Create a WEB-INF/classes/application.properties file which will keep all the default values. Another may be located under your home directory. Configure the following which will first take parameters

<!-- these properties do override default settings -->
<ctx:property-placeholder 
    <!-- ${user.home}/application.properties} when -Dlocal.configFile not defined -->
    location="${local.configFile:${user.home}/application.properties}"
    ignore-resource-not-found="true" ignore-unresolvable="true" order="0" />

<!-- default settings -->
<ctx:property-placeholder 
    location="classpath:/application.properties"
    ignore-resource-not-found="false" ignore-unresolvable="false" order="1" />

A placeholder can now be declared within Spring XML files via ${placeholder:default}. If present in the application properties file (your one or the default) it will be replaced, otherwise the given default will be used.

Separate Configuration Sections

To keep overview we can separate parts of the configuration files and include them via file import, e.g. <import resource="mvc.xml" />.

Configuring Web Layer

The Web Layer can be configured by Spring MVC controllers served by a dispatcher servlet (configured in web.xml). Configuration includes content negotiation, serialization config, and Web controller injections.

Dispatcher Servlet

This describes an example configuration via Spring. There are lots of variants and alternatives which may end in the same result. This example splits Spring configuration files into two main files:

  • /WEB-INF/spring/dispatcher-servlet.xml contains Web controllers and views
  • /WEB-INF/spring/application-context.xml contains SPI implementations

However, everything starts by adding Spring’s DispatcherServlet within the web.xml, put both configuration files, and relate it to some context path like so:

<servlet>
  <servlet-name>api-dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>/WEB-INF/spring/dispatcher-servlet.xml,/WEB-INF/spring/application-context.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>api-dispatcher</servlet-name>
  <url-pattern>/api/*</url-pattern>
</servlet-mapping>

Content Negotiation

To support proper content negotiation and JSON serialization the following should be added to the /WEB-INF/spring/dispatcher-servlet.xml:

<mvc:annotation-driven />
<ctx:annotation-config />

<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
    <property name="serializationInclusion" value="NON_NULL" />
</bean>

<bean id="jsonViewResolver" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
    <property name="extractValueFromSingleKeyModel" value="true" />
    <property name="objectMapper" ref="objectMapper" />
</bean>

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="defaultContentType" value="application/json" />
</bean>

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="defaultViews">
        <util:list>
            <ref bean="jsonViewResolver" />
        </util:list>
    </property>
</bean>

Web Controller injections

A Web controller serves an endpoint and behaves like described in the Web API. It performs I/O operations (graph rendering, generalization, etc.) if neccessary.

A Web controller delegates data request to the actual SPI implementation so it has to be referenced here. SPI implementors have to use these references to make sure the right backend service is called.

<mvc:annotation-driven />
<ctx:annotation-config />

<!--
    This bean description file injects the Web binding layer. SPI implementation 
    beans have to match the ref-ids associated below.
-->

<bean class="org.n52.web.ctrl.ResourcesController">
    <property name="metadataService" ref="metadataService" />
</bean>

<bean class="org.n52.web.ctrl.SearchController">
    <property name="searchService" ref="searchService"/>
</bean>

<!-- a parent controller configuration -->
<bean class="org.n52.web.ctrl.ParameterController" id="parameterController" abstract="true">
    <!-- ${external.url} property is configured in an external properties file (see above) -->
    <property name="externalUrl" value="${external.url}" />
    <property name="metadataExtensions">
        <list>
            <bean class="org.n52.io.response.extension.LicenseExtension" />
        </list>
    </property>
</bean>

<bean class="org.n52.web.ctrl.OfferingsParameterController" parent="parameterController">
    <property name="parameterService">
        <bean class="org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter">
            <constructor-arg index="0" ref="offeringParameterService" />
        </bean>
    </property>
</bean>

<bean class="org.n52.web.ctrl.ServicesParameterController" parent="parameterController">
    <property name="parameterService">
        <bean class="org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter">
            <constructor-arg index="0" ref="serviceParameterService" />
        </bean>
    </property>
</bean>

<bean class="org.n52.web.ctrl.CategoriesParameterController" parent="parameterController">
    <property name="parameterService">
        <bean class="org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter">
            <constructor-arg index="0" ref="categoryParameterService" />
        </bean>
    </property>
</bean>

<bean class="org.n52.web.ctrl.FeaturesParameterController" parent="parameterController">
    <property name="parameterService">
        <bean class="org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter">
            <constructor-arg index="0" ref="featureParameterService" />
        </bean>
    </property>
</bean>

<bean class="org.n52.web.ctrl.ProceduresParameterController" parent="parameterController">
    <property name="parameterService">
        <bean class="org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter">
            <constructor-arg index="0" ref="procedureParameterService" />
        </bean>
    </property>
</bean>

<bean class="org.n52.web.ctrl.PhenomenaParameterController" parent="parameterController">
    <property name="parameterService">
        <bean class="org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter">
            <constructor-arg index="0" ref="phenomenonParameterService" />
        </bean>
    </property>
</bean>

<bean class="org.n52.web.ctrl.PlatformsParameterController" parent="parameterController">
    <property name="parameterService" ref="platformParameterService" />
    <property name="metadataExtensions">
        <list merge="true">
            <bean class="org.n52.io.extension.parents.HierarchicalParameterExtension">
                <property name="service" ref="hierarchicalParameterService" />
            </bean>
        </list>
    </property>
</bean>

<bean class="org.n52.web.ctrl.GeometriesController" parent="parameterController">
    <property name="parameterService" ref="geometriesService" />
</bean>

<bean class="org.n52.web.ctrl.DatasetController" parent="parameterController">
    <property name="parameterService" ref="datasetService" />
    <property name="metadataExtensions">
        <list merge="true">
            <bean class="org.n52.io.extension.RenderingHintsExtension" />
            <bean class="org.n52.io.extension.StatusIntervalsExtension" />
            <bean class="org.n52.io.extension.resulttime.ResultTimeExtension">
                <property name="service" ref="resultTimeService" />
            </bean>
            <!-- Using DatabaseMetadataExtension requires some preparation work. -->
            <!--<bean class="org.n52.io.extension.metadata.DatabaseMetadataExtension" />-->
        </list>
    </property>
</bean>

<bean class="org.n52.web.ctrl.DataController">
    <property name="dataService" ref="datasetService" />
    <property name="datasetService" ref="datasetService" />
    <property name="preRenderingTask" ref="preRenderingJob" />
    <property name="requestIntervalRestriction" value="${request.interval.restriction}" />
</bean>

Known application properties are Some things to note:

  • ${external.url} and ${request.interval.restriction} are property placeholders defined in an extra application properties file
  • org.n52.web.ctrl.ParameterBackwardsCompatibilityAdapter is a backwards compatibility wrapper
  • metadataExtensions list contains extensions which adds further metadata to /<endpoint>/extras

General Properties

Configurable properties are

  • requestIntervalRestriction: sets the maximum time period a clients can query data for, e.g. P380D
  • externalUrl: sets the external URL under which the API can be accessed by clients, e.g. (https://example.com/my-api/)

Extensions

See Extension section for how to configure extensions in detail.

Common Utils

<bean class="org.n52.series.db.dao.DefaultDbQueryFactory">
  <property name="databaseSrid" value="${database.srid}" />
</bean>

<bean class="org.n52.series.db.da.EntityCounter" />
<bean id="metadataService" class="org.n52.series.srv.CountingMetadataAccessService" />
<bean id="searchService" class="org.n52.series.srv.Search" />

<bean class="org.n52.series.db.da.DefaultDataRepositoryFactory" />
<bean class="org.n52.io.DefaultIoFactory" />

Static Service Entity

In case of a unique data backend a static service entity can be defined via Spring bean. Here’s an example

<bean class="org.n52.series.db.beans.ServiceEntity">
  <property name="pkid" value="1" />
  <property name="version" value="2.0" />
  <property name="name" value="My Dataset Service" />
  <property name="noDataValues" value="-9999.0,99999,NO_DATA" />
</bean>