BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles BlazeDS and JMS for PHP Developers, Part 1

BlazeDS and JMS for PHP Developers, Part 1

BlazeDS is an open source project from Adobe that allows you to connect your Adobe Flex applications with data services. The Java Messaging Service (JMS) is a method of communicating with services written in Java. This article will help you discover the advantages of using JMS and how you can use BlazeDS to communicate with your Java services via JMS from your Flex applications.

Overview of JMS

JMS is an application programming interface (API) standard that allows you to send and receive messages using Java Platform, Enterprise Edition (Java EE). Many vendors in the Java community offer both commercial and open source implementations, which gives you the freedom to choose the vendor that suits your needs.

Benefits of Using JMS

There are several benefits to using JMS, including abstraction, guaranteed delivery, asynchronous messaging, and failover and high availability. Abstraction is a key benefit, because JMS consumers and producers don’t have dependencies on each other. Code in either the consumer or the producer can change, but as long as the JMS messages stay consistent, the connection between the two of them won’t break.

JMS implementations typically support two forms of message – persistent and non-persistent. Non-persistent messages are processed in memory and are very fast, but may be lost in the event of a system failure. Persistent messages are written to disk and are therefore recoverable in the event of a system failure, but are correspondingly slow. The JMS provider guarantees the delivery of all persistent messages exactly once regardless of whether transactions are used or not. Asynchronous messaging provides the ability to send a message and move on without waiting for a response. Asynchronous messaging is useful in web applications, where a request can be sent and the application will not block or hang until the response is complete. This type of messaging is ideal for processes that may take a long time, such as an application that finds hotel reservations or builds dynamic documents.

When properly configured, many JMS implementations provide the ability for failover and high availability. So, if one server running JMS becomes unavailable, another server with the same JMS implementation can handle the load. Failover is ideal for applications that must be continuously available for business continuity.

Benefits of Using JMS with BlazeDS

When using BlazeDS to connect to JMS, your Flex application gains all of the benefits of using JMS, which is something that in the past was usually reserved for web applications implemented completely in platforms such as Java EE.

Sending messages to your services with BlazeDS and JMS allows your Flex application to be developed more independently of the services. The services JMS implementation can even change vendors, and your Flex application will not have to change.

Another key benefit of using JMS with BlazeDS is that if you were using Hypertext Transfer Protocol (HTTP) services before using JMS, very little code in your Flex application needs to change. BlazeDS handles all of the details for you.

Understanding JMS Messaging

JMS offers two types of messaging models, point-to-point (P2P), and publish-and-subscribe (pub/sub). In simple terms publish-and-subscribe is intended for a one-to-many broadcast of messages, while point-to-point is intended for one-to-one delivery of messages.

Messaging Clients in JMS are referred to as JMS clients, and the messaging system – the Messaging Orientated Middleware (MOM) – is the JMS provider. In addition a JMS client that produces a message is called a producer, whilst one that receives a message is called a consumer.

Point-to-point

The point-to-point messaging model allows JMS clients to send and receive messages both synchronously and asynchronously via virtual channels called queues. JMS queuesare configured containers for messages, much like a mail box. A JMS sender puts a message in a JMS queue similar to a sender putting an envelope in the mail box. Later, a receiver gets the message from the mail box and takes some type of action with it. Like envelopes in mail boxes, messages sent using JMS queues sent from a sender are read by a single receiver. The P2P model is traditionally a pull- or polling-based model where messages are requested from a queue instead of being pushed to a client, though JMS does also support a push model for P2P similar to that used by the publish-and-subscribe model.

One example of where you might use a JMS message and a Flex client is when initiating a process from the Flex client that takes the services tier some time to complete, such as creating an on-demand PDF file with custom content.

Publish-and-subscribe

In pub/sub, one producer can send a message to many consumers through a virtual channel called a topic. Topics are similar to printed magazines: Subscribers register with the publisher to receive a particular magazine. The publisher sends multiple copies of the same magazine to different subscribers on a periodic basis. The publish-and-subscribe model is by and large a push-based model, where messages are broadcast to consumers without them having to request or poll the topic for them.

Using JMS topics, you can send information from the services tier to many Flex clients at once. One example of a JMS topic in a Flex client is user presence. In this example, when a user logs in to the system, the services send a message via a JMS topic to tell all the other Flex clients that the user is online.

JMS messages

JMS makes using topics and queues easier by also providing a way to build messages. When writing Java code, the JMS API provides methods of properly constructing the messages in a consistent manner. An advantage of using BlazeDS is that you don’t have to worry about how to create the messages correctly—BlazeDS does that for you.

JMS implementations

Several JMS implementations are available both as open source projects and for commercial offerings. This article uses the ActiveMQ implementation of JMS, which is an open source project from the Apache Software Foundation and is licensed with the Apache license. In addition to providing a JMS implementation, it provides the ability to send messages to it using other languages, including PHP. Other JMS implementations include IBM's WebSphere MQ, JBoss Messaging, Open Message Queue, and OpenJMS.

Configuring BlazeDS for JMS

Configuring BlazeDS to use JMS consists of setting up the proper endpoint and setting the JMS properties in your destination configuration. The examples in this article use a simple Flex interface, the BlazeDS libraries, and an installation of ActiveMQ.

The JMSAdapter

BlazeDS uses the JMSAdapter to convert messages between the Flex application and the JMS implementation after the messages reach the MessageBroker servlet. The adapter is configured in the messages-config.xml file:

<adapters>
	<adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
	<adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter" />
</adapters>

Endpoint differences

It is important to remember that the BlazeDS Java libraries and the MessageBroker servlet still need to be configured and running a web container. Because the MessageBroker must be running, the destination using the JMS Adapter still uses an Adobe Action Message Format (AMF) channel. The endpoints used in this example are:

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service-include file-path="messaging-config.xml" />
</services>

<security/>

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:8161/blazeds/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>

<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="http://{server.name}:8161/blazeds/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
</channel-definition>

<channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:8161/blazeds/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>true</polling-enabled>
<polling-interval-seconds>1</polling-interval-seconds>
</properties>
</channel-definition>
</channels>
</services-config>

JMS Properties

JMS requires some of its own configuration, like the Java Naming and Directory Interface (JNDI) name of the connection factory used to build the connection to JMS. The name of the queue or topic is also resolved using JNDI. JNDI is a Java API that allows the physical locations of resources to be abstracted into names. A common use of JNDI is database naming, where the application uses a JNDI name to get a database connection.

In addition to JNDI names for the connection factory and the topic or queue, JMS variables allow you to define user credentials, message type, destination type, and more. Shown here are the JMS properties used in this example:

	<destination id="my-jms-destination">
		<properties>
<jms>
<destination-type>Topic</destination-type>
<message-type>javax.jms.TextMessage</message-type>
<connection-factory>ConnectionFactory</connection-factory>
<destination-jndi-name>dynamicTopics/MyTopic</destination-jndi-name>
<delivery-mode>NON_PERSISTENT</delivery-mode>
<message-priority>DEFAULT_PRIORITY</message-priority>
<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
<initial-context-environment>
<property>
<name>Context.INITIAL_CONTEXT_FACTORY</name>
<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
</property>
<property>
<name>Context.PROVIDER_URL</name>
<value>tcp://localhost:61616</value>
</property>
</initial-context-environment>
</jms>
		</properties>
		<channels>
			<channel ref="my-amf" />
		</channels>
		<adapter ref="jms" />
	</destination>

See the BlazeDS documentation for further explanation of additional properties.

Messaging from BlazeDS

To try the examples in this article, follow the steps outlined here before creating your test Flex project. Download the ActiveMQ binary distribution and extract it. You can start ActiveMQ and go through the verification steps in the ActiveMQ documentation, but then shut ActiveMQ down before proceeding.

Download the BlazeDS binary distribution

Begin by downloading the BlazeDS binary:

Extract the files from the BlazeDS web archive (WAR) into the webapps directory of the ActiveMQ distribution. Doing so allows you to run this example using only ActiveMQ.

Set up the web application context in Jetty, which is the web container that comes with the ActiveMQ binary distribution. Add the context by modifying the jetty.xml file found in the conf folder of your ActiveMQ distribution:
<webAppContext contextPath="/blazeds" resourceBase="${activemq.base}/webapps/blazeds" logUrlOnStart="true"/>
Start ActiveMQ, and point your browser to http://localhost:8161/blazeds. You should see a listing of the directories in BlazeDS. This step verifies that you have the BlazeDS WAR in the correct place and configured properly.

