Tag Archives: spring

xfire, inheritance and annotations, via spring

Yuck, xfire sure was about 1000% better than axis, but compared to modern jax-ws and contract first, it really can be just painful sometimes. So, I had some subclasses I wanted to expose over web services, but the directions for inheritance in xfire, either with Aegis or JaxB, both rely on you having horribly verbose spring configs.

We just use @WebService annotations, but we still needed a way to list these extra types that should be listed in the generated wsdl. (It’s worth noting that you get no errors if you don’t do this, but only fields from the parent class will be sent to web service clients)

The “fix” was committed in XFIRE-594 But as usual, the documentation got shotgunned all over somewhere. What you need for inherited types, when you’re using annotations is:

@WebService(endpointInterface = "acme.package.ISomeInterface")
@ServiceProperties(properties = {
    @ServiceProperty(key="writeXsiType", value="true"),
    @ServiceProperty(key="overrideTypesList", list = {
        "acme.package.SomeChildType",
        "acme.package.SomeOtherChildType"}) 
})
public class SomeService implements ISomeInterface
{
    // blah
    SomeParentType someMethod();
}

Then, if you’re just using the simple config from Spring remoting You’re done!

JaxWs, Spring, and referencing the wsdl in a jar, not via http

Well, this was awkward. Spring provides a quite handy factory class for creating JaxWs clients with JaxWsPortProxyFactoryBean

The config is quite simple really, you just do:

<bean id="partnerProxy" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
        <property name="serviceInterface" value="is.vf.conan.ws.ConanPartnerPortType"/>
        <property name="wsdlDocumentUrl" value="http://localhost:8088/mockConanPartnerBinding?WSDL"/>
        <property name="namespaceUri" value="http://is.vf.conan/1.0/"/>
        <property name="serviceName" value="ConanPartnerService"/>
        <property name="portName" value="ConanPartnerPort"/>
</bean>

The “namespaceUri”, “serviceName” and “portName” are all boiler plate, that you have to extract from the wsdl, and shouldn’t really have to specify, but whatever, it’s boiler plate.  “serviceInterface” has to already exist.  You  have to have generated the types and interfaces before hand. But, if you’re doing wsdl first development, and you’re reading this, you already have all that. (Update 2010-07: If you don’t want to type in all that boiler plate (and who does), see: Spring JaxWS clients with less config)

Here in the office, we have a client jar, which contains the generated types and interfaces, and the wsdls themselves.  This is easy to distribute, and lets you add in support for a new service by simply dropping in a new client jar. (The jar is made with maven, using the jaxws-maven-plugin and it’s wsimport goal)

All well and good. Except. You can’t start this application unless that wsdl is up and available. You can add

        <property name="lookupServiceOnStartup" value="false"/>

Which is certainly a good start, but what if my endpoint doesn’t actually provide the wsdl at runtime? What if the ?wsdl url suffix doesn’t work here.

Well, we have two options:

  1. Have a server running all the time to provide the wsdl, and use the “endpoint” property to specify the final destination. (yuck)
  2. Have the proxy factory reference the WSDL inside that client jar.

You will see in some docs that you can do things like this:

<property name"wsdlDocumentUrl" value="classpath:blah.wsdl"/>

This works, if the wsdl can be found. If you have the wsdl in the same class structure as the rest of your code, this is probably all you need. But if your wsdl is inside a jar in the classpath, well, I tried lots of singing and dancing, but couldn’t work out how to make a url that pointed reliably to the wsdl inside the jar. :( This made me very sad.

I really don’t like having to override things, but well…. Here’s an extended spring factory bean. You now must use the endpoint property, plus the new property “localWsdlName” as well as “lookupServiceOnStartup = false” I was going to be using endpoint properties anyway, to make it nice and easy to switch out the different test environments we use, so that was not a big problem.

“localWsdlName” refers to the path inside the jar. It _should_ be able to be a “classpath:blah” reference as well, but it’s all relative to the jar that provides the service interface.

public class VodafoneJaxWsFactoryBean extends JaxWsPortProxyFactoryBean {
    private String localWsdlName;
 
    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        // This will be a looonnng file:// url, reaching into the jar 
        // holding the service interface
        URL url = getServiceInterface().getResource(localWsdlName);
        setWsdlDocumentUrl(url);
    }
 
    public void setLocalWsdlName(String localWsdlName) {
        this.localWsdlName = localWsdlName;
    }
}

