Sunday, January 27, 2013

Spring & ActiveMQ SSL-enabled

In this blog I will show you how to configure ActiveMQ to use SSL as its transport.

Follow steps 1-4 on http://activemq.apache.org/how-do-i-use-ssl.html.

If running ActiveMQ as a server:
cd ~/activemq
vi conf/activemq.xml

Modify <transportConnector> to use SSL.
Before:
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireformat.maxFrameSize=104857600"/>
After:
<transportConnector name="openwire" uri="ssl://localhost:61616?maximumConnections=1000&amp;wireformat.maxFrameSize=104857600"/>

Add SSLContext to <broker>.
<sslContext>
    <sslContext 
      keyStore="/home/nbashir/broker.ks" keyStorePassword="your-password" 
      trustStore="/home/nbashir/client.ts" trustStorePassword="your-password" />
</sslContext>

If you are using spring, you need the following info in your application-context files for producer and consumer classes:

Producer  application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" 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.xsd
                        http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.noushin.spring.jms" />
    <context:annotation-config />

    <!-- ActiveMQ destinations to use -->
    <amq:queue id="destination" physicalName="TestQ" />
    
    <bean id="sslConnectionFactory" class="org.apache.activemq.ActiveMQSslConnectionFactory"
        p:brokerURL="ssl://localhost:61616"
        p:trustStore="client.ts"
        p:trustStorePassword="your-password" />
    
    <!-- Spring JMS Producer Configuration -->
    <bean id="jmsProducerTemplate" class="org.springframework.jms.core.JmsTemplate"
        p:connectionFactory-ref="sslConnectionFactory"
        p:defaultDestination-ref="destination"/>
        
</beans>

 Consumer applciation-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jms="http://www.springframework.org/schema/jms" 
    xmlns:p="http://www.springframework.org/schema/p"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
                        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd">

    <context:component-scan base-package="com.noushin.spring.jms" />
    <context:annotation-config />
    
    <bean id="sslConnectionFactory" class="org.apache.activemq.ActiveMQSslConnectionFactory"
        p:brokerURL="ssl://localhost:61616"
        p:trustStore="client.ts"
        p:trustStorePassword="your-password" />
    
   <!-- JMS Consumer Configuration -->        
    <jms:listener-container container-type="default" 
                            connection-factory="sslConnectionFactory"
                            acknowledge="auto">
        <jms:listener destination="TestQ" ref="messageConsumer" />
    </jms:listener-container>
    
</beans>


Alternatively, you can configure ActiveMQ to run as a embedded JMS provider within your app. For that add the following lines to your application's application-context.xml
<!-- SSL Context --> 
<amq:broker useJmx="true" persistent="false">
    <amq:sslContext>
        <amq:sslContext 
            keyStore="/home/nbashir/broker.ks"
            keyStorePassword="your-password" 
            trustStore="/home/nbashir/client.ts"
            trustStorePassword="your-password" />
    </amq:sslContext>
    <amq:transportConnectors>
        <amq:transportConnector uri="ssl://localhost:61616" />
    </amq:transportConnectors>
</amq:broker>

Monday, January 14, 2013

Spring 3 & JMS

Here is a quick way to develop a simple application using Jms.  You will need the following components:

  • Apache ActiveMQ
  • Spring
  • Java
  • Maven
 
Install ActiveMQ on Ubuntu. The latest download bundle is available at ActiveMQ download page.

Example (Running ActiveMQ 5.7.0 on Ubuntu):

1. Download apache-activemq-5.7.0-bin.tar.gz.

2. Untar the bundle:

tar zxvf apache-activemq-5.7.0-bin.tar.gz

3. Configure and start Activemq

cd apache-activemq-5.7.0
bin/activemq setup newConfig
bin/activemq start

4. Verify Activemq is running:

netstat -an |grep 61616
or
Go to Admin console by visiting http://localhost:8161/admin/

5. Create a basic Jms queue for testing:

Use Admin console.
Select Queues
Create a new queue called TestQ.

Once you have created a Jms queue, you need to write classes to produce and consume messages that are transmitted via the newly created queue.

We will utilize Spring to transmit messages over JMS queues. Since this type of delivery is point to point, you need message producers at one end and message consumers at the other end.

JMS Message Producer

1. Create a context file called: ~/workspace/jms/src/main/resources/application-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    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-2.0.xsd
                        http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.noushin.spring.jms" />
    <context:annotation-config />

    <!-- ActiveMQ destinations to use -->
    <amq:queue id="destination" physicalName="TestQ" />
    
    <!-- ActiveMQ broker URL -->
    <amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:61616" />

    <!-- Spring JMS ConnectionFactory -->
    <bean id="singleConnectionFactory" 
          class="org.springframework.jms.connection.SingleConnectionFactory"
          p:targetConnectionFactory-ref="jmsFactory"/>
    
    <!-- Spring JMS Producer Configuration -->
    <bean id="jmsProducerTemplate" class="org.springframework.jms.core.JmsTemplate"
        p:connectionFactory-ref="singleConnectionFactory"
        p:defaultDestination-ref="destination"/>
        
</beans>


2. Create a class that produces messages and sends them over Jms. Lets call it MessageProducer.
package com.noushin.spring.jms.producer;

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

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;

@Component
public class MessageProducer {

   final static Logger logger = Logger.getLogger(MessageProducer.class);

   @Autowired
   private JmsTemplate jmsTemplate;

   public void produce() throws Exception {
      
      if (jmsTemplate != null) {
         MessageCreator mc = new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
               try {
                  TextMessage message = session.createTextMessage("This is a message.");
                  return message;
               } 
                catch (JMSException je) {
                  logger.error("JMS Exception : ", je);
                  return null;
               }
            }
         };
         jmsTemplate.send(mc);
      }
   }
}

3. Here is the pom.xml to successfully run this example
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.noushin.spring</groupId>
    <artifactId>jms</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>jms</name>
    <url>http://maven.apache.org</url>

    <properties>
        <activemq.version>5.2.0</activemq.version>
        <junit.version>4.10</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>3.2.0.RELEASE</spring.version>
    </properties>

    <repositories>
        <repository>
            <id>springsource-repo</id>
            <name>SpringSource Repository</name>
            <url>http://repo.springsource.org/release</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
                <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-core</artifactId>
            <version>${activemq.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-optional</artifactId>
            <version>${activemq.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.7</version>
        </dependency>
    </dependencies>
</project>

4. After running MessageProducer.main, you should see a message added to TestQ queue.

5. Use ActiveMQ admin console to verify the above steps: http://localhost:8161/admin/queues.jsp

JMS Message Consumer

Now we need to write a class that consumes the messages in the queue waiting to be processed. In this example, I will create a second project.

1.   Create a context file called: ~/workspace/jmsc/src/main/resources/application-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jms="http://www.springframework.org/schema/jms" 
    xmlns:p="http://www.springframework.org/schema/p"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
                        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd">

    <context:component-scan base-package="com.noushin.spring.jms" />
    <context:annotation-config />

    <!-- ActiveMQ destinations to use -->
    <amq:queue id="destination" physicalName="TestQ" />

    <!-- ActiveMQ broker -->
    <amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:61616" />

   <!-- JMS Consumer Configuration -->
    <bean id="jmsConsumerConnectionFactory" 
          class="org.springframework.jms.connection.SingleConnectionFactory"
          p:targetConnectionFactory-ref="jmsFactory" />
        
    <jms:listener-container container-type="default" 
                            connection-factory="jmsConsumerConnectionFactory"
                            acknowledge="auto">
        <jms:listener destination="TestQ" ref="messageConsumer" />
    </jms:listener-container>

</beans>

2.  Create a class that consumes messages as they arrive on the queue. Let's call it MessageConsumer.
package com.noushin.spring.jms.consumer;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

@Component
public class MessageConsumer implements MessageListener {

   final static Logger logger = Logger.getLogger(MessageConsumer.class);

   private int numOfMessages = 0;

   public void onMessage(Message message) {
      try {
         numOfMessages++;
         if (message instanceof TextMessage) {
            TextMessage tm = (TextMessage) message;
            String msg = tm.getText();
            logger.info(">>>>>Processed message: " + msg + " - numOfMessages : " + numOfMessages);
         }
      } catch (JMSException e) {
         logger.error(e.getMessage(), e);
      }
   }
}

3. You can use the same pom file you used for MessageProducer. Make sure to change your project name in the pom file.

4. To test your app, write a JUnit
package com.noushin.spring.jmsc;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppTest {

   @Test 
   public void testApp() {
         ApplicationContext ctx = new ClassPathXmlApplicationContext("application-context.xml");
    }
}

5. As soon as application context is initialized, go to ActiveMQ admin console and notice the messages you produced in the first project are now removed from the queue. You should also see messages logging the results of executing onMessage method in MessageConsumer class.
2013-01-10 11:50:24,365 [org.springframework.jms.listener.DefaultMessageListenerContainer#0-1] INFO  com.noushin.spring.jms.consumer.MessageConsumer - Processed message: this is a test. - numOfMessages : 2

6. That's all folks. Have fun with Jms :)

Saturday, January 12, 2013

Spring & Testing

I think unit testing Spring components is really cool. You can have a separate application context just for testing purposes, which will not conflict with your application's runtime context when developing.

Here is a couple of steps you need to take to test your components using Spring testing.

1. Add the following dependency to your pom file:
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>org.springframework.test</artifactId>
    <version>${spring.version}</version>
    <scope>test</scope>
</dependency>
where
    <spring.version>3.2.0.RELEASE</spring.version>

2. In your test/resources folder, your need to a create a package matching the your Test class, and create a context file with a name that matches your Test class name.

Here is an example:

Lets say you are testing a class called MessageProducer.
package com.noushin.spring.jms.producer;

import java.io.IOException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;

@Component
public class MessageProducer {

   final static Logger logger = Logger.getLogger(MessageProducer.class);

   @Autowired
   private JmsTemplate jmsTemplate;

   public void produce() throws Exception {
      
      if (jmsTemplate != null) {
         MessageCreator mc = new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
               try {
                  String jmsMessage = FileUtils.readFileToString(FileUtils.toFile(this.getClass().getResource("/jms-message.txt")));
                  TextMessage message = session.createTextMessage(jmsMessage);
                  logger.info(">>>>>Sending message: " + jmsMessage);
                  return message;
               } 
               catch (IOException ioe) {
                  logger.error("File not found : ", ioe);
                  return null;
               }
               catch (JMSException je) {
                  logger.error("JMS Exception : ", je);
                  return null;
               }
            }
         };
         jmsTemplate.send(mc);
      }
   }
}

3. Here is your JUnit test case, ~workspace/jms/src/test/java/com/noushin/spring/jms/producer/MessageProducerTest.java
package com.noushin.spring.jms.producer;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MessageProducerTest {
   
   @Autowired
   protected MessageProducer producer;
   
   @Test
   public void testProduce() {
      try {
         producer.produce();
         assert(true);
      } 
      catch (Exception e) {
         e.printStackTrace();
      }
   }
}

4. Corresponding test context is at ~workspace/jms/src/test/resources/com/noushin/spring/jms/producer/MessageProducerTest-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    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.xsd
                        http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.noushin.spring.jms" />
    <context:annotation-config />

    <!-- ActiveMQ destinations to use -->
    <amq:queue id="destination" physicalName="TestQ" />
    
    <!-- ActiveMQ broker URL -->
    <amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:61616" />

    <!-- Spring JMS ConnectionFactory -->
    <bean id="singleConnectionFactory" 
          class="org.springframework.jms.connection.SingleConnectionFactory"
          p:targetConnectionFactory-ref="jmsFactory"/>
    
    <!-- Spring JMS Producer Configuration -->
    <bean id="jmsProducerTemplate" class="org.springframework.jms.core.JmsTemplate"
        p:connectionFactory-ref="singleConnectionFactory"
        p:defaultDestination-ref="destination"/>
        
</beans>

Alternatively, if there is a context configuration that can be reused by test classes in different packages, you can specify its location on classpath:
@ContextConfiguration(locations={"classpath:/com/noushin/spring/jms/producer/MessageProducerTest-context.xml"})

5. If in Eclipse, right click your test class and run it as JUnit Test. :)