Kirk Evans Blog

.NET From a Markup Perspective

Use JMS and Azure Event Hubs with Eclipse

This post will show how to use Eclipse to build a web application that uses JMS to send AMQP 1.0 messages to Azure Event Hubs.

Background

I have been working on a project that leverages Azure Event Hubs, Azure Stream Analytics, and other related technologies to demonstrate a highly scalable IOT platform.  Since many of the customers that would potentially use this solution are already familiar with Java, I decided to build a demo using Eclipse.

This post uses an environment that includes Tomcat, Eclipse, and the Azure Toolkit for Eclipse.  For instructions on creating this environment, see my blog post Creating an Eclipse Development Environment for Azure

This post follows the instructions in the article How to Use JMS with AMQP 1.0 in Azure with Eclipse, with a few slight changes and deeper explanations.

Create the Web Project

In Eclipse, choose File / New / Dynamic Web Project.

image

I’ll name it JMSDemo.  On the Dynamic Web Project page, click the New Runtime button to define a new runtime.  I am using Apache Tomcat 8.0.

image

On the next page, we configured the Tomcat Server by providing the root directory where Tomcat is installed, and we choose the installed JRE.

image

Click Finish.  The Dynamic Web Project looks like this:

image

Add Some JSP Pages

Right-click the new WebContent folder and add a new JSP page named index.jsp.

Index.jsp
  1. <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  2.     pageEncoding="ISO-8859-1"%>
  3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  4. <html>
  5. <head>
  6.     <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  7.     <title>AMQP 1.0 message sender</title>
  8. </head>
  9. <form method="post" action="sendMessages.jsp">
  10.     <br />
  11.     This sample sends a message to a queue.
  12.     <br /><br />
  13.     <div>Device ID: <input type="text" name="deviceID" /></div>
  14.     <div>Temperature: <input type="text" name="temperature" /></div>
  15.  
  16.     <input type="submit" name="submit" value="Send Message" />
  17. </form>
  18. </html>

Right-click the new WebContent folder again and add a new JSP page, this time named sendMessages.jsp.

sendMessages.jsp
  1. <%@ page import="MyPackage.MyAMQPSender" language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  3. <html>
  4. <head>
  5.     <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
  6.     <title>AMQP 1.0 page 2</title>
  7. </head>
  8. <body>
  9.     <%
  10.     String message = "{'DeviceId':" + request.getParameter("deviceID") + ", 'Temperature':" + request.getParameter("temperature") + "}";
  11.     out.print("Sending message ——-> " + message );
  12.     out.print("<br />");
  13.  
  14.     try
  15.     {
  16.     // Send the message.
  17.  
  18.     MyAMQPSender.postMessages(message);
  19.     out.println("Message sent successfully\n");
  20.     }
  21.     catch (Exception e)
  22.     {
  23.     out.println("Error occurred while sending message to queue.\n");
  24.     out.println(e);
  25.     }
  26.     %>
  27. </body>
  28. </html>

Now that we’ve created the JSP pages, we need to implement the MyAMQPSender class.

Have Some Class

Right-click the project node in the Project Explorer pane and choose New Class.  Use the following properties:

  • Package: MyPackage
  • Name: MyAMQPSender

image

Click Finish to close the dialog.  Now edit the contents of the MyAMQPSender.java class:

MyAMQPSender.java
  1. package MyPackage;
  2.  
  3. import javax.jms.*;
  4. import javax.naming.Context;
  5. import javax.naming.InitialContext;
  6.  
  7. import java.io.UnsupportedEncodingException;
  8. import java.util.Hashtable;
  9.  
  10. public class MyAMQPSender {
  11.     private static Connection connection;
  12.     private static Session sendSession;
  13.     private static MessageProducer sender;
  14.     private static Context context;
  15.     
  16.     public static void postMessages(String textMessage) throws Exception
  17.     {
  18.         try
  19.         {
  20.             init();
  21.             sendTextMessage(textMessage);
  22.             close();
  23.         }
  24.         catch (Exception e)
  25.         {
  26.             System.out.println("Exception: " + e);
  27.         }
  28.     }
  29.  
  30.     private static void sendTextMessage(String textMessage) throws JMSException
  31.     {
  32.         BytesMessage message = null;
  33.         byte[] utf8bytes;
  34.         try {
  35.             utf8bytes = textMessage.getBytes("UTF8");
  36.         
  37.         } catch (UnsupportedEncodingException e) {
  38.             // TODO Auto-generated catch block
  39.             e.printStackTrace();
  40.             throw new JMSException("Unable to convert to UTF8");
  41.         }
  42.         
  43.         message = sendSession.createBytesMessage();
  44.         message.writeBytes(utf8bytes);
  45.         sender.send(message);
  46.         
  47.         System.out.println("Sent message with JMSMessageID = " + message.getJMSMessageID());
  48.     }
  49.     
  50.     private static void init()
  51.     {
  52.         try
  53.         {
  54.             // Configure JNDI environment
  55.             Hashtable<String, String> env = new Hashtable<String, String>();
  56.             env.put(Context.INITIAL_CONTEXT_FACTORY,
  57.             "org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory");
  58.             env.put(Context.PROVIDER_URL, "servicebus.properties");
  59.             context = new InitialContext(env);
  60.     
  61.             // Lookup ConnectionFactory and Queue
  62.             ConnectionFactory cf = (ConnectionFactory) context.lookup
  63.                     ("SBCONNECTIONFACTORY");
  64.             Destination queue = (Destination) context.lookup("QUEUE");
  65.     
  66.             // Create Connection
  67.             connection = cf.createConnection();
  68.     
  69.             // Create sender-side Session and MessageProducer
  70.             sendSession = connection.createSession(false,
  71.                     Session.AUTO_ACKNOWLEDGE);
  72.             sender = sendSession.createProducer(queue);
  73.         }
  74.         catch (Exception e)
  75.         {
  76.             System.out.println("Exception: " + e);
  77.         }
  78.     }
  79.  
  80.     private static void close() throws JMSException
  81.     {
  82.         sender.close();
  83.         sendSession.close();
  84.         connection.close();
  85.     }
  86.  
  87.  
  88. }

