Tuesday 11 June 2013

Spring AOP Error

AOP can be used in spring to weave aspects.  This is commonly used for database transactions using the @Transactional annotation.  However, this means that spring has to make proxy objects for beans which use these annotations.  This can lead to the error,

    Error Cannot convert value of type [$Proxy...] to required type

Basically what is happening is that spring is creating the proxy objects from the corresponding interfaces but in the spring context an actual implementation of the interface is required as a property by a bean.  In this case spring can't guarantee that the proxy implementation will satisfy the beans requirement and this error is thrown.

Solution 1

The first and best solution is to correct the receiving bean to take an interface instead of an implementation.  Most beans should take interfaces so that the implementation can easily be swapped in the spring configuration.  Once the beans are wired purely with interfaces this problem with go away.

Solution 2

Where solution 1 above isn't an option it is possible to tell spring to proxy classes and not interfaces.  This is done with this spring command

    <aop:config proxy-target-class="true"/>

This requires the correct namespace definitions at the top of the spring context xml file such as 

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
          http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/aop
                http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

Tuesday 4 June 2013

JMS, Spring & MQ

Here is a sample configuration for using MQ with JMS and Spring.

Firstly the MQ library is needed.  This can be found in a number of places but if you are using eclipse you can install the Websphere Liberty Profile (8.5.next) from the market place.  The required MQ library is available in

    <liberty_install_dir>/lib/com.ibm.ws.messaging.jms.wmq_1.0.jar

Maven Dependencies

The MQ jar can be used as a maven dependency using the System scope using the install directory above.

    <dependency>
        <groupId>com.ibm.ws.messaging.jms</groupId>
        <artifactId>wmq</artifactId>
        <version>1.0</version>
        <scope>system</scope>
        <systemPath><liberty_install_dir>/lib/com.ibm.ws.messaging.jms.wmq_1.0.jar</systemPath>
    </dependency>

The JMS interfaces are all that is necessary if installing into an application server / container.

    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>6.0</version>
        <scope>provided</scope>
    </dependency>

Note that this is a provided dependency because the implementation of these interfaces are provided by the container.  If the app is standalone and isn't deployed into a container then concrete implementations of the JMS spec needs to be provided.  One example is the geronimo library,

    <dependency>
        <groupId>org.apache.geronimo.specs</groupId>
        <artifactId>geronimo-jms_1.1_spec</artifactId>
        <version>1.1.1</version>
        <scope>compile</scope>
    </dependency>

And finally the spring jms jar is also required,

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>3.1.1.RELEASE</version>
        <scope>compile</scope>
    </dependency>

(Note: you'll need to add a spring property place holder for the properties or replace the ${...} values in the following examples)

JMS Connection Factory

The JMS connection factory is dependent on the queue / topic implementation that is being used.  In this case this is IBM MQ,

    <bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
        <property name="hostName" value="${ip_address}" />
        <property name="port" value="${port_number}" />
        <property name="queueManager" value="${queue_manager_name}" />
        <property name="transportType" value="1" />
    </bean>
    <bean id="jmsDestination" class="com.ibm.mq.jms.MQQueue">
        <constructor-arg value="${queue_name}" />
    </bean>

This will configure a Queue based connection.  To create a topic based connection is just as simple.  Just replace the 'Queue' with 'Topic' in the configuration above.  This gives,

    <bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQTopicConnectionFactory">
        <property name="hostName" value="${ip_address}" />
        <property name="port" value="${port_number}" />
        <property name="queueManager" value="${queue_manager_name}" />
        <property name="transportType" value="1" />
    </bean>
    <bean id="jmsDestination" class="com.ibm.mq.jms.MQTopic">
        <constructor-arg value="${topic}" />
    </bean>

Spring Connection Factory

The spring connection factory wraps the JMS Connection Factory.  There are a number of connection factories available.  If this is a simple connection with no authentication then just use,

    <bean id="springConnectionFactory"
            class="org.springframework.jms.connection.SingleConnectionFactory">
        <property name="targetConnectionFactory" ref="jmsConnectionFactory" />
    </bean>

otherwise if credentials are required use,

    <bean id="springConnectionFactory"
            class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
        <property name="targetConnectionFactory" ref="jmsConnectionFactory" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

Spring JMS Template

The JMS template does a lot of the boilerplate code required to open and close connections.  It is very like JdbcTemplate from that point of view.

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="springConnectionFactory" />
        <property name="defaultDestination" ref="jmsDestination" />
    </bean>

Message Driven Bean

A message driven bean is triggered when a message is received by the JMS system.  Spring maintains this relationship and in its simplest form it is exactly the same as the standard JMS setup,

    <!-- Message driven bean -->
    <bean id="sampleMessageDrivenBean" class="com.me.SampleMessageDrivenBean">
    </bean>

    <!-- and this is the message listener container -->
    <bean id="springJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="springConnectionFactory" />
        <property name="destination" ref="jmsDestination" />
        <property name="messageListener" ref="sampleMessageDrivenBean" />
    </bean>

In this case the message driven bean just implements the standard MessageListener interface from JMS,

public class SampleMessageDrivenBean implements MessageListener
{
    @Override
    public void onMessage(Message message)
    {
        System.out.println("Message Driven Bean: New Message");
        System.out.println(message.toString());
    }
}

Spring also provides a session message listener which provides the session object in the onMessage() call.

Sending Messages

Creating a class to send a message is very easy too.  Using spring to inject the JmsTemplate and Destination leaves a class to send messages as,


public class MessageSender
{
    
    /**
     * JMSTemplate.
     */
    private JmsTemplate jmsTemplate;
    
    /**
     * JMSTemplate.
     */
    private Destination jmsDestination;

    /**
     * Send a message.
     */
    public void sendMessage()
    {
        jmsTemplate.send(jmsDestination, new MessageCreator()
        {
            @Override
            public Message createMessage(Session session) throws JMSException
            {
                System.out.println("Message Sent");
                final Calendar now = Calendar.getInstance();
                return session.createTextMessage(now.toString());
            }
        });
    }

    /**
     * Sets jmsTemplate to the given value.
     *
     * @param jmsTemplate the jmsTemplate to set
     */
    public void setJmsTemplate(JmsTemplate jmsTemplate)
    {
        this.jmsTemplate = jmsTemplate;
    }

    /**
     * Sets jmsDestination to the given value.
     *
     * @param jmsDestination the jmsDestination to set
     */
    public void setJmsDestination(Destination jmsDestination)
    {
        this.jmsDestination = jmsDestination;
    }
}