Converting WebLogic MDBs to Spring Message-Driven POJOs

3.8k Views Asked by At

I have an application built on WebLogic 11b (10.3.4) using MDBs. I'm trying to convert these to Spring MDPs. Here is the MDB in question:

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "ejbName", propertyValue = "RecipientEventRouterBean"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
        @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "Durable") })
public class RecipientEventRouterBean extends TraxMessageRouter {

    @Resource
    private int appInstance;

    @Resource
    private String appName;

    @Resource(mappedName = "jms/TraxErrorQ")
    private Queue errorQueue;

    @Resource(name = "TraxCF")
    private ConnectionFactory errorQueueConnectionFactory;

    @EJB
    private Logger loggingService;

    @Resource
    private int maxJMSXDeliveryCount;

    @Resource
    private boolean validate;

    @EJB
    protected RecipientRegistrationService recService;

    @Override
    @PostConstruct
    public void init() {

        if (appName == null) {
            throw new EJBException("appName is null");
        }

        try {
            super.traxFactory = TraxFactory.getInstance(Application.fromValue(appName));
            super.processor = new ObjectFactory().createApplicationInstance();
            super.processor.setName(appName);
            super.processor.setInstance(this.appInstance);
            super.errorQueueConnectionFactory = this.errorQueueConnectionFactory;
            super.errorQueue = this.errorQueue;
            super.maxJMSXDeliveryCount = this.maxJMSXDeliveryCount;
            super.validate = this.validate;
            super.loggingService = this.loggingService;
        } catch (Exception e) {
            throw new EJBException(e);
        }

        super.init();
    }

    @Override
    protected TraxMessageService getTraxMessageService() {

        return recService;
    }

}

The parent of the MDB, TraxMessageRouter, implements the MessageListener interface.

I've created the MDP beans in the jms-applicationContext.xml:

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

    <!-- Spring JMS Destination Resolver -->
    <bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
        <property name="cache" value="true" />
    </bean>


    <!-- Spring JMS Queue Connection Factory leveraging a single connection -->
    <!-- SingleConnectionFactory will return the same Connection on all createConnection calls and ignore calls to close -->
    <bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="jms/TraxCF" />
    </bean>

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destinationResolver" ref="jmsDestinationResolver" />
    </bean>

    <bean id="jmsSender" class="com.mycompany.web.service.jms.JMSSender">
        <property name="jmsTemplate" ref="jmsTemplate" />
    </bean>

    <bean id="jmsReceiver" class="com.mycompany.web.service.jms.JMSReceiver">
        <property name="jmsTemplate" ref="jmsTemplate" />
    </bean> 

    <bean id="jmsQueue" class="javax.jms.Topic" />  

    <!-- this is the Message Driven POJO (MDP) -->
    <bean id="messageListener" class="com.mycompany.messaging.RecipientEventRouterBean" />

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

How do I convert the @MessageDriven annotation into the proper Spring syntax? Can I just comment it out now?

I have several more EventRouterBeans. Do they all get bean declarations in the jms-ApplicationContext.xml or can I add the parent, TraxMessageRouter, to the xml?

What other changes to the config files need to be done? Is there a good guide on how to transition from MDBs to MDPs? Is there a good guide on how to transition from EJB 3 to Spring?

1

There are 1 best solutions below

2
On BEST ANSWER

It almost looks like ok to me. Here are a few hints:

  • Yes, you can remove the @MessageDriven annotation as you have configured the container already
  • Your bean jmsQueueis wrong. As you are using weblogic, you should either locate the queue using JNDI (like you did for the connection factory) or you should provide the (jndi) name of the queue using the destinationName property (that accepts a String and not a javax.jms.Destination)

You need one declaration per message listener that you want to deploy. Perhaps you should go the xml namespace route to share those settings?

<jms:listener-container connectionFactory="connectionFactory" 
                        destinationResolver="jmsDestinationResolver">
    <jms:listener ... />
    <jms:listener ... />
</jms:listener-container>

This will create two listener containers (i.e. one container per jms:listener entry).

Note also that Spring 4.1 is going to provide a better declarative approach where you can just annotate a method of any Spring managed bean. Check this blog post for more information. 4.1.0.RC2 has been released so you can try it out now.