Search code examples
xmlspringjaxbmoxyendpoint

Jaxb specify Marshaller for Endpoint


I am using the Spring framework.

I have multiple mashallers and multiple endpoints. The Jaxb context for my marshallers are defined with xml-binding files (using the eclipselink-oxm-xml jaxbContextProperties) since I have multiple marshalling tasks. Using annotations was not a good solution because the same object requires a different marshalling/unmarshalling procedure depending on endpoint.

My configuration at the moment for a single JaxbMarshaller and Endpoint looks like so:

<sws:annotation-driven marshaller="marshallerA" unmarshaller="marshallerA"/>        

    <bean id="loggingInterceptor"
        class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>


    <bean id="messageReceiver"
        class="org.springframework.ws.soap.server.SoapMessageDispatcher">
        <property name="endpointAdapters">
            <list>
                <ref bean="defaultMethodEndpointAdapter" />
            </list>
        </property>
    </bean> 

    <bean id="defaultMethodEndpointAdapter" 
        class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
        <property name="methodReturnValueHandlers">
            <list>
                <ref bean="marshallingPayloadMethodProcessor"/>
            </list>
        </property>
        <property name="methodArgumentResolvers">
            <list>
                <ref bean="marshallingPayloadMethodProcessor"/>
            </list>
        </property>     
    </bean>  


    <bean id="marshallingPayloadMethodProcessor"
        class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
        <constructor-arg ref="marshallerA" />
        <constructor-arg ref="marshallerA" />
    </bean> 


    <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
        <property name="order" value="1" />
    </bean>

    <sws:dynamic-wsdl id="myEndpointA"
        portTypeName="WebService"
        locationUri="/ws/myEndpointServiceA/"
        targetNamespace="http://company.com/schema/webServices" >

        <sws:xsd location="classpath:/path/to/schema/mySchemaA.xsd"/>

    </sws:dynamic-wsdl>

    <bean id="marshallerA" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPath" value="x.y.z:x.y.w"/>
            <property name="jaxbContextProperties">
            <util:map>
                <entry key="eclipselink-oxm-xml">
                    <list>
                         <value>file:/bindingFileOne.xml</value>
                         <value>file:/bindingFileTwo.xml</value>
                         <value>file:/bindingFileThree.xml</value>    
                    </list>
                </entry>
            </util:map>
        </property>
    </bean>

Now say I have a second endpoint and a second marshaller, how can I force an Endpoint to use a specific marshaller?

Thanks for any help! :)


Solution

  • I ended up extending the DefaultMethodEndpointAdapter, creating SpecificEndpointAdapter. I only need to override one method, the implementation is as follows:

    public class SpecificEndpointAdapter extends DefaultMethodEndpointAdapter {
        private List<Object> acceptedEndpoints;
    
        public void setAcceptedEndpoints(List<Object> acceptedEndpoints) {
            this.acceptedEndpoints = acceptedEndpoints;
        }
    
        @Override
            protected boolean supportsInternal(MethodEndpoint methodEndpoint) {
            if(acceptedEndpoints.contains(methodEndpoint.getBean()))
                return super.supportsInternal(methodEndpoint);
            return false;
        }
    }
    

    In my context file, this allows me to restrict an endpoint adapter to a specific endpoint, thus having a specific marshaller:

    <bean id="serviceAEndpointAdapter" class="my.package.SpecificEndpointAdapter">
        <property name="acceptedEndpoints">
            <list>
                <ref bean="serviceAEndpoint"/>
            </list>
        </property>
        <property name="methodReturnValueHandlers">
            <list>
                <ref bean="serviceA.marshallingPayloadMethodProcessor"/>
            </list>
        </property>
        <property name="methodArgumentResolvers">
            <list>
                <ref bean="serviceA.marshallingPayloadMethodProcessor"/>
            </list>
        </property>     
    </bean>    
    
    <bean id="serviceBEndpointAdapter" class="my.package.SpecificEndpointAdapter">
        <property name="acceptedEndpoints">
            <list>
                <ref bean="serviceBEndpoint"/>
            </list>
        </property>      
        <property name="methodReturnValueHandlers">
            <list>
                <ref bean="serviceB.marshallingPayloadMethodProcessor"/>
            </list>
        </property>
        <property name="methodArgumentResolvers">
            <list>
                <ref bean="serviceB.marshallingPayloadMethodProcessor"/>
            </list>
        </property>     
    </bean>    
    

    In my MarhsallingPayloadProcessor I can now declare which marshaller to use. Note that I ended up using the bean id as the SpecificEndpointAdapter reference, so if you are using annotations, you will need to figure out the bean id of that endpoint.

    I hope this helps anyone who was facing the same issue, let me know if the answer requires more clarification.