I recently got involved in a project where reliable communication between etherogenous platforms was required. Having already worked with JMS, I had to find a way to expose its services to the frontend(a PHP base web application). Some time before I had come across XFire, and had been looking for a chance to work on it. This was that chance. I know SOAP can sometimes be cumbersome, but XFire really makes its use easy, and PHP well supports it( we'll use the nusoap library). In this article I'll make an introduction on how to easily merge together OpenJMS,XFire and PHP to get a generic, immediately usable messaging system. If someone will be interested in it, further articles will follow on this topic.
Overview To make this as simple as possible, I'm going to cover not a line more than what is really needed to achieve our goal, so just a few words about the involved technologies.
JMS (java message service) is :"a messaging standard that allows application components based on the Java 2 Platform, Enterprise Edition (J2EE) to create,send, receive, and read messages. It enables distributed communication that is loosely coupled, reliable, and asynchronous"(ref.). What we need is a way to allow non-java technologies(in our case PHP) to access the functionalities exposed by JMS. This time we'll use SOAP.
SOAP(simple object access protocol) is :a protocol for exchanging XML-based messages over a computer network, normally using HTTP. SOAP forms the foundation layer of the Web services stack, providing a basic messaging framework that more abstract layers can build on. SOAP can be used to facilitate a Service-Oriented architectural pattern "(ref.).
Used platforms
The JMS implementation we're going to use is OpenJMS. There are plenty of other valid JMS implementations; personally I find OpenJMS to be a good choice, is open source and easy to use . SOAP services will be handled by XFire, which allows us to expose even POJOs as web services at a snap. PHP will be our front end platform. The PHP part just handles soap calls, and it's quite easy to adapt this part for other languages with soap extensions(basically every language has at least one).
Before we start writing code First thing to do is to download both OpenJMS(here) and XFire(here). From XFire we just need its libraries, but with OpenJMS we also need to start up the server. Nothing easier, we're not going to configure anything. Unzip OpenJMS distribution and just start it(startup.sh or startup.bat) from its bin directory.
Writing the OpenJms functionalities wrapper
We need to get/post a message from/to a queue/topic. We have 4 combination, 2 for getting, 2 for posting. Let's first define an interface:
Note that: 1) We're going to work only on text messages (more message types are available through JMS) 2) We're not going to handle exceptions here. The thrown exceptions will be magically encapsulated by XFire and sent back to the client in the soap response 3) The interface will be used by XFire to build the SOAP wsdl and service.
Here we initialize a connection and a session to the OpenJMS server running on localhost at port 3035. The URL used on line 20 depends on the OpenJMS server configuration. We're using the default, but keep in mind that OpenJMS could be on a different machine, different port, and could even use a different protocol(i.e. RMI). We're using generic items for : ConnectionFactory,Connection and Session. Specific items for both topics and queues exist, but in this case we're trying to simplify things. On line 25, the first parameter(false) tells that we're not going to use a transactioned session.
The above snippet implements the 4 methods that will be exposed as SOAP methods. As you can see, message retrieving is demanded to the generic method "getMsg"(explained below), as the generic session created can be used by both topics and queues, so the specific methods just need to create a proper MessageConsumer. Same thing for posting messages, a MessageProducer is created this time and passed to the generic function "postMsg". On "getMsgFromTopic" we use an instance scoped MessageConsumer because a DurableSubscriber is valid at a session level, so we try to create it and if it already exists an (ignored) exception is thrown and trapped. Not very elegant...
These are the generic methods we were talking about. They just post/retrieve a message. In both cases a text message(the received or the sent) is passed back to the caller. On "getMsg" I'm using a TextMessage array as return type to make the function compatible with an eventual multi message retrieving method.
This is a very basic implementation, lots of things are missing and other should be considered(finalizing, closing sessions/connections etc.), but it's maybe good enough as a starting point. Let's go on with the XFire section now.
Exposing the service via XFire
The following simple class is everything we need to expose our OpenJMSService interface as a SOAP service through XFire.
Xfire allows us to avoid the hassle of setting up a servlet engine, by providing an embedded http server(Jetty). The above code is pretty self-explaining; after creating an XFire object through a factory, we register a service (our OpenJMSService service) on it and just start the embedded http server. That's all!!! Just put in your classpath the required XFire and OpenJMS jars and launch our Main class. Main.java, OpenJMSService.java and OpenJMSServiceImpl.java must reside in the same directory(you can arrange them differently in packages if you prefer).
Now we can call our service with any SOAP client. Let's see how to do it with PHP.
Accessing the SOAP service from PHP
First, from a system shell start OpenJMS admin GUI (admin.sh or admin.bat in bin) and create a queue with name "myqueue" and a topic with name "mytopic" containig a consumer named "cons1". The following simple class is everything we need to access our OpenJMSService SOAP service from PHP.
Nusoap is an excellent soap library for PHP. PHP5 includes its own soap classes. You can use them if you prefer, only minimal changes are needed. After creating a soap object, we retrieve the proxy, and just call the exposed methods. We'll get back an array containing the returned object, which can also be an error.