Uploaded image for project: '[Read Only] - Hippo Essentials'
  1. [Read Only] - Hippo Essentials
  2. ESSENTIALS-1104

When Custom REST APIs are added, the default jaxrsRestAllEntityProviders is ignored and replaced

    XMLWordPrintable

Details

    • Bug
    • Status: Closed
    • Normal
    • Resolution: Fixed
    • 4.0.0
    • 4.1.0
    • None

    Description

      When you add a custom plain JAX-RS services using Essentials tool, it adds an overlay XML like the following:

      site/src/main/resources/META-INF/hst-assembly/overrides/spring-plain-rest-api.xml
        <bean id="essentialsRestAllEntityProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
          <property name="sourceList">
            <list>
              <ref bean="jaxrsRestJsonProvider"/>
              <ref bean="jaxrsHippoContextProvider"/>
              <ref bean="jaxrsRestExceptionMapper"/>
            </list>
          </property>
        </bean>
      
        <bean id="jaxrsRestPlainServiceValve" class="org.hippoecm.hst.core.container.JaxrsRestServiceValve"
              init-method="initialize" destroy-method="destroy">
          <constructor-arg>
            <bean class="org.hippoecm.hst.jaxrs.cxf.CXFJaxrsService">
              <constructor-arg value="jaxrsPlainService"/>
              <property name="servletPath" value=""/>
              <property name="jaxrsServerFactoryBean">
                <bean class="org.apache.cxf.jaxrs.JAXRSServerFactoryBean">
                  <property name="address" value="/"/>
                  <property name="providers" ref="essentialsRestAllEntityProviders"/>
                  <property name="resourceProviders" ref="jaxrsRestPlainResourceProviders"/>
                  <property name="invoker" ref="jaxrsRestPlainServiceInvoker"/>
                </bean>
              </property>
              <property name="inInterceptors">
                <list>
                  <ref bean="jaxrsRestPlainServiceQueryStringReplacingInterceptor"/>
                </list>
              </property>
            </bean>
          </constructor-arg>
          <property name="valveName" value="jaxrsRestPlainServiceValve"/>
        </bean>
      

      The overridden jaxrsRestPlainServiceValve bean above has only one difference from the default jaxrsRestPlainServiceValve defined in HST's SpringComponentManager-rest-plain-pipeline.xml, which is just replacing the default jaxrsRestAllEntityProviders by the essentialsRestAllEntityProviders list bean for the JAXRSServerFactoryBean.

      And the reason why this overriding is to add those two new entity providers:

      site/src/main/resources/META-INF/hst-assembly/overrides/spring-plain-rest-api.xml
        <!--
          JAXB CONTEXT MAPPINGS
        -->
        <bean id="jaxrsHippoContextProvider" class="org.onehippo.cms7.essentials.components.rest.JaxbContextProvider">
          <property name="beansPackage" value="org.onehippo.cms7.apichannel.demo.beans"/>
          <!--
            you can add extra classes if needed as followed:
          -->
          <!--
            <property name="classes">
              <list>
                <value type="java.lang.Class">org.example.beans.MyBean</value>
              </list>
            </property>
          -->
        </bean>
      
        <bean id="jaxrsRestExceptionMapper" class="org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper">
          <property name="printStackTrace" value="false" />
        </bean>
      

      Overriding itself is fine here in the Essentials level, but the problem is that the way how it overrides can miss any additional default entity providers in HST version upgrades.
      For example, the default jaxrsRestDefaultEntityProviders (that is combined by jaxrsRestAllEntityProviders and has been assumed that those must be applied everywhere in HST) list bean in HST is defined as follows:

      SpringComponentManager-rest-jackson.xml
        <!-- 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"/>
              <ref bean="jaxrsParametersInfoProviderContextProvider" />
            </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.hippoecm.hst.site.container.TypeDeterminedMethodInvokingFactoryBean">
          <constructor-arg value="java.util.List" />
          <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>
      

      In a new version, the jaxrsRestDefaultEntityProviders starts including the new jaxrsParametersInfoProviderContextProvider as of HSTTWO-4133. However, that HST's default entity providers are ignored and totally overwritten in Essentials.

      To avoid this situation, in the Essentials' configuration, it is strongly desirable to combine jaxrsRestDefaultEntityProviders with Essentials' custom ones. Otherwise, the default plain JAX-RS framework can easily be broken.

      Therefore, the spring-plain-rest-api.xml in Essentials' template files must be corrected like the following example:

      site/src/main/resources/META-INF/hst-assembly/overrides/spring-plain-rest-api.xml
        <bean id="essentialsRestEntityProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
          <property name="sourceList">
            <list>
              <ref bean="jaxrsHippoContextProvider"/>
              <ref bean="jaxrsRestExceptionMapper"/>
            </list>
          </property>
        </bean>
      
        <bean id="essentialsRestAllEntityProviders" class="org.hippoecm.hst.site.container.TypeDeterminedMethodInvokingFactoryBean">
          <constructor-arg value="java.util.List" />
          <property name="targetClass" value="org.apache.commons.collections.ListUtils" />
          <property name="targetMethod" value="union" />
          <property name="arguments">
            <list>
              <ref bean="jaxrsRestDefaultEntityProviders" />
              <ref bean="essentialsRestEntityProviders" />
            </list>
          </property>
        </bean>
      

      This way, Essentials can keep the fundamental default entity providers (which must be applied to anywhere) without losing anything and its own custom ones.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              wko Woonsan Ko (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: