Tuesday, December 30, 2008

Changing the root web application in Tomcat 6

These days I am very busy developing a large and complex web application, which is nearing its first release. This web based product runs on Tomcat 6 and will be deployed on http://www.mydomain.com

But if I deploy my web application on Tomcat 6, I need to specify the context root. Well this is no good, why will the users of this website be willing to remember the context root. What if the context root changes. In other words the users would not be interest in typing the following - http://www.mydomain.com/myappcontext to visit this site.

Hence it is necessary that I change the default / root web application of Tomcat and point it to my application. I wanted to try and test this on localhost first. As always I Googled and found a number of posts and none seem to work. I also read the documentation it was helpful in understanding the concepts but was not effective as I could not get it working. Finally after few hours of trial and error one of my colleague Joyeeta Majumdar found the solution. Here it is step by step account.

1> I use Maven 2 + Eclipse 3.4 to build my web application. If I run the Maven 'install' goal the web application is generated in the target folder. It is available both as .war file and in exploded form. In my case it generated as Jing-Web-0.0.1-SNAPSHOT and Jing-Web-0.0.1-SNAPSHOT.war in the folder c:\jingworkspace\Jing-Web-Main\Jing-Web\target

2> Create a folder <engine>/<hostname>. In my case - Catalina\localhost. This folder is created under <Tomcat_Home>/conf. In my case this is C:\tomcat6\conf.

3> Create a file named ROOT.xml (case-sensitive) in the C:\tomcat6\conf\Catalina\localhost folder.

4> Here is the content of my ROOT.xml file

<?xml version='1.0' encoding='utf-8'?>

<Context reloadable="true" path=""
    docBase="c:/jingworkspace/Jing-Web-Main/Jing-Web/target/target/ping-web-0.0.1-SNAPSHOT" />

You will need to change the docBase attribute to the location of the exploded web application.

No changes to be made either in server.xml or context.xml located in c:\tomcat6\conf folder.

Now try http://localhost:8080/ and it should take us to Jing-Web application. You may need to delete the contents of your browser cache(press F5) to view the changes. You may also need to delete the contents of <Tomcat_Home>/work directory to get this working.

Sunday, December 21, 2008

Consuming a JMS Message from ActiveMQ using Spring Message Driven POJO

In my last post, I had shown how you can post a JMS message to a ActiveMQ queue. In this post I will show the reverse process. I will not try to consume this message using a Spring Message Driven POJO. I assume your familiarity with Spring MDP. If not you can look here - http://static.springframework.org/spring/docs/2.5.x/reference/jms.html

The operation that I intend to carry out is depicted in Figure 1.

image

Figure 1 - Consuming JMS message using Spring MDP

Step 1 - Create the message listener.

Listing 1 - EventMessageConsumer.java

/**
*
*/
package org.opengarage.pingscape.mq.consumer;

import javax.jms.Message;
import javax.jms.MessageListener;

/**
* @author dhrubo
*
*/
public class EventMessageConsumer implements MessageListener {

    /* (non-Javadoc)
     * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
     */
    public void onMessage(Message message) {
        System.out.println("Message Consumed -"+message);
        //TODO : Take some action.... invoke service method

       //NOTE : Service object is injected by Spring framework

}

}

Step 2 - Wire up the bean in Spring configuration

Listing 2 - spring-activemq-config.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" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    <bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL">
            <value>tcp://localhost:61616</value>
        </property>
    </bean>
    <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg>
            <value>queue.opengarage</value>
        </constructor-arg>
    </bean>
     <!-- lets wrap in a pool to avoid creating a connection per send -->
   <bean id="connectionFactory"
        class="org.springframework.jms.connection.SingleConnectionFactory">
        <property name="targetConnectionFactory">
            <ref local="jmsFactory" />
        </property>
    </bean>
    <!-- Spring JMS Template -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
    </bean>
    <!-- a sample POJO message producer which uses a Spring JmsTemplate -->
    <bean id="messageProducer"
        class="org.opengarage.pingscape.mq.producer.EventInvitationMessageProducer">
        <property name="jmsTemplate" ref="jmsTemplate" />
        <property name="queue" ref="queue" />
        <property name="messageCreator">
            <bean class="org.opengarage.pingscape.mq.producer.EventMessageCreator" />
        </property>
    </bean>
    <!-- Consumer part -->
    <!-- this is the Message Driven POJO (MDP) -->
    <bean id="messageListener"
        class="org.opengarage.pingscape.mq.consumer.EventMessageConsumer" />

    <!-- 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="queue" />
        <property name="messageListener" ref="messageListener" />
    </bean>
</beans>

Step 3  - Test

Finally you can see the results of message produced and consumed in the Admin web console as shown in Figure 2.

image

Figure 2 - Queue statistics

Sending JMS message to ActiveMQ using Spring 2.5 from a web application

In this post I will show how you can send JMS message to ActiveMQ from your Spring web application. I have a Spring 2.5 + JBOSS Richfaces based web application that runs on Tomcat 6 running on JDK 5. Figure 1. depicts what I intend to do.

F1

Figure 1. - Send JMS Message to ActiveMQ using Spring from a web application.

Step 1: Drop in the following jars to WEB-INF/lib

  • activemq-core-5.2.0.jar
  • jms.jar
  • geronimo-j2ee-management_1.0_spec-1.0.jar

Step 2: Create the destination/Queue using the ActiveMQ admin web application.

Launch your browser and point it to - http://localhost:8161/admin/

 image

Figure 2. - ActiveMQ admin console.