Create a Flex project that contains two configuration files in the src/WEB-INF/flex folder: messaging-config.xml and services-config.xml. Use the examples earlier in this article to set up the JMS destination and add the JMSAdapter.

Copy the services-config.xml and messaging-config.xml files from your Flex application project into the webapps/blazeds/WEB-INF/flex folder in the ActiveMQ location.

Sending a message (example)

To test the messages sent from Flex, this article uses the command-line examples that come with ActiveMQ. To send a message from your Flex application to a command-line client, you can build a producer in your Flex code, then run the command-line example with some different parameters and see the results.

You will need to create a producer in your Flex application that will send the message. As a destination, the new producer will use the my-jms-destination configured in the messaging-config.xml file. Here is an example of a very simple Flex form with a producer:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="consumer.subscribe()">
	<mx:Script>
		<![CDATA[
			import mx.messaging.messages.IMessage;
			import mx.messaging.messages.AsyncMessage;
			import mx.messaging.events.MessageEvent;
			
			private function sendMessage(value : String) : void
			{
				var message : IMessage = new AsyncMessage();
				message.body = value;
				producer.send(message);
			}
		]]>
	</mx:Script>
	<mx:Producer id="producer" 
		channelConnect="trace('producer connected')"
		destination="my-jms-destination"
		fault="trace(event.faultDetail)"
		/>
	<mx:VBox>
		<mx:Form>
		<mx:FormItem label="Message:">
		<mx:TextInput id="textInput" />
		</mx:FormItem>
		</mx:Form>
	<mx:Button id="sendButton" label="Send Message" click="sendMessage(textInput.text)" />
	</mx:VBox>
</mx:Application>

The form has a button, sendButton, that when clicked calls the sendMessage() function. The sendMessage() function creates an AsyncMessage, which is an implementation of the IMessage interface, and then sends the message using the producer’s send() method. The trace() method assigned to the fault attribute prints the given message out to the console, which helps you debug any issues that might occur.

To verify the message, run the Flex application. Switch to the command line, and run the consumer example that comes with ActiveMQ:

$ ant consumer -Dtopic=true -Dsubject=MyTopic -Dmax=2 

If you put a message in the input box and click Send Message, the message appears in the command-line consumer. After receiving two messages, the command-line consumer example quits.

Receiving a message (example)

To receive JMS messages, you build a consumer that uses a destination configured for JMS. For simplicity in this example, the consumer re-uses the same destination the producer uses to send a message. The code to create the consumer looks like this:

<mx:Consumer id="consumer" channelConnect="trace('consumer connected')" 
	channelFault="trace(event.faultDetail)"
	fault="trace(event.faultDetail)" 
	destination="my-jms-destination" 
	message="messageHandler(event)" />
...
<mx:TextArea id="results" width="100%" />

And the code for the function in the <mx:Script> block to handle the message looks like this:

private function messageHandler(event : MessageEvent) : void 
{ 
	results.text += event.message.body + "\n";            
}

After adding the code, start the Flex application. When the Flex application has started, run the producer example on the command line:

ant producer -Dtopic=true -Dsubject=MyTopic -Dmax=5 

The messages appear in the Flex text area.

Integration with PHP via REST

Web services are a way of integrating web applications in PHP and Java. The Representational State Transfer (REST) web services are relatively easy to implement in Java as servlets and in PHP as scripts.

REST overview

REST web services are simpler than SOAP web services, as they do not have a standard for the message format (for example, a single web page is an example of a REST web service). The URL is the service endpoint, and the web page that is returned is the response of the service.

One of the advantages of writing a REST web service is that many types of clients can use it. The REST web service shown here, written as a Java servlet, can be called by other languages aside from PHP with no further modification to the service.

The Java servlet example

This example uses a simple Java servlet that will act as a REST web service. The servlet handles a PUT request, getting the message from the PUT contents and the various parameters, such as the subject (the name of the JMS destination), whether the message is a topic, and the URL to use for the JMS connection.

package com.example.servlets;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.util.IndentPrinter;

public class MessageService extends HttpServlet {
    private static final long serialVersionUID = -8129137144911681674L;

    private Destination destination;
    private String user = ActiveMQConnection.DEFAULT_USER;
    private String password = ActiveMQConnection.DEFAULT_PASSWORD;