And the new config you need to use this…

    <bean id="partnerProxy" class="is.vf.conan.VodafoneJaxWsFactoryBean">
        <property name="serviceInterface" value="is.vf.conan.ws.ConanPartnerPortType"/>
        <property name="endpointAddress" value="http://localhost:8088/mockConanPartnerBinding"/>
        <property name="localWsdlName" value="/conanPartner.wsdl"/>
        <property name="lookupServiceOnStartup" value="false"/>
        <property name="namespaceUri" value="http://is.vf.conan/1.0/"/>
        <property name="serviceName" value="ConanPartnerService"/>
        <property name="portName" value="ConanPartnerPort"/>
    </bean>

There you have it! If you have everything in one big blob project, you don’t need this, just specify the localWsdlName as “classpath:blah” and you’re done.

EHCache, JMX, Hibernate and Spring

We have a spring/hibernate application that we also like being able to use JConsole with, so we expose a few things via jmx.  Spring makes this pretty easy for the most part, you just use @ManagedResource on with the boiler plate config below, you’re good to go!

    <!-- Allow any bean to be exposed as an mbean.  Just use @ManagedResource and @ManagedAttribute -->
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="assembler" ref="assembler"/>
        <property name="namingStrategy" ref="namingStrategy"/>
        <property name="autodetect" value="true"/>
        <property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING"/>
    </bean>
    <bean id="attributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
    <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
        <property name="attributeSource" ref="attributeSource"/>
    </bean>
    <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <property name="attributeSource" ref="attributeSource"/>
    </bean>

But what about exposing beans from libraries?  For hibernate it’s easy, you just make spring create the hibernate provided bean…

    <bean id="hibernateStatistics" class="org.hibernate.jmx.StatisticsService">
        <property name="statisticsEnabled" value="true"/>
        <property name="sessionFactory" ref="nipSessionFactory"/>
    </bean>

and then modify your mbean exporter config to specifically include hibernate stats…

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="assembler" ref="assembler"/>
        <property name="namingStrategy" ref="namingStrategy"/>
        <property name="autodetect" value="true"/>
        <property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING"/>
        <property name="beans">
            <map>
                <entry key="Hibernate:name=statistics" value-ref="hibernateStatistics"/>
            </map>
        </property>
    </bean>

Presto, hibernate stats are now available in Jconsole, wherever you specify with the key. Well and good. But what about EHCache? According to http://ehcache.org/documentation/jmx.html there’s mbeans provided here too, so we can just list them in our bean config and add them to the list with hibernate right? MAYBE! You can get the cache manager working like this…

    <bean id="cacheManager" class="net.sf.ehcache.management.CacheManager">
        <constructor-arg ref="innerCacheManager"/>
    </bean>
    <bean id="innerCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="shared" value="true"/>
    </bean>

and then refer to cacheManager in the list of beans to export. But when it comes to the CacheStatistics object, you need to somehow get at the EhCache object itself, and spring bean config doesn’t have any way of getting at that easily.

Instead, you can just ask EhCache to register itself with the mbean server. This is covered at http://forum.springsource.org/showthread.php?t=63453 but I just wanted to cover it with a bit more information…

The extra beans you need for full EhCache jmx management, on top of the hibernate stats config above is:

    <!-- only needed explicitly because ehcache needs it to register itself -->
    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
      <property name="locateExistingServerIfPossible" value="true"/>
    </bean>
 
    <!-- ehcache needs to register itself, we can't just give the bean to jmx ourselves -->
    <bean id="ehCacheMBeanRegistration" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="net.sf.ehcache.management.ManagementService.registerMBeans"/>
        <property name="arguments">
            <list>
                <ref bean="innerCacheManager"/>
                <ref bean="mbeanServer"/>
                <value>true</value>
                <value>true</value>
                <value>true</value>
                <value>true</value>
            </list>
        </property>
    </bean>
    <bean id="innerCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="shared" value="true"/>
    </bean>