When you add the class, you are going to see red squiggly underlines and errors all over the place.  This is because we need to leverage additional libraries.  Because we have already installed the Azure Toolkit for Eclipse (see my blog post Creating an Eclipse Development Environment for Azure for step-by-step instructions), we can easily add the Apache Qpid Client Libraries for JMS provided by MS Open Tech. 

To add the package, right-click the project and choose Properties.  Next, go to the Java Build Path node, then go to the Libraries tab.  Click the Add Library button and choose Package for Apache Qpid Client Libraries for JMS (by MS Open Tech)

image

Click Next, and then ensure the Include in the project deployment assembly option is selected.

image

Click Finish, then OK to close the Properties dialog.  Your errors should now be gone.

Create an Event Hub

Using your Azure subscription (don’t have one? You can create a free trial subscription), go to https://manage.windowsazure.com and go to the Service Bus tool.

image

Click the New button on the bottom left of the screen and choose Service Bus / Event Hub / Custom Create.

image

Give it a name, choose the region closest to you, and create a new namespace.

image

On the next screen, specify 16 for the partition count and 7 for the message retention. 

image

Choose OK to create the event hub.  Once the event hub is created, go to the Event Hubs tab (you may need to refresh the screen) and click on the event hub that you created, then click on Connection Information.

image

We are going to define a shared access signature policy that enables us to only send messages to the event hub.  Create a new policy named SendPolicy with Send permission.

image

Click Save, and the Shared Access Key Generator is shown.  Copy the primary key value.

image

Create the servicebus.properties File

Go back to Eclipse.  Right-click the project and choose New / File.  Name the file servicebus.properties.

image

The servicebus.properties file is used to register a ConnectionFactory in JNDI.  Update its contents:

Code Snippet
  1. # Register a ConnectionFactory in JNDI using the form:
  2. # connectionfactory.[jndiname] = [ConnectionURL]
  3. connectionfactory.SBCONNECTIONFACTORY = amqps://SendPolicy:your_url_encoded_key@your_namespace.servicebus.windows.net
  4.  
  5. # Register some queues in JNDI using the form:
  6. # queue.[jndiName] = [physicalName]
  7. queue.QUEUE = your_queue

In the connection string, the SendPolicy value is the same name as the SAS policy that we added in the Azure management portal.  The “your_url_encoded_key” is the key that we copied from the Shared Access Key Generator, but its value needs to be URL-encoded.  One technique for creating a URL encoding is via the web site http://www.w3schools.com/tags/ref_urlencode.asp.  Finally, the “your_queue” value needs to be updated with the name of the Event Hub that you created.

Here is a redacted version of my servicebus.properties file:

Code Snippet
  1. # Register a ConnectionFactory in JNDI using the form:
  2. # connectionfactory.[jndiname] = [ConnectionURL]
  3. connectionfactory.SBCONNECTIONFACTORY = amqps://SendPolicy:C9OIXEiREDACTEDWhIQ%3D@amqpdemo-ns.servicebus.windows.net
  4.  
  5. # Register some queues in JNDI using the form:
  6. # queue.[jndiName] = [physicalName]
  7. queue.QUEUE = amqpdemo

We’ll use this file as part of our deployment project. 

Create an Azure Deployment Project

We’re almost there… right-click the project in Eclipse and choose Azure / Package for Azure.  I name the deployment project JMSDemoDeployment.

image

On the JDK page, I provide the path to my installed JDK.  The storage account setting is left as “(auto)”.

image

On the Server tab, I provide the root path to the location where I unzipped Apache Tomcat to, and choose the Type as Apache Tomcat 8.  I leave the storage account as “(auto)”.

image

Click Finish to close the wizard.

Now right-click the deployment project and choose Properties, then go to the Azure / Roles node.  Highlight the worker role and click Edit.

image

Go to the Component tab and choose Add.  In the Azure Role Component dialog, use the File button to locate your servicebus.properties file.  The import method is copy, and the “as name” value is “servicebus.propertiesUnder the deploy from package settings, the method is copy, and the “to directory” value is %CATALINA_HOME%\bin.

image

Click OK, OK, OK to close the windows.  Click the deployment project’s node in the Project Explorer pane and choose the Run in Azure Emulator button.

image

After some time, you will see the output in the compute emulator that Java is running.

image

Go to http://localhost:8080/JMSDemo/ and you will see the following JSP page served by Tomcat within the Azure Emulator.

image

Enter some values then click the button and we should see the following:

image

We successfully sent a message to Event Hub!

In order to receive the message, we need some code to process the message.  This is beyond the scope of this article, but you can refer to the Event Hub documentation for a walkthrough creating a cross-platform messaging solution between JMS and .NET.  You can also learn about using Visual Studio to receive messages with EventProcessorHost to create a highly scalable solution to process massive amounts of data with parallel actions.

For More Information

Creating an Eclipse Development Environment for Azure

How to Use JMS with AMQP 1.0 in Azure with Eclipse

Free trial Azure subscription

Cross-platform messaging solution between JMS and .NET

Receive messages with EventProcessorHost