Using JAXB Annotations in REST services

516 views
Skip to first unread message

Stefan Siprell

unread,
Sep 18, 2014, 6:59:34 AM9/18/14
to hippo-c...@googlegroups.com
Hey guys,
it's me again. We are currently implementing Hippo CMS Services. We are using Jackson annotations to map a list of abstract types to concrete classes:

package de.einsundeins.hippo.restapi;

import org.codehaus.jackson.annotate.JsonSubTypes;
import org.codehaus.jackson.annotate.JsonTypeInfo;

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=UnlayoutedCheckBox.class, name="checkbox"),
@JsonSubTypes.Type(value=UnlayoutedInput.class, name="input"),
@JsonSubTypes.Type(value=UnlayoutedOption.class, name="option"),
@JsonSubTypes.Type(value=UnlayoutedRadio.class, name="radio"),
@JsonSubTypes.Type(value=UnlayoutedSelect.class, name="select")
})
public abstract class UnlayoutedField {

protected String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

}

Using the native objectmapper, this works flawlessy. If we use the same configuration in hipppo we get following exception: 

18.09.2014 12:34:11 WARN  http-bio-8080-exec-3 [WebApplicationExceptionMapper.toResponse:73] javax.ws.rs.WebApplicationException: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of de.einsundeins.hippo.restapi.UnlayoutedField, problem: abstract types can only be instantiated with additional type information

at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1@2f2f7204; line: 7, column: 17] (through reference chain: de.einsundeins.hippo.restapi.CampaignRepresentation["form"]->de.einsundeins.hippo.restapi.UnlayoutedForm["modules"]->de.einsundeins.hippo.restapi.UnlayoutedFormModule["fields"])

                at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:247)

                at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:91)

                at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)

                at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)

                at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:237)

                at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:239)

                at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:213)

                at org.hippoecm.hst.jaxrs.cxf.CXFJaxrsService.invoke(CXFJaxrsService.java:147)

                at org.hippoecm.hst.core.container.JaxrsRestServiceValve.invoke(JaxrsRestServiceValve.java:38)

                at org.hippoecm.hst.core.container.HstSitePipeline$Invocation.invokeNext(HstSitePipeline.java:271)

                at org.hippoecm.hst.core.container.GenericResponseHeadersValve.invoke(GenericResponseHeadersValve.java:83)

                at org.hippoecm.hst.core.container.HstSitePipeline$Invocation.invokeNext(HstSitePipeline.java:271)

                at org.hippoecm.hst.core.container.SubjectBasedSessionValve.invoke(SubjectBasedSessionValve.java:58)

                at org.hippoecm.hst.core.container.HstSitePipeline$Invocation.invokeNext(HstSitePipeline.java:271)

                at org.hippoecm.hst.core.container.SecurityValve.invoke(SecurityValve.java:168)

                at org.hippoecm.hst.core.container.HstSitePipeline$Invocation.invokeNext(HstSitePipeline.java:271)

                at org.hippoecm.hst.core.container.CmsSecurityValve.invoke(CmsSecurityValve.java:80)

                at org.hippoecm.hst.core.container.HstSitePipeline$Invocation.invokeNext(HstSitePipeline.java:271)

                at org.hippoecm.hst.core.container.InitializationValve.invoke(InitializationValve.java:72)

                at org.hippoecm.hst.core.container.HstSitePipeline$Invocation.invokeNext(HstSitePipeline.java:271)

                at org.hippoecm.hst.core.container.HstSitePipeline.invokeValves(HstSitePipeline.java:170)

                at org.hippoecm.hst.core.container.HstSitePipeline.invoke(HstSitePipeline.java:153)

                at org.hippoecm.hst.core.container.HstRequestProcessorImpl.processRequest(HstRequestProcessorImpl.java:79)

                at org.hippoecm.hst.container.HstFilter.processResolvedSiteMapItem(HstFilter.java:840)

                at org.hippoecm.hst.container.HstFilter.doFilter(HstFilter.java:468)

                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

                at org.hippoecm.hst.container.XSSUrlFilter.doFilter(XSSUrlFilter.java:49)

                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

                at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)

                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

                at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176)

                at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)

                at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)

                at org.onehippo.forge.rewriting.HippoRewriteFilter.doFilter(HippoRewriteFilter.java:291)

                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)

                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)

                at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)

                at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)

                at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)

                at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)

                at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)

                at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)

                at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)

                at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)

                at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)

                at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

                at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)

                at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

                at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

                at java.lang.Thread.run(Thread.java:744)

Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of de.einsundeins.hippo.restapi.UnlayoutedField, problem: abstract types can only be instantiated with additional type information

