Wednesday, December 24, 2008

JNDI lookup on Tomcat and JBoss using Spring

Unfortunately Java EE specs does not specify any standard way of JNDI naming conventions, hence most of the application servers have their own way of JNDI naming. On specifying a Datasource's JNDI name as 'jdbc/myDatasource', Tomcat (6) binds that as 'java:/comp/env/jdbc/myDatasource' while JBoss (5) binds as 'java:/jdbc/myDatasource'. So if one wants to deploy the application on multiple application servers then at least JNDI names has to be changed. I was wondering if Spring Framework has a solution for this, so tried to do lookup using <jee:jndi-lookup ...> as shown below-

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatasource" resource-ref="true"/>
it works well with Tomcat but fails on JBoss. Reason, the JNDI prefix is hard coded in Spring to 'java:comp/env/'.
package org.springframework.jndi;

public abstract class JndiLocatorSupport extends JndiAccessor {

    /** JNDI prefix used in a J2EE container */
    public static final String CONTAINER_PREFIX = "java:comp/env/";
    . . .
}
So it is obvious that it will fail on JBoss. To overcome this limitation, I came up with following solution-
public final class ServiceLocator {

    private static final Map<String, Object> services = new ConcurrentHashMap<String, Object>();

    private static ServiceLocator instance;

    private static Context context;