    @Override
    protected void doPut(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        Connection connection = null;            

        try {
            boolean isTopic = "true".equals(request.getParameter("topic"));
            boolean isPersistent = "true".equals(request.getParameter("persistent"));
            String subject = request.getParameter("subject");
            String url = URLDecoder.decode(request.getParameter("url"), "UTF-8");
            String messageText = "";

            BufferedReader buffer = new BufferedReader(new InputStreamReader(request.getInputStream()));

            messageText = buffer.readLine();

            System.out.println("Using URL:  <" + url + ">");
            System.out.println("Using Subject:  <" + subject + ">");
            System.out.println("Sending Message Text:  <" + messageText + ">");

            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
            connection = connectionFactory.createConnection();
            connection.start();

            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            if (isTopic) {
                destination = session.createTopic(subject);
            } else {
                destination = session.createQueue(subject);
            }

            // Create the producer.
            MessageProducer producer = session.createProducer(destination);
            if (isPersistent) {
                producer.setDeliveryMode(DeliveryMode.PERSISTENT);
            } else {
                producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            }

            TextMessage message = session.createTextMessage(messageText);

            producer.send(message);

            System.out.println("Done.");

            // Use the ActiveMQConnection interface to dump the connection
            // stats.
            ActiveMQConnection c = (ActiveMQConnection)connection;
            c.getConnectionStats().dump(new IndentPrinter());

        } catch (Exception e) {
            System.out.println("Caught: " + e);
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (Throwable ignore) {
            }
        }

    }

}

As an alternative to using ActiveMQ-specific classes, you can write the code to use JNDI. For more information about using JNDI to get the connection, see the ActiveMQ documentation.

The compiled servlet .class file must reside in the webapps directory of ActiveMQ in an application folder (for example, messageservice). After adding the new directory, make an entry in the conf/jetty.xml file (similar to step 4 earlier).

The PHP example

The PHP script calls the REST service using the CURL library to post data to the REST service URL (handled by the Java servlet). If your version of PHP doesn’t have the CURL library installed, you can use Asynchronous JavaScript and XML (Ajax) to make the REST call. In a production web application, Ajax might provide a better user experience.

The PHP script contains a form that posts to itself. When the form is submitted, the script takes the message from the text box and PUTs it into the REST URL as data. Here’s an example:

<?php

if (isset($_POST['submit'])) {

$url = 'http://localhost:8161/messageservice/?url=tcp://localhost:61616&subject=MyTopic&topic=true';

function doPut($ch, $a_data) {
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Length: '.strlen($a_data)));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $a_data);
    return curl_exec($ch);
}

$data = $_POST['message'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
$response = doPut($ch, $data);

echo 'Message sent';
} else {
?>
<html><head><title>Post a message</title></head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF'];?>" method="post">
<p><label for="message">Message:
<input type="text" id="message" name="message" />
</label></p>
<input type="submit" value="submit" name="submit" id="submit" />
</form>
</body>
</html>
<?php
}
?>

The topic, subject, and URL of the JMS server are passed on the URL assigned to $url.

Example in Action: Receiving/Sending with BlazeDS

To see the example in action, start the Flex application. With the application open, open the PHP script in a different web browser. Add a value in the message box, and then click Submit. The value you put in the message box appears in the text box in the Flex application.

Summary

JMS is a messaging service that supports topics and queues and has many features that make it a good choice for robust messaging. BlazeDS allows your Flex applications to send messages from Flex clients to JMS implementations without much effort.

PHP applications can connect to JMS in a few different ways. One way to integrate PHP with your Java applications is by using REST web services—web services that provide a relatively simple interface for clients to use. With REST services implemented in Java, you can leverage existing Java code to send messages with JMS.

Part 2 of this series will cover using other messages for integrating PHP with Flex through JMS: the PHP/Java Bridge and Stomp.

About the Author

Nathan A. Good lives in the Twin Cities area of Minnesota. Professionally, he does software development, software architecture, and systems administration. When he's not writing software, he enjoys building PCs and servers, reading about and working with new technologies, and trying to get his friends to make the move to open source software. He's written and co-written many books and articles, including Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach and Foundations of PEAR: Rapid PHP Development.

Rate this Article

Adoption
Style

BT