martes, abril 15, 2008

There are four new colours on the rainbow, an old man takes polaroids ...


Spring Integration is a new project from Spring Framework developers. As it says on its website:
"Provides a programming model to support the well-known Enterprise Integration Patterns (...) and enables simple messaging within Spring-based applications and integrates with external systems via simple adapters."
In this entry, but is supposed that some basic concepts about Spring Integration are known. What we are going to explain is the development of a new adapter.
But what is an Adapter? In Spring Integration, an adapter is a component that interacts with external systems (servers, other components ...). As its name suggests, an adapter adapts the input of an external component and it transforms to something that can be sent through spring-integration pipe, and also adapts what is received from the pipe for sending to external component.
That pipe is MessageChannel class. Spring Integration is built with different adapters, JMS, RMI, Files, Mail, ... That's good while you are working with systems that required adapters are already implemented, but, what's happens if your application requires communication with an external system which an adapter has not already developed? The solution is easy, implement yourself the adapter.
That's what I am going to explain, how to implements a TargetAdapter (adapter for output messaging). In next entry would be explain how to implement a SourceAdapter (adapter for incoming messages).
Only two classes are really important org.springframework.integration.handler.MessageHandler that is used for handle the messages, and org.springframework.integration.message.AbstractMessageMapper that transform:
  1. From specific input to Spring Integration Message.
  2. From Spring Integration Message to specific output.

In this example, integration to instant messaging system will be used. The basic idea is develop a system that sent a message from Spring Integration to a XMPP server (a.k.a Jabber) user account.
Full schema of application is:

For running, apart from Spring/Spring Integration jars, JDK ..., two programs and an API are required:
  1. OpenFire, that's a XMPP server. XMPP is an open, XML-inspired protocol for near real time, extensible instant messaging (IM) and presence information. http://www.igniterealtime.org/projects/openfire/index.jsp
  2. Spark, a client for communicating with XMPP server. http://www.igniterealtime.org/projects/spark/index.jsp
  3. Smack, an API for communicating with XMPP server. http://www.igniterealtime.org/projects/smack/index.jsp

First install OpenFire server and add two users:
- login) logger password) logger
- login) incidence password) incidence
Second, install Spark, and then login with logger account and add incidence user as buddy and logout, after that login with incidence and add logger user as buddy, and don't logout.
Third, put smack jar and its dependencies into class path (Spring dependencies are there too).
Now, start the development of two most important classes:
class IMTargetAdapter implements MessageHandler, InitializingBean


Acts as an adapter through internal messages to external messages. The most important method is handle.


public Message handle(final Message msn{
final IMMessage iMessage = this.textMessage
.fromMessage((Message) msn); (1)
iMessage.setTo(this.getReceiver());
try {
this.xmppSender.send((SimpleIMMessage) iMessage); (2)
} catch (final IMException e) {
throw new MessageHandlingException("Error while sending message.",
e); (3)
}
return null;
}
(1) using a MessageMapper the spring integration message class (msn variable) is transformed to specific external system messages.
(2) using xmppSender (Facade for accessing Smack Library [out of scope of this entry]), the message is sent.
(3) case that an error occurs, MessageHandlingException must be thrown.

And finally class TextIMMessageMapper extends
AbstractMessageMapper
That has two important methods:

public IMMessage fromMessage(final Message msn) { (1)
final SimpleIMMessage simpleIMMessage = new SimpleIMMessage();
simpleIMMessage.setText(msn.getPayload());
return simpleIMMessage;
}
public Message toMessage(final IMMessage msn) { (2)
return new StringMessage(((SimpleIMMessage) msn).getText());
}
(1) transforms spring integration message to external message. SimpleIMMessage is a class that wraps messages that are sent using Smack API.
(2) transforms an external message to spring integration message (used in SourceAdapters).
Then it is time to configure the Spring XML file for configure channels and TargetAdapters so messages can be delivered to Instant Message Service.

<channel id="output"></channel> (1)

<beans:bean id="defaultMessageEndpoint" class="org.springframework.integration.endpoint.DefaultMessageEndpoint">(2)
<beans:property name="handler">
<beans:bean class="test.spring.integration.im.IMTargetAdapter">(3)
<beans:property name="xmppSender" ref="xmppIM"></beans:property>
<beans:property name="receiver" value="incidence@grumpy"></beans:property>
</beans:bean>
</beans:property>
<beans:property name="subscription">(4)
<beans:bean class="org.springframework.integration.scheduling.Subscription">
<beans:constructor-arg type="java.lang.String" value="output"></beans:constructor-arg>
</beans:bean>
</beans:property>
</beans:bean>


(1) Defines the output channel.
(2) Each component that is connected into MessageChannel must be a MessageEndpoint. DefaultMessageEndpoint is a helper class for this porpoise. Because TargetAdapter is not an Endpoint, it must be wrapped into one.
(3) Handler method is method that will be invoked by MessageEndpoint when a message is received. In this case, a MessageHandler (IMTargetAdapter) is provided, with required information (Facade for communicating with Smack API, and the receiver of messages (incidence@).
(4) MessageEndpoint must be subscribed into a channel.
Using Namehandler is out of scope of this document; of course it would be cleaner using Ext. XML authoring.
And that's all; you can download source code for trying, but remember to change receiver value.
The example has been developed using:
JDK 6.0
Eclipse 3.3

P.S. This entry was written using spring integration 1.0.0 M3