    static {
        try {
            Context initContext = new InitialContext();
            if (ServerDetector.isJBoss()) {
                context = (Context) initContext.lookup("java:");
            } else if (ServerDetector.isTomcat()) {
                context = (Context) initContext.lookup("java:/comp/env");
            } else {
                context = initContext;
                // or add more 'else if' blocks according to servers to be supported
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public DataSource getDataSource(String name) throws Exception {
        if (name == null || name.length() <= 0)
            throw new IllegalArgumentException("name");

        if (services.containsKey(name))
            return (DataSource) services.get(name);

        DataSource ds = (DataSource) context.lookup(name);

        services.put(name, ds);
        return ds;
    }
}

Here ServerDetector is a good utility class I found in Liferay code base. Now ServiceLocator takes care of application server specific JNDI prefixes and avoids hassles of changing configuration or code to deploy it on a specific server. In case of Spring instead of using <jee:jndi-lookup ...> one can do the lookup using above-mentioned ServiceLocator as shown below-
    <bean id="serviceLocator" class="com.vinodsingh.ServiceLocator" factory-method="getInstance" />

    <bean id="dataSource" factory-bean="serviceLocator" factory-method="getDataSource">
        <constructor-arg value="jdbc/myDatasource" />
    </bean>
Now entire code (including configuration files) becomes truly portable, at least for JNDI lookups :-)

Saturday, December 20, 2008

Let Spring load service class for JAX-WS

During deployment JAX-WS RI by default creates an instance (singleton) of web service implementation class and uses that to serve requests. At times we may want to customize the instantiation of the web service implementation class e.g. let Spring configure and load the object.

JAX-WS Commons has an extension for Spring integration, which delegates total configuration to Spring and makes RI specific files (sun-jaxws.xml) redundant. Currently I am happy with the way JAX-WS RI exposes web services and do not want everything to move to Spring or something else. I was just looking for a way to load the service implementation class by my code instead of RI doing so. JAX-WS RI has a poorly documented (or may be I am not able find it easily) feature called InstanceResolver, which lets developers to customize the way service implementation class is loaded. The code snippets below demostrate how to so-

public class MyResolver extends InstanceResolver<Object> {

    private final Object serviceImpl;

    /**
     * @param clazz
     */
    public MyResolver(Class<?> clazz) {
        serviceImpl= . . . load the class here or get it from Spring or somewhere else . . .;
    }

    /**
     * @see com.sun.xml.ws.api.server.InstanceResolver#resolve(com.sun.xml.ws.api.message.Packet)
     */
    @Override
    public Object resolve(Packet request) {
        return serviceImpl;
    }

}

// Now create an Annotation, which has @InstanceResolverAnnotation with MyResolver.class
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InstanceResolverAnnotation(MyResolver.class)
public @interface MyWs {
}

// Now annotate the service class with above-mentioned annotation
@WebService(endpointInterface = "com.vinodsingh.Address")
@MyWs
public class AddressImpl implements Address {
    . . .
}

During deployment (WSServletContextListener) JAX-WS RI iterates through all annotations on the service class and sees if there is any annotation annotated with @InstanceResolverAnnotation. On finding one it will delegate the task of creating instance of the service class to the user defined InstanceResolver. With this I am able to load service classes using Spring.

Sunday, November 30, 2008

Environment specific property with Spring

In an application there are lots of properties which vary from one environment to another e.g. database used in development will be different from the one used in production, leading to different connection properties (url, user names and password). Applications using Spring Framework usually fallback on PropertyPlaceholderConfigurer for inserting such values from an external properties file.

I would like to have everything at one place instead of externalizing properties into separate file for each environment. Fortunately Spring Framework provides extension points to create user defined XML elements and classes to instantiate beans using those elements. To have environment specific properties in single configuration file along with other bean definitions, I decided to create a custom element named <property>. Next I will try to explain how it works.

Here is the schema for <property> element, which is used to define different property for each environment-
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://vinodsingh.com/schema/spring"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   targetNamespace="http://vinodsingh.com/schema/spring">

    <xsd:element name="property">
        <xsd:complexType>
            <xsd:attribute name="id" type="xsd:string" use="required" />
            <xsd:attribute name="class" type="xsd:string" use="optional" default="java.lang.String" />
            <xsd:attribute name="value" type="xsd:string" use="required" />
            <xsd:attribute name="region" use="optional" default="all">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:string">
                        <xsd:enumeration value="dev" />
                        <xsd:enumeration value="test" />
                        <xsd:enumeration value="integ" />
                        <xsd:enumeration value="stage" />
                        <xsd:enumeration value="uat" />
                        <xsd:enumeration value="prod" />
                        <xsd:enumeration value="all" />
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>
Now create a bean definition parser to parse the <property> element-
public class PropertyDefinitionParser extends AbstractSingleBeanDefinitionParser {

    /** Region where bean will be available. */
    private String beanRegion = null;

    private static String sysRegion = System.getProperty("region");

    static {
        if (sysRegion == null || sysRegion.length() <= 0)
            sysRegion = "all";
    }

    @Override
    protected Class<?> getBeanClass(Element element) {
        Class<?> clazz = null;
        String className = element.getAttribute("class");
        if (className != null && className.length() > 0) {
            try {
                clazz = Class.forName(className);
            } catch (Exception e) {
                log.error("Failed to load class: " + className, e);
                throw new RuntimeException(e);
            }
        }

        return clazz;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        beanRegion = element.getAttribute("region");

        bean.addConstructorArgValue(element.getAttribute("value"));
    }

    @Override
    protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
        if (beanRegion.equals(sysRegion) || "all".equals(beanRegion))
            super.registerBeanDefinition(definition, registry);
    }
}
Based on the above-mentioned schema the resulting bean definitions will looks like-
<property id="jdbc.url" value="jdbc:hsqldb:hsql://dev:9002" region="dev" />
<property id="jdbc.url" value="jdbc:hsqldb:hsql://production:9002" region="prod" />

<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="driverClassName"/>
    <property name="url" ref="jdbc.url"/>
    <property name="username" value="username"/>
    <property name="password" value="password"/>
</bean>
Here jdbc.url property is defined twice with different region value. In bean definition for dataSource the jdbc.url is used as bean reference. The PropertyDefinitionParser will register only one definition of jdbc.url,which belongs to the current runtime region. The region will be passed as a system property to JVM.

Sunday, November 23, 2008

Consuming web services with Spring

Consuming web services with Spring framework is amazingly easy. It avoids the need of creating client side stubs during compile time and does the same at run time. A typical web service client can be configured as shown below-

<bean id="myWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="pkg.MyService"/>
    <property name="wsdlDocumentUrl" value="http://localhost:8080/testws/myService?wsdl"/>
    <property name="namespaceUri" value="http://com.pkg/"/>
    <property name="serviceName" value="MyService"/>
    <property name="portName" value="MyServicePort"/>
</bean>
Where serviceInterface is the business interface that clients will use for invoking service methods. wsdlDocumentUrl is the URL for the published WSDL file. Spring will download WSDL from the given URL and generate the client side stubs when it creates the bean usually during application start up. namespaceUri corresponds to the targetNamespace in the .wsdl file. serviceName corresponds to the service name in the .wsdl file. portName corresponds to the port name in the .wsdl file.

Now accessing web service pretty easy, just get the bean from Spring context and use that to invoke methods-
MyService myService = springContext.getBean("myWebService");
myService.someMethod(. . .);
One downside of this approach is that the web service must be up running when consumer application is being deployed. To avoid this problem one can use lazy-init="true" to lazily initialize the beans when they are first requested but all this quickly becomes unmanageable when we have an array of SOA based applications. In such a scenario one have to strictly follow the order of application deployment else deployment might fail.