1. MDBs must be run within an EJB container. Depending on the architecture of your app, this may be an excessive requirement, especially if you aren't use any other EJBs and do not require the features of a full-blown EJB container. Message-driven POJOs, on the other hand, can run anywhere, even (as shown here) in a simple main() method. The only requirement is an ActiveMQ message queue.
2. MDBs require that you implement the lifecycle methods of javax.ejb.MessageDrivenBean. Often these lifecycle methods aren't needed and are left as empty implementations. This isn't a real problem, except that it's simply unnecessary.
3. Although it may not be apparent from the simple HelloBean example, message-driven POJOs can take full advantage of the dependency injection and AOP support offered by Spring (including Spring's support for declarative transactions and Acegi's support for declarative security). In short, you can do anything with the POJO that you can do with any other bean in a Spring context.
4. The XML used to declare a message-driven POJO is slightly more verbose then for an MDB. You should, however, keep in mind that you'll only need to declare one JCAContainer bean, regardless of how many message-driven POJOs your application has.
Monday, November 30, 2009
MDP with ActiveMQ
The Steps are:
1. Create a Bean, say HelloBean.java
package test.mdp;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
public class HelloBean implements MessageListener {
public void onMessage(Message msg) {
try {
String name = msg.getStringProperty("name");
if(name == null) {
name = "Unknown";
}
System.out.println("Hello " + name + "!");
} catch (JMSException e) {
System.out.println(e);
}
}
}
Message-driven POJOs must implement javax.jms.MessageListener.
The onMessage() method is self-explanatory, when a message is dispatched to this bean, this method is called. You can see in this method, we extract the "name" property from the message and use it to display.
2. Create a spring context file, say mdppojo.xml. Add the below line in it.
<bean id="helloBean" class="test.mdp.HelloBean"/>
Here we are defining our HelloBean in Spring. In real time, your HelloBean may be a business object or a database object.
3. To associate "helloBean" with a message queue, we'll need to use ActiveMQ's JCAContainer. The JCAContainer associates itself with a specific ActiveMQ server and acts as a factory to produce connectors to that server. It is declared in Spring as:
<bean id="activeMQContainer" class="org.activemq.jca.JCAContainer">
<property name="workManager">
<bean id="workManager" class="org.activemq.work.SpringWorkManager"/>
</property>
<property name="resourceAdapter">
<bean id="activeMQResourceAdapter"
class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
</bean>
61616 is the port number on which ActiveMQ is running.
4. At this point we have a JCAContainer that is associated with the ActiveMQ server and we have a POJO that is ready and willing to accept messages. All that's left is to connect the POJO to the JCAContainer. The following code does that:
<bean id="HelloMDP" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="MyQueue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>
<property name="ref" value="helloBean" />
</bean>
Using Spring's factory-method feature, this bean declares JCAContainer's addConnector() method to create a connector to associate the "helloBean" with the ActiveMQ server. Specifically, the "activationSpec" property tells the JCAContainer to set up a connector that listens to the queue named "MyQueue" and the "ref" property tells it to send messages from that queue to the bean named "helloBean".
5. Now you run ActiveMQ on 61616.
6. The next thing you'll need is a simple application that loads the Spring application context. Lets say HelloListener.java
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class HelloListener {
public static void main(String[] args) {
new FileSystemXmlApplicationContext("mdppojo.xml");
}
}
SENDER:
7. Now you need a sender to send a message to MyQueue. There are many ways to send a JMS message, but here I am using JMSTemplate. First create an another spring context file,say sender.xml. add the below lines:
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="defaultDestinationName" value="MyQueue"/>
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<bean id="connectionFactory" class="org.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
The "defaultDestinationName" property tells the template the name of the message queue to attach to. The "connectionFactory" property tells the template how to connect. You could use a Spring's JndiObjectFactoryBean to pull a connection factory from JNDI or any implementation of javax.jms.ConnectionFactory.
8. Now we just need to use the "jmsTemplate" bean to send a message. For this lets create a Java class called Sender.java
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class Sender {
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext("client.xml");
JmsTemplate template = (JmsTemplate) ctx.getBean("jmsTemplate");
template.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
message.setStringProperty("name", "heyabhi");
return message;
}
}
);
}
}
8. Required jars are:
Spring.jar
activemq-core.jar
activemq-ra.jar
activemq-container-.jar
log4j-.jar
commons-logging-.jar
concurrent-.jar
geronimo-spec-jms-.jar
geronimo-spec-j2ee-connector-.jar
geronimo-spec-j2ee-management-.jar
geronimo-spec-jta-.jar
The above four jars can be replaced by j2ee.jar.
ADDITIONAL:
9. Configurations of ActiveMQ broker with Queues and Topics:
<beans>
<bean id="brokerURL" class="java.lang.String">
<constructor-arg>
<value>tcp://localhost:61616</value>
</constructor-arg>
</bean>
<bean id="topic.destination" class="org.apache.activemq.message.ActiveMQTopic" autowire="constructor">
<constructor-arg>
<value>MyTopic</value>
</constructor-arg>
</bean>
<bean id="queue.destination" class="org.apache.activemq.message.ActiveMQQueue" autowire="constructor">
<constructor-arg>
<value>MyQueue</value>
</constructor-arg>
</bean>
<!-- A JCA container for ActiveMQ -->
<bean id="activeMQContainer" class="org.activemq.jca.JCAContainer">
<property name="workManager">
<bean id="workManager" class="org.activemq.work.SpringWorkManager"/>
</property>
<!-- the JCA Resource Adapter -->
<property name="resourceAdapter">
<bean id="activeMQResourceAdapter" class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" ref="brokerURL"/>
</bean>
</property>
</bean>
<!-- a queue MDP activator -->
<bean id="queueMDPActivator" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="MyQueue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>
<property name="ref" value="helloBean"/>
</bean>
<!-- a topic MDP activator -->
<bean id="topicMDPActivator" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="MyTopic"/>
<property name="destinationType" value="javax.jms.Topic"/>
</bean>
</property>
<property name="ref" value="helloBean"/>
</bean>
<bean id="helloBean" class="com.habuma.mdpojo.HelloBean"/>
<!-- Client-side beans -->
<bean id="jmsFactory" class="org.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" ref="brokerURL"/>
</bean>
</property>
</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>
</beans>
1. Create a Bean, say HelloBean.java
package test.mdp;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
public class HelloBean implements MessageListener {
public void onMessage(Message msg) {
try {
String name = msg.getStringProperty("name");
if(name == null) {
name = "Unknown";
}
System.out.println("Hello " + name + "!");
} catch (JMSException e) {
System.out.println(e);
}
}
}
Message-driven POJOs must implement javax.jms.MessageListener.
The onMessage() method is self-explanatory, when a message is dispatched to this bean, this method is called. You can see in this method, we extract the "name" property from the message and use it to display.
2. Create a spring context file, say mdppojo.xml. Add the below line in it.
<bean id="helloBean" class="test.mdp.HelloBean"/>
Here we are defining our HelloBean in Spring. In real time, your HelloBean may be a business object or a database object.
3. To associate "helloBean" with a message queue, we'll need to use ActiveMQ's JCAContainer. The JCAContainer associates itself with a specific ActiveMQ server and acts as a factory to produce connectors to that server. It is declared in Spring as:
<bean id="activeMQContainer" class="org.activemq.jca.JCAContainer">
<property name="workManager">
<bean id="workManager" class="org.activemq.work.SpringWorkManager"/>
</property>
<property name="resourceAdapter">
<bean id="activeMQResourceAdapter"
class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
</bean>
61616 is the port number on which ActiveMQ is running.
4. At this point we have a JCAContainer that is associated with the ActiveMQ server and we have a POJO that is ready and willing to accept messages. All that's left is to connect the POJO to the JCAContainer. The following code does that:
<bean id="HelloMDP" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="MyQueue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>
<property name="ref" value="helloBean" />
</bean>
Using Spring's factory-method feature, this bean declares JCAContainer's addConnector() method to create a connector to associate the "helloBean" with the ActiveMQ server. Specifically, the "activationSpec" property tells the JCAContainer to set up a connector that listens to the queue named "MyQueue" and the "ref" property tells it to send messages from that queue to the bean named "helloBean".
5. Now you run ActiveMQ on 61616.
6. The next thing you'll need is a simple application that loads the Spring application context. Lets say HelloListener.java
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class HelloListener {
public static void main(String[] args) {
new FileSystemXmlApplicationContext("mdppojo.xml");
}
}
SENDER:
7. Now you need a sender to send a message to MyQueue. There are many ways to send a JMS message, but here I am using JMSTemplate. First create an another spring context file,say sender.xml. add the below lines:
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="defaultDestinationName" value="MyQueue"/>
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<bean id="connectionFactory" class="org.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
The "defaultDestinationName" property tells the template the name of the message queue to attach to. The "connectionFactory" property tells the template how to connect. You could use a Spring's JndiObjectFactoryBean to pull a connection factory from JNDI or any implementation of javax.jms.ConnectionFactory.
8. Now we just need to use the "jmsTemplate" bean to send a message. For this lets create a Java class called Sender.java
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class Sender {
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext("client.xml");
JmsTemplate template = (JmsTemplate) ctx.getBean("jmsTemplate");
template.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
message.setStringProperty("name", "heyabhi");
return message;
}
}
);
}
}
8. Required jars are:
Spring.jar
activemq-core.jar
activemq-ra.jar
activemq-container-.jar
log4j-.jar
commons-logging-.jar
concurrent-.jar
geronimo-spec-jms-.jar
geronimo-spec-j2ee-connector-.jar
geronimo-spec-j2ee-management-.jar
geronimo-spec-jta-.jar
The above four jars can be replaced by j2ee.jar.
ADDITIONAL:
9. Configurations of ActiveMQ broker with Queues and Topics:
<beans>
<bean id="brokerURL" class="java.lang.String">
<constructor-arg>
<value>tcp://localhost:61616</value>
</constructor-arg>
</bean>
<bean id="topic.destination" class="org.apache.activemq.message.ActiveMQTopic" autowire="constructor">
<constructor-arg>
<value>MyTopic</value>
</constructor-arg>
</bean>
<bean id="queue.destination" class="org.apache.activemq.message.ActiveMQQueue" autowire="constructor">
<constructor-arg>
<value>MyQueue</value>
</constructor-arg>
</bean>
<!-- A JCA container for ActiveMQ -->
<bean id="activeMQContainer" class="org.activemq.jca.JCAContainer">
<property name="workManager">
<bean id="workManager" class="org.activemq.work.SpringWorkManager"/>
</property>
<!-- the JCA Resource Adapter -->
<property name="resourceAdapter">
<bean id="activeMQResourceAdapter" class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" ref="brokerURL"/>
</bean>
</property>
</bean>
<!-- a queue MDP activator -->
<bean id="queueMDPActivator" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="MyQueue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>
<property name="ref" value="helloBean"/>
</bean>
<!-- a topic MDP activator -->
<bean id="topicMDPActivator" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="MyTopic"/>
<property name="destinationType" value="javax.jms.Topic"/>
</bean>
</property>
<property name="ref" value="helloBean"/>
</bean>
<bean id="helloBean" class="com.habuma.mdpojo.HelloBean"/>
<!-- Client-side beans -->
<bean id="jmsFactory" class="org.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" ref="brokerURL"/>
</bean>
</property>
</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>
</beans>
MDP with JBoss's JMS
<!-- our MDP -->
<bean id="messageListener" class="test.mdp.HelloBean"/>
<!-- The Spring JMS container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination">
<bean id="jmsDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="queue/MyQueue"/>
</bean>
</property>
<property name="messageListener" ref="messageListener"/>
</bean>
<!-- A JNDI Template which is used to get a reference to the JBoss connection factory -->
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.provider.url">localhost:1099</prop>
<prop key="java.naming.factory.url.pkgs">org.jnp.interfaces:org.jboss.naming</prop>
<prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop>
</props>
</property>
</bean>
<!-- The JBoss connection factory -->
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>ConnectionFactory</value>
</property>
</bean>
<bean id="messageListener" class="test.mdp.HelloBean"/>
<!-- The Spring JMS container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination">
<bean id="jmsDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="queue/MyQueue"/>
</bean>
</property>
<property name="messageListener" ref="messageListener"/>
</bean>
<!-- A JNDI Template which is used to get a reference to the JBoss connection factory -->
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.provider.url">localhost:1099</prop>
<prop key="java.naming.factory.url.pkgs">org.jnp.interfaces:org.jboss.naming</prop>
<prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop>
</props>
</property>
</bean>
<!-- The JBoss connection factory -->
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>ConnectionFactory</value>
</property>
</bean>
Subscribe to:
Posts (Atom)