at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1@2f2f7204; line: 7, column: 17] (through reference chain: de.einsundeins.hippo.restapi.CampaignRepresentation["form"]->de.einsundeins.hippo.restapi.UnlayoutedForm["modules"]->de.einsundeins.hippo.restapi.UnlayoutedFormModule["fields"])

                at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)

                at org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.java:233)

                at org.codehaus.jackson.map.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:60)




We can provoke the same exception in our unit-tests by removing the Annotations. This lets us think, that the annotations are ignored.

We did some digging around and found the SpringComponentManager-rest-jackson.xml file, which was using the org.codehaus.jackson.xc.JaxbAnnotationIntrospector. We created our own SpringComponentManager-rest-jackson.xml  and used the org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector instead. The problem is the same, here the complete file:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Copyright 2008-2013 Hippo B.V. (http://www.onehippo.com)

  Licensed under the Apache License, Version 2.0 (the  "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at


  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS"
  BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
  
  <bean id="jaxrsRestJacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper">
  </bean>

  <!-- bean id="jaxrsRestJacksonJaxbIntrospector" class="org.codehaus.jackson.xc.JaxbAnnotationIntrospector" / -->

  <bean id="jacksonRestJacksonJaxbIntrospector" class="org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector" />

  <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject">
      <bean class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
        <property name="targetObject" ref="jaxrsRestJacksonObjectMapper" />
        <property name="propertyPath" value="serializationConfig"/>
      </bean>
    </property>
    <property name="targetMethod" value="setAnnotationIntrospector"/>
    <property name="arguments">
      <list>
        <ref bean="jacksonRestJacksonJaxbIntrospector" />
      </list>
    </property>
  </bean>
  
  <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject">
      <bean class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
        <property name="targetObject" ref="jaxrsRestJacksonObjectMapper" />
        <property name="propertyPath" value="deserializationConfig"/>
      </bean>
    </property>
    <property name="targetMethod" value="setAnnotationIntrospector"/>
    <property name="arguments">
      <list>
        <ref bean="jacksonRestJacksonJaxbIntrospector" />
      </list>
    </property>
  </bean>

  <bean id="jaxrsRestJsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider">
    <property name="mapper" ref="jaxrsRestJacksonObjectMapper" />
  </bean>

  <!-- Built-in Entity Providers which are annotated with javax.ws.rs.ext.Provider. -->
  <bean id="jaxrsRestDefaultEntityProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
      <list>
        <ref bean="jaxrsRestJsonProvider"/>
      </list>
    </property>
  </bean>

  <!-- Custom Entity Providers which are annotated with javax.ws.rs.ext.Provider. -->
  <bean id="customJaxrsRestEntityProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
      <list>
      </list>
    </property>
  </bean>

  <bean id="jaxrsRestAllEntityProviders" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass" value="org.apache.commons.collections.ListUtils" />
    <property name="targetMethod" value="union" />
    <property name="arguments">
      <list>
        <ref bean="jaxrsRestDefaultEntityProviders" />
        <ref bean="customJaxrsRestEntityProviders" />
      </list>
    </property>
  </bean>

</beans>





marijan milicevic

unread,
Sep 18, 2014, 8:04:34 AM9/18/14
to hippo-c...@googlegroups.com
Hi Stefan,
you'll need make at least pone implementation of your class and than you could use @XmlSeeAlso [1] annotation..or make class not be abstract...
cheers
marijan


--
Hippo Community Group: The place for all discussions and announcements about Hippo CMS (and HST, repository etc. etc.)
 
To post to this group, send email to hippo-c...@googlegroups.com
RSS: https://groups.google.com/group/hippo-community/feed/rss_v2_0_msgs.xml?num=50
---
You received this message because you are subscribed to the Google Groups "Hippo Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hippo-communi...@googlegroups.com.
Visit this group at http://groups.google.com/group/hippo-community.
For more options, visit https://groups.google.com/d/optout.

Stefan Siprell

unread,
Sep 18, 2014, 8:36:23 AM9/18/14
to hippo-c...@googlegroups.com
Hi,
we considered using the jaxb annotations, but we  are stuck:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=UnlayoutedCheckBox.class, name="checkbox"),
@JsonSubTypes.Type(value=UnlayoutedInput.class, name="input"),
@JsonSubTypes.Type(value=UnlayoutedOption.class, name="option"),
@JsonSubTypes.Type(value=UnlayoutedRadio.class, name="radio"),
@JsonSubTypes.Type(value=UnlayoutedSelect.class, name="select")
})

