Sunday, July 19, 2009

SOA Platform Gateways and Notifiers

One of the strengths of the SOA Platform is how it enables you to connect legacy applications together. How are these connections made? To answer that question, we have to think about awareness.

In this context, "awareness" refers to whether or not your application clients and services are "ESB-aware" or, in other words, whether they can understand the message formats and transport protocols used by the JBossESB in the SOA Platform. If you (as a programmer) are ESB-aware, then you can simply code any new clients and services that you write to also be ESB-aware. ESB-aware clients and services communicate with each other using messages. ESB-aware services are identified using Endpoint References (EPRs). These ESB-aware messages take the form defined by the org.jboss.soa.esb.message interface.

An ESB-aware message [1] consists of the following components:

  • Header: the header information contains information such as the destination EPR, the sender EPR, and where the reply goes--general message-level functional information.
  • Context: additional information that further explains the message; for example, transaction or security data, the identity of the ultimate receiver, or HTTP-cookie-like information.
  • Body: the actual payload of the message.
  • Fault: any error information associated with the message.
  • Attachment: any attachments (additional files) associated with the message.
  • Properties: any message specific properties (for example the jbossesb.message.id property is a unique value for each message.

But...

What about cases where legacy clients and services that are not ESB-aware? This is suddenly getting complicated. It sounds like you will have to build lots of custom plumbing adapters to get all these combinations of clients and services to talk to each other. But don't panic and don't start building home-grown infrastructure. Supporting interoperability between different clients and services is one of the goals of the SOA Platform.

Interoperability through ESB adapters

One of the ways that the SOA Platform makes this interoperability possible is though gateway adapters. A gateway (org.jboss.soa.esb.listeners.gateway) is a service that acts as a bridge between an ESB-aware and an ESB-unaware client and service. Gateways translate information between ESB and non-ESB message formats and EPRs. (EPR stands for endpoint reference.) Gateways are listener processes in that they "listen" for incoming communications. They are different from ESB-aware listeners in that they accept data in different formats such as objects in files or SQL tables. ESB-aware listeners can only accept messages in the org.jboss.soa.esb.message format.

The SOA Platform supports these gateways:

  • file gateways: local filesystem, ftp, sftp and ftps
  • JMS
  • HTTP/HTTPS
  • email (POP3)
  • SQL table
  • Hibernate

When a gateway listener hears something (incoming data), it converts the non-ESB messages that it receives into the org.jboss.soa.esb.message format. How this conversion happens depends on the gateway type. For example, the file gateway takes the contents of the file and puts it into the location named "BytesBody.BYTES_LOCATION" within the message body.

Let's look at an example.

A quick start (with a quickstart)

One of the helpful features included in the SOA Platform is the set of "quickstart" sample applications. The quickstarts demonstrate SOA Platform functions and integrations. The goals of the quickstarts are to serve as learning tools and as a starting point for your own application coding. We'll examine one of these quickstarts to view the operation of a gateway.

Let's assume that you have a legacy application that stores data in text files. What you want to do is to create an ESB-aware service to read these files and process them as they are created. You could write infrastructure code that periodically polls a target directory to determine when a new file is deposited there. With the SOA Platform, however, you can simply use a file gateway.

The quickstarts are distributed under the samples/quickstarts directory. Let's take a look at helloworld_file_action.
cd samples/quickstarts/helloworld_file_action

Deploy the test to the server and run it with these simple commands:
ant deploy
ant runtest

The server log should look something like this:
16:42:22,744 INFO [STDOUT] Message structure:
16:42:22,744 INFO [STDOUT] [Hello World In A File].
16:42:22,746 INFO [STDOUT]
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
16:42:22,746 INFO [STDOUT] Body: Hello World In A File
16:42:22,746 INFO [STDOUT] &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
16:42:22,747 INFO [STDOUT] Message structure:
16:42:22,747 INFO [STDOUT] [ message: [ JBOSS_XML ]

What just happened here? Let's take a look. The "runtest" ant target invokes CreateTestFile.java program. As its name implies, this program creates a test file. The file contains this text: "Hello World In A File"

For what happens next, we need to look at how the test application is configured. A SOA Platform application's primary configuration file is named jboss-esb.xml. In the case of this quickstart, that file is generated at run time so that the SOA Platform install directory can be determined. So, we need to look at the source of the generated file, which is this file: jboss-esb-unfiltered.xml

The first part of the file that we're interested in looks like this:
1  <fs-provider name="FSprovider1">
2 <fs-bus busid="helloFileChannel" >
3 <fs-message-filter
4 directory="@INPUTDIR@"
5 input-suffix=".dat"
6 work-suffix=".esbWorking"
7 post-delete="false"
8 post-directory="@OUTPUTDIR@"
9 post-suffix=".sentToEsb"
10 error-delete="false"
11 error-directory="@ERRORDIR@"
12 error-suffix=".IN_ERROR"
13 />
14 </fs-bus>
15 </fs-provider>

The lines that we're most interested in are:

  • Line 4,5: This is the definition for the input file directory and extension that the listener "listens for."
  • Line 6: When the message in the file is being processed, a work file with this extension is created by the listener.
  • Lines 7-9: And, when that processing is complete, the message is written to a file in the output directory. The file is retained after the listener is complete.
  • Lines 10-12: Unless something goes wrong, then the message is written to an error file.

This is the definition of the file gateway that we'll use. Note the directory names. In this example, they are filled in at run time from values determined by the directory in which you installed the SOA Platform.

Now, remember how we said that everything is either a message or a service? Scroll down in the file and we'll see our service.
1  <service
2 category="myCategory"
3 name="myFileListener"
4 description="Hello World File Action (esb listener)" >
5
6 <listeners>
7 <fs-listener name="FileGateway"
8 busidref="helloFileChannel"
9 maxThreads="1"
10 is-gateway="true"
11 poll-frequency-seconds="10"
12 />
13 <jms-listener name="helloWorldFileAction"
14 busidref="quickstartEsbChannel"
15 maxThreads="1"
16 />
17 </listeners>
18
19 <actions mep="OneWay">
20 <action name="testAction" class="org.jboss.soa.esb.actions.SystemPrintln" />
21 <action name="action1"
22 class="org.jboss.soa.esb.samples.quickstart.helloworldfileaction.MyAction"
23 process="displayMessage,playWithMessage"
24 />
25 <action name="dump" class="org.jboss.soa.esb.actions.SystemPrintln">
26 <property name="printfull" value="true"/>
27 </actions>
28 </service>

Let's look at this service description.

  • Lines 7-12: First, the listeners. Note the definition of the file system (fs) listener. This is our gateway. The "busidref" field references the "fs-provider" that we discussed a minute ago. Did you notice a second listener? This is an ESB-aware JMS listener. It is there because the SOA Platform requires that each gateway have a corresponding ESB listener defined.
  • Lines 19: Let's look at the actions. The "mep" definition of "OneWay" refers to the action pipeline (the sequence of actions) explicitly not sending a response. (You can find more detail about these definitions in the SOA Platform Programmers' Guide [2].)
  • Lines 20, 25-26: The actions named "testAction" and "dump" write the message to the server log.
  • Lines 21-23: The action that we're most interested in is "action1." The methods (displayMessage, playWithMessage) in the class referred to by this action (org.jboss.soa.esb.samples.quickstart.helloworldfileaction.MyAction) receive the data in the input file as an ESB-Aware message, extract that information, and process it. In the case of your application, you would replace “MyAction” with your own business logic code.

So, in summary, what happens when this quickstart is run?

  1. The file system gateway is initialized.
  2. A file is created in the input directory defined in its fs-provider definition.
  3. The gateway reads the file, transforms it into an ESB-aware message, and passes the message to the methods in the action class defined in the action definition.
  4. These methods process the message (in the case of the quickstart, they write the message contents to the server log).5. The gateway moves the file to the output directory defined in its fs-provider definition.


It's important to note that the only new code that had to be written is the action class. The infrastructure code to listen for and transform the file into a message in the format that the SOA Platform can process is all part of the gateway.

From the perspective of an application developer, this is a plus as you can concentrate on writing code to fulfill the business process requirements of your application.

From the perspective of a QE engineer, this is a real plus as it means that less code has to be written. And, the less code, the fewer opportunities for bugs!

The Data is in the SOA Platform, Now What?

The gateway listeners make it possible for your legacy applications to get data into the SOA Platform's ESB and have that data transformed into ESB-aware messages, so that the ESB can route the data to the desired destination service. However, that's only have the battle if the destination service is another legacy application that is also not ESB-aware. How can you easily transform the messages into a form that a legacy application can process?

The answer is to use one of the SOA Platform's notifiers. Just as a gateway listener enables you to move ESB-unaware messages onto the SOA Platform's ESB, notifiers enable you to move ESB-aware messages from the ESB to ESB-unaware services. The notifiers convert the ESB-aware messages into data into various forms that your services can understand. The SOA Platform supports these notifiers:

  • NotifyConsole
  • NotifyFiles
  • NotifySQLTable
  • NotifyFTP
  • NotifyQueues
  • NotifyTopics
  • NotifyEmail

Let's add some of these notifiers to the helloworld_file_action quickstart. Note that while there are other quickstarts dedicated to illustrating notifiers, our expanding on the helloworld_file_action quickstart is in keeping with one of the goals of the quickstarts; to give you a starting point for developing your own customer applications.

One thing to keep in mind is that the action pipeline has two stages, first normal processing and then outcome processing. Notifiers do not perform any processing of messages during that first stage. They send notifications during the second stage. The notification occurs after the processing of the action pipeline. This means that you cannot use notifiers to alter the results of any action processing. The data sent by the notifier is the ESB message that is processed by the action pipeline. [3]

Let's insert the following statements (without the line numbers) into the service defined in the quickstart's jboss-esb-unfiltered.xml file:
1  <action name="notificationAction" class="org.jboss.soa.esb.actions.Notifier">
2 <property name="okMethod" value="notifyOK" />
3 <property name="notification-details">
4 <NotificationList type="ok">
5 <target class="NotifyConsole"/>
6 <target class="NotifyFiles">
7 <file append="false" URI="@OUTPUTDIR@/notify.txt"/>
8 <file append="true" URI="@OUTPUTDIR@/results.log"/>
9 </target>
10 <target class="NotifyEmail"
11 from="soauser@server.com"
12 sendTo="user@server.com.com"
13 ccTo="admin@server.com"
14 subject="Test was successful"
15 message="See attached file">
16 <attachment>@OUTPUTDIR@/notify.txt</attachment>
17 </target>
18 </NotificationList>
19 <NotificationList type="err">
20 <target class="NotifyConsole"/>
21 <target class="NotifyFiles">
22 <file append="true" URI="@ERRORDIR@/error.log"/>
23 </target>
24 </NotificationList>
25 </property>
26 </action>

Before we re-deploy and rerun the quickstart, we'll walk through these new statements:

  • Line 1: Note the reference to the Notifier class
  • Line 2: The okMethod enables the server to notify about success or failure at each action in the action pipeline
  • Line 4: These notifications will happen if the processing of the action pipeline is successful
  • Line 5: The simplest notifier - the ESB message is sent to the server console log
  • Line 6: Since we started the quickstart by creating a file, we'll generate another file with the NotifyFiles notifier.
  • Line 7: The ESB message is written to a file named "notify.txt" in the output dir. Note that if a file with this name already exists, it is overwritten.
  • Line 8: We'll also append the ESB message to a log file.
  • Line 10: And, we'll send that same ESB message in an email - the ESB message will be in the email
  • Line 11: But, just to be sure, we'll also send the notify.txt file as an attachment to the email
  • Line 19: Finally, if the action pipeline processing encounters an error, these notifiers will execute

At this point, redeploy the quickstart and execute "ant runtest" again.

The server log should look something like this:
 2009-07-18 22:45:33,291 INFO  [STDOUT] ConsoleNotifier 2009/07/18 10:45:33.291<
BEFORE
Hello World In A File
AFTER
>
2009-07-18 22:45:33,292 INFO [org.jboss.soa.esb.helpers.Email] Initialising mail server session. Properties: {mail.smtp.port=25, mail.smtp.auth=true, mail.smtp.host=localhost}

For the console and email notifier. An email will be sent to the addresses you specified and you should also see a file named notify.txt and results.log in the output directory. If you run the test again, you'll see the results.log file include the ESB message twice.

Closing Thoughts

With the SOA Platform's gateways and notifiers, it's possible to reuse existing applications as services that are ESB-unaware, without having to rewrite the applications' code. The applications can continue to communicate via files or FTP or database records or other data formats. The gateways and notifiers take care of getting data into the SOA Platform (where you can route it to the intended service - see the previous post on content based routing) and then back out. And, that is a lot easier than having to rewrite legacy applications. COBOL anybody? ;-)

Resources

[1] Look in this file in the javadocs installed with your SOA Platform server: jboss/soa/esb/message/Message.html
[2] http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/4.3.GA/html/Programmers_Guide
[3] http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/4.3.GA/html/Programmers_Guide/ch11s06.html

Acknowledgments

As always, I'd like to thank the members of the SOA Platform project for their help and timely review comments! Also, this post relies heavily on the extensive SOA Platform documents and the quickstarts. The information on listeners in this post was originally published in Red Hat Magazine (http://magazine.redhat.com/2008/05/22/adapters-for-an-esb). I'd like to thank everyone at the magazine for their help over the past few years, especially the magazine editor, Bascha Harris.

2 comments:

Jeff Yu (余昌) said...

Very well written. ;-)

I wrote a blog entry to introduce your soa-p three blog entries.
http://jeff.familyyu.net/2009/07/updates-on-jboss-esb.html

IT Cogito said...

Great post! Thanks!
How to extend JBossESB in order to provide more gw/notifiers?
I need to use SFTP notifier, which is not provided included within JBossESB.