Click on 'Queues' to navigate to queue list and create screen. You will need to supply the queue name and click on 'Create' to create a new queue destination on ActiveMQ server. This is shown in Figure 3.

image

Figure 3 - Create a new Queue.

Step 3. Create a message producer interface.

This may not be necessary, but since I prefer to implement Program to Interface in my application i define a message producer interface. This is shown in Listing 1.

Listing 1 - MessageProducer.java

/**
*
*/
package org.opengarage.pingscape.mq.producer;

/**
* @author dhrubo
*
*/
public interface MessageProducer {
    public void send(Object message);
}

This interface defines a single method(can have more, but for now this will suffice) which accepts an object (Java bean) which will be passed to the Queue.

Step 4. An implementation of the MessageProducer interface

This class is responsible for sending a particular Javabean object to the queue (your goal may be different / may want to send something different).

Listing 2 - EventInvitationMessageProducer.java

/**
*
*/
package org.opengarage.pingscape.mq.producer;

import javax.jms.Queue;

import org.opengarage.pingscape.bean.Event;
import org.springframework.jms.core.JmsTemplate;

/**
* @author dhrubo
*
*/
public class EventInvitationMessageProducer implements MessageProducer{
    private JmsTemplate jmsTemplate;
    private Queue queue;
    private EventMessageCreator messageCreator;
    /**
     * @param jmsTemplate the jmsTemplate to set
     */
    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }
    /**
     * @param queue the queue to set
     */
    public void setQueue(Queue queue) {
        this.queue = queue;
    }

    /**
     * @param messageCreator the messageCreator to set
     */
    public void setMessageCreator(EventMessageCreator messageCreator) {
        this.messageCreator = messageCreator;
    }

    public void send(Object message) {
        this.messageCreator.setEvent((Event)message);
        this.jmsTemplate.send(this.queue, this.messageCreator);
    }

}

The key is the send method. It uses a MessageCreator to consume the supplied Event object. It then uses the JmsTemplate to send this message to the destination.

Step 5. Provide the message creator implementation

Listing 3 - EventMessageCreator.java

/**
*
*/
package org.opengarage.pingscape.mq.producer;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;

import org.opengarage.pingscape.bean.Event;
import org.springframework.jms.core.MessageCreator;

/**
* @author dhrubo
*
*/
public class EventMessageCreator implements MessageCreator {
    private Event event;
    /* (non-Javadoc)
     * @see org.springframework.jms.core.MessageCreator#createMessage(javax.jms.Session)
     */
    public Message createMessage(Session session) throws JMSException {
        ObjectMessage objectMsg = session.createObjectMessage();
        objectMsg.setObject(event);
        return objectMsg;
    }
    /**
     * @param event the event to set
     */
    public void setEvent(Event event) {
        this.event = event;
    }

}

Step 6 - Wire up in Spring configuration

Listing 4 - spring-activemq-config.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" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    <bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL">
            <value>tcp://localhost:61616</value>
        </property>
    </bean>
    <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg>
            <value>queue.opengarage</value>
        </constructor-arg>
    </bean>
    <!-- Spring JMS Template -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory">
      <!-- lets wrap in a pool to avoid creating a connection per send -->
            <bean class="org.springframework.jms.connection.SingleConnectionFactory">
                <property name="targetConnectionFactory">
                    <ref local="jmsFactory" />
                </property>
            </bean>
        </property>
    </bean>
    <!-- a sample POJO message producer which uses a Spring JmsTemplate -->
    <bean id="messageProducer" class="org.opengarage.pingscape.mq.producer.EventInvitationMessageProducer">
        <property name="jmsTemplate" ref="jmsTemplate"/>
        <property name="queue" ref="queue"/>
        <property name="messageCreator">
            <bean class="org.opengarage.pingscape.mq.producer.EventMessageCreator"/>
        </property>
    </bean>
</beans>

Step 7 - Client of the message producer

Now we need a client to use the message producer. The Event business service class will use this message producer. This is shown in Listing 5.

Listing 5 - EventServiceImpl.java

/**
*
*/
package org.opengarage.pingscape.service.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengarage.pingscape.bean.Event;
import org.opengarage.pingscape.business.api.EventService;
import org.opengarage.pingscape.mq.producer.MessageProducer;

/**
* @author dhrubo
*
*/
public class EventServiceImpl implements EventService {
    private static final Log _LOG = LogFactory.getLog(EventServiceImpl.class);
   
    private MessageProducer messageProducer;

    /* (non-Javadoc)
     * @see org.opengarage.pingscape.business.api.EventService#saveEvent(org.opengarage.pingscape.bean.Event)
     */
    public void saveEvent(Event event) {
        messageProducer.send(event);
    }
   
     /**
     * @param messageProducer the messageProducer to set
     */
    public void setMessageProducer(MessageProducer messageProducer) {
        this.messageProducer = messageProducer;
    }

}

Step 8 - Configure in Spring application context.

The message producer is injected by the Spring framework. Here is the configuration snippet from the service configuration.

Listing 6 - spring-service-config.xml

<!-- Other beans -->

<bean name="eventService"
        class="org.opengarage.pingscape.business.impl.EventServiceImpl">
         <property name="messageProducer" ref="messageProducer" />
    </bean>

Step 9 - Test for message

Finally I restart my Tomcat when everything is ready, launch the event entry form, fill in data and click on Send button in the screen. The event details are now in the queue. You can test this in the ActiveMQ admin web console as shown in the Figure - 4 below.

image

Figure 4 - New message now in the Queue.