In JsonTypeInfo we tell Jacksin to use the property "type" as a look-up value for the following JsonSubTypes. @XMLSeeAlso helps to find subclasses, which are not otherwise found. But this won't help us, as JAXB only looks at the root-element name to distringuish subclasses. There seems to be no way to differntiate based on properties. There is a stackoverflow thread about this problem: http://stackoverflow.com/questions/2992234/java-jaxb-unmarshall-xml-to-specific-subclass-based-on-an-attribute

All we need is for "hippo" -the underlying CXF/Jackson configuration- to support the native Jackson annotations. Any leed on this?

Stefan

You received this message because you are subscribed to a topic in the Google Groups "Hippo Community" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/hippo-community/8NrLzUIkkz4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to hippo-communi...@googlegroups.com.



--

Stefan Siprell | Geschäftsführer Karlsruhe | Agile Software Factory  | Consulting
codecentric AG | Zeppelinstr 2 | 76185 Karlsruhe | Deutschland
tel: +49 (0) 721.9595-683 | fax: +49 (0) 721.9595-666 | mobil: +49 (0) 152.01385470

Sitz der Gesellschaft: Solingen | HRB 25917| Amtsgericht Wuppertal
Vorstand: Michael Hochgürtel . Mirko Novakovic . Rainer Vehns
Aufsichtsrat: Patric Fedlmeier (Vorsitzender) . Klaus Jäger . Jürgen Schütz

Diese E-Mail einschließlich evtl. beigefügter Dateien enthält vertrauliche und/oder rechtlich geschützte Informationen. Wenn Sie nicht der richtige Adressat sind oder diese E-Mail irrtümlich erhalten haben, informieren Sie bitte sofort den Absender und löschen Sie diese E-Mail und evtl. beigefügter Dateien umgehend. Das unerlaubte Kopieren, Nutzen oder Öffnen evtl. beigefügter Dateien sowie die unbefugte Weitergabe dieser E-Mail ist nicht gestattet

marijan milicevic

unread,
Sep 18, 2014, 9:12:43 AM9/18/14
to hippo-c...@googlegroups.com
Hi Stefan,

On Thu, Sep 18, 2014 at 2:36 PM, Stefan Siprell <stefan....@codecentric.de> wrote:
Hi,
we considered using the jaxb annotations, but we  are stuck:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=UnlayoutedCheckBox.class, name="checkbox"),
@JsonSubTypes.Type(value=UnlayoutedInput.class, name="input"),
@JsonSubTypes.Type(value=UnlayoutedOption.class, name="option"),
@JsonSubTypes.Type(value=UnlayoutedRadio.class, name="radio"),
@JsonSubTypes.Type(value=UnlayoutedSelect.class, name="select")
})

In JsonTypeInfo we tell Jacksin to use the property "type" as a look-up value for the following JsonSubTypes. @XMLSeeAlso helps to find subclasses, which are not otherwise found. But this won't help us, as JAXB only looks at the root-element name to distringuish subclasses. There seems to be no way to differntiate based on properties. There is a stackoverflow thread about this problem: http://stackoverflow.com/questions/2992234/java-jaxb-unmarshall-xml-to-specific-subclass-based-on-an-attribute

All we need is for "hippo" -the underlying CXF/Jackson configuration- to support the native Jackson annotations. Any leed on this?




we are using very same config in essentials project (copy of org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml   file *without* any changes) [1] 
and with jackson annotations [2]



What happens when you instantiate object mapper and try to serialize/de-serialize your objects e.g.:

ObjectMapper mapper = new ObjectMapper();
YourObj obj = new YourObj();
// etc...
String result = mapper.writeValueAsString(obj);
...


cheers
marijan

marijan milicevic

unread,
Sep 18, 2014, 9:21:10 AM9/18/14
to hippo-c...@googlegroups.com
On Thu, Sep 18, 2014 at 3:12 PM, marijan milicevic <m.mil...@onehippo.com> wrote:
Hi Stefan,

On Thu, Sep 18, 2014 at 2:36 PM, Stefan Siprell <stefan....@codecentric.de> wrote:
Hi,
we considered using the jaxb annotations, but we  are stuck:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
@JsonSubTypes.Type(value=UnlayoutedCheckBox.class, name="checkbox"),
@JsonSubTypes.Type(value=UnlayoutedInput.class, name="input"),
@JsonSubTypes.Type(value=UnlayoutedOption.class, name="option"),
@JsonSubTypes.Type(value=UnlayoutedRadio.class, name="radio"),
@JsonSubTypes.Type(value=UnlayoutedSelect.class, name="select")
})

In JsonTypeInfo we tell Jacksin to use the property "type" as a look-up value for the following JsonSubTypes. @XMLSeeAlso helps to find subclasses, which are not otherwise found. But this won't help us, as JAXB only looks at the root-element name to distringuish subclasses. There seems to be no way to differntiate based on properties. There is a stackoverflow thread about this problem: http://stackoverflow.com/questions/2992234/java-jaxb-unmarshall-xml-to-specific-subclass-based-on-an-attribute

All we need is for "hippo" -the underlying CXF/Jackson configuration- to support the native Jackson annotations. Any leed on this?




we are using very same config in essentials project (copy of org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml   file *without* any changes) [1] 
and with jackson annotations [2]



Stefan Siprell

unread,
Sep 18, 2014, 10:06:53 AM9/18/14
to hippo-c...@googlegroups.com
Hi,
I wanted to double check the version of the libraries used, but I cannot build the latest snapshot from git as te test fail. Could you give me a dump from the dependency report?

Regards

Stefan

marijan milicevic

unread,
Sep 18, 2014, 10:32:55 AM9/18/14
to hippo-c...@googlegroups.com
On Thu, Sep 18, 2014 at 4:06 PM, Stefan Siprell <stefan....@codecentric.de> wrote:
Hi,
I wanted to double check the version of the libraries used, but I cannot build the latest snapshot from git as te test fail. Could you give me a dump from the dependency report?


no errors here and hudson is "green"..
see attached file for dependencies..
cheers,
marijan
deptree.txt

Stefan Siprell

unread,
Sep 19, 2014, 8:52:31 AM9/19/14
to hippo-c...@googlegroups.com
Thanks for the Support Marijan,
I am not sure how the dashboard REST services work, but we are using the Plain Resource Providers. 

If you look at the file org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml you clearly see, that the (De-)Serializers are configred to exclusively use the JaxbAnnotationIntrospector. This is the reason our REST service did not work. We have replaced the import inside the site/META-INF/hst-assembly-overrides/hst-cms-rest.xml to use our own SpringComponentManager-rest-jackson.xml which in turn uses the JacksonAnnotationIntrospector.  In theory this should have worked, it did not, I wrote the forum post :-)

The reason that the problem remained was, that site/META-INF/hst-assembly-overrides/template-composer-rest.xml also imported the org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml, which overwrote our customized JacksonAnnotationIntrospector configuration.  After commenting out the import (it is a bad idea to import spring configurations multiple times anyway), the service worked perfectly fine.

We also found a backward compatible solution for the SpringComponentManager-rest-jackson.xml:

  <bean id="jaxrsRestJacksonPairIntrospector" class="org.codehaus.jackson.map.AnnotationIntrospector" factory-method="pair">
  <constructor-arg><bean class="org.codehaus.jackson.xc.JaxbAnnotationIntrospector"/></constructor-arg>
  <constructor-arg><bean class="org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector"/></constructor-arg>
  </bean>

this introspector first uses the Jaxb Annotations and then fallbacks to Jackson Annotations. We found this on the Jackson Homepage. Perhaps this could be a good idea, for all hippo installations?

Regards

Stefan

marijan milicevic

unread,
Sep 19, 2014, 9:29:38 AM9/19/14
to hippo-c...@googlegroups.com
Hi Stefan,.
On Fri, Sep 19, 2014 at 2:52 PM, Stefan Siprell <stefan....@codecentric.de> wrote:
Thanks for the Support Marijan,
I am not sure how the dashboard REST services work, but we are using the Plain Resource Providers. 

If you look at the file org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml you clearly see, that the (De-)Serializers are configred to exclusively use the JaxbAnnotationIntrospector. This is the reason our REST service did not work. We have replaced the import inside the site/META-INF/hst-assembly-overrides/hst-cms-rest.xml to use our own SpringComponentManager-rest-jackson.xml which in turn uses the JacksonAnnotationIntrospector.  In theory this should have worked, it did not, I wrote the forum post :-)

The reason that the problem remained was, that site/META-INF/hst-assembly-overrides/template-composer-rest.xml also imported the org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml, which overwrote our customized JacksonAnnotationIntrospector configuration.  After commenting out the import (it is a bad idea to import spring configurations multiple times anyway), the service worked perfectly fine.

We also found a backward compatible solution for the SpringComponentManager-rest-jackson.xml:

  <bean id="jaxrsRestJacksonPairIntrospector" class="org.codehaus.jackson.map.AnnotationIntrospector" factory-method="pair">
  <constructor-arg><bean class="org.codehaus.jackson.xc.JaxbAnnotationIntrospector"/></constructor-arg>
  <constructor-arg><bean class="org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector"/></constructor-arg>
  </bean>

this introspector first uses the Jaxb Annotations and then fallbacks to Jackson Annotations. We found this on the Jackson Homepage. Perhaps this could be a good idea, for all hippo installations?

nice one!
I've made an issue for this [1],
cheers
marijan

Reply all
Reply to author
Forward
0 new messages