Thursday, April 29, 2010

SOA Governance

I wrote an article on SOA Governance last year but they forgot to let me know it had been published. Better late than never.

Tuesday, April 20, 2010

Wednesday, April 14, 2010

JMS Transactions and the SOA Platform

The Server's Down - And What's Worse, I Lost my Money!

Regardless of how carefully you plan, it's inevitable that the software you build will encounter a situation or chain of events that you did not anticipate, and experience some type of failure. (Trust me on this. I work in software testing and spend most of my waking hours either causing software failures, or debugging them.) Since it is impossible to avoid every failure, the ability to recover from failures is a crucial part of every software system design.

A classic type of failure involves moving money between bank accounts. It's a 2-phase set of actions. First, money is withdrawn from one account and, second, the money is deposited into a second account. This sounds simple, right? Well, suppose that the the system encounters a failure after it has withdrawn the money from the first account, but before it has had a chance to deposit into the second account? What happens to the money? How can you avoid losing this money? The answer is that you enclose these two actions into a transaction.

Transactions and ACID Properties

What exactly is a "transaction?" Like a lot of things in software, it's a buzzword that is overused. But beyond that, a transaction is a logical grouping of actions into a single larger action, where this larger action is performed, or not performed, based on an all-or-nothing evaluation of the results of the individual actions[1]. In the case of the transfer of money example we just mentioned, wrapping the two transfers into a transaction would ensure that if either transfer failed, your money would not be lost, and both accounts would be restored to their original condition.

Transactions are frequently described in terms of having an "ACID"[2] set of properties:
  • Atomicity - When we talk about wanting a transaction to be "atomic" what we mean is that we want the transaction, which will be made up of a set of individual actions, to be treated as a single atomic unit. If any of these actions fails, such as one of the bank account transfers in our example, then the entire transaction is canceled. (The term used for this is "rolled back.") A transaction is an all-or-nothing construct. All the actions enclosed by the transaction must be completed successfully before the transaction itself is completed (or "committed.")
  • Consistency - Transactions don't leave the data that it works on in a partially finished state. If a transaction rolls back, the data or database in question is returned to the (consistent) state that it was in before the transaction started.
  • Isolation - In the context of a transaction, "isolation" refers to how any conditions or state created by a transaction are not visible to any other transaction. For example, if our bank account transfer was executed in a transaction, then while it was executing, any other transactions or operations would not be able to see any of results of the transaction until the entire transaction had completed.
  • Durability - One of the dangers of the bank account transfer example, if it were to be attempted outside of a transaction, is that if something went wrong, data (or even worse, money!) would be lost. Because a transaction guarantees the consistency of the data, that data is durable. If the transaction rolls back, the original data is restored.
Transactions and the SOA Platform's JBossESB

OK, this is all interesting, but it sounds like something better suited to databases than Service Oriented Architecture. How does a transactional model apply to the JBossESB in the SOA Platform where everything is either a message or a service?

Here's how: some transports supported by the Platform (InVM[3] and JMS) support a transactional delivery of messages. The actual delivery of that message will not happen until the transaction is committed. How can you cause a rollback of a transaction in an action pipeline to occur? By configuring the application and its services to include the transaction and then by raising a RuntimeException in the action pipeline. The best way to explain and illustrate how this is with one of the SOA Platform's "quickstart" example programs. Let's take a look.

The Quickstart

One of the great features of the SOA Platform is its extensive, and always growing, set of "quickstart" programs. These programs illustrate various features supported by the Platform and serve as a great resource for writing your own applications. For our example, we'll look at the aptly named "jms_transacted" quickstart.

This quickstart illustrates using the JMS transport with the JBossESB in the SOA Platform to handle transactions. The quickstart also shows how message redelivery can be performed with the JMS transport. Before we examine the quickstart's code, configuration files, and actual output, it's important that we make note of the quickstart's use of JCA (Java Connector Architecture) [4]. JCA provides a standardized way to connect to JMS providers. For the JBossESB in the SOA Platform, jms-jca-providers provide transaction support for the action pipeline by enclosing actions in a JTA [5] transaction. These transactions ensure that messages are handled within an enclosing transaction. If something goes wrong in the transaction, the messages are put back into the JMS queues to be reprocessed. We'll see this in action when we run the quickstart.

The sequence of actions that the quickstart performs is:
  • Like many of the SOA Platform's quickstarts, things begin by having a JMS message routed to a service to start the action pipeline.
  • The first time that the pipeline is entered, a row is inserted into the quickstart's HSQLDB database. Note that while the SOA Platform supports multiple databases such as MySQL, Oracle, PostgreSQL and others, in production environments, it includes a fully functional HSQLDB database for demonstration purposes.
  • Then, the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction class is called to raise an exception. This action class is configured to raise the exception (5) times, so as to force a rolback of the transaction that encloses the writes to the database. Each time that the exception is raised, the JCA adapter rolls back the transaction. The exception is also propagated to the JCA adapter and written error messages to the log.
  • After the configured limit of (5) rollbacks is reached, the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction class completes and the transaction is committed by the SOA Platform's JBossESB's org/jboss/soa/esb/common/JBossESBTransactionService class so that the net result is only one row written to the database.
Now, we'll take a detailed look at how this transaction is configured, and just what happens as the quickstart runs.

The Quickstart - In Detail

The transaction in quickstart includes both JMS messaging and writing to a database. Let's start by examining the connection to the HSQLDB database that the quickstart uses. First, we need to create a datasource definition to connect to the database.

In file: quickstart-ds.xml

1   <datasources>
2 <local-tx-datasource>
3 <jndi-name>JmsTransactedDB</jndi-name>
4 <connection-url>jdbc:hsqldb:hsql://localhost:1706</connection-url>
5 <driver-class>org.hsqldb.jdbcDriver</driver-class>
6 <user-name>sa</user-name>
7 <password></password>
8 <min-pool-size>5</min-pool-size>
9 <max-pool-size>20</max-pool-size>
10 <idle-timeout-minutes>0</idle-timeout-minutes>
11 <depends>jboss:service=JmsTransactedDB</depends>
12 <prepared-statement-cache-size>32</prepared-statement-cache-size>
13 </local-tx-datasource>
14
15 <mbean code="org.jboss.internal.soa.esb.dependencies.HypersonicDatabase"
16 name="jboss:service=JmsTransactedDB">
17 <attribute name="Port">1706</attribute>
18 <attribute name="BindAddress">localhost</attribute>
19 <attribute name="Database">JmsTransactedDB</attribute>
20 <attribute name="Silent">true</attribute>
21 <attribute name="Trace">false</attribute>
22 <attribute name="No_system_exit">true</attribute>
23 <attribute name="DataDir">${jboss.server.data.dir}</attribute>
24 </mbean>
25 </datasources>
Some items to make note of here are:
  • Line 3 - We're defining a local-tx-datasource as we will want a JCA connection with transaction support[6].
  • Line 4 - We're use this JNDI name to reference the datasource.
  • Lines 5-13 - DB driver class, connection, URL, username/password, etc. for the database connection
  • Line 12 - MBean interface for running Hypersonic in the same VM with JBoss - note that the service name and the JNDI name as we'll refer to them later.
OK, we have a datasource that we can use to get to the HSQLDB database, and get us there in a mode that supports transactions. What's next? We have to ensure that our database actually exists before we try to use it! Luckily, the SOA Platform has a class that can initialize our database when the .esb application is deployed.

In file: jbossesb-service.xml
1  <?xml version="1.0" encoding="UTF-8"?>
2
3 <server>
4 <mbean code="org.jboss.internal.soa.esb.dependencies.DatabaseInitializer"
5 name="jboss.esb:service=JmsTransactedDatabaseInitializer">
6 <attribute name="Datasource">java:/JmsTransactedDB</attribute>
7 <attribute name="ExistsSql">select * from jms_transacted_table</attribute>
8 <attribute name="SqlFiles">
9 hsqldb/create.sql
10 </attribute>
11 <depends>jboss.jca:name=JmsTransactedDB,service=DataSourceBinding</depends>
12 </mbean>
13 </server>

There are (3) interesting things in this file:
  • Line 4 - This MBean creates the database on startup. How does it connect to the database? See line 6.
  • Line 6 - Here's a reference to our datasource. Note the JNDI name that we observed in the datasource definition file. How does the DatabaseInitializer know what the database schema look like? See line 9.
  • Line 9 - Here's a reference to the quickstart's src/hsqldb/create.sql file. This file create a very simple table in our database for the quickstart to use:
In file: src/hsqldb/create.sql:
create table jms_transacted_table
(
unique_id INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY,
data_column VARCHAR(255) NOT NULL
);
What else does the quickstart's database need? The service that creates the database (JmsTransactedDatabaseInitializer) has to be deployed. This is handled in the deployment.xml file (as are the JBoss Messaging queues that the quickstart will use):
<jbossesb-deployment>
<depends>jboss.esb.quickstart.destination:service=Queue,name=quickstart_jms_transacted_Request_esb</depends>
<depends>jboss.esb.quickstart.destination:service=Queue,name=quickstart_jms_transacted_Request_gw</depends>
<depends>jboss.esb:service=JmsTransactedDatabaseInitializer</depends>
</jbossesb-deployment>
And finally, the jbossesb-service.xml quickstart-ds.xml files have to be included in the quickstart's .esb application archive file. This is handled by the "additional.deploys" property in the quickstart's build.xml file and the ../conf/base-build.xml file that is used by all the quickstarts.

cat -n build.xml | grep quick
<property name="additional.deploys" value="jbossesb-service.xml quickstart-ds.xml" />

OK, that takes care of the database. Now, let's look at how we configure the quickstart to take advantage of the transaction support on the JBossESB in the SOA Platform. As always, the place to begin is the jboss-esb.xml file.

In file: jboss-esb.xml
1  <?xml version = "1.0" encoding = "UTF-8"?>
2 <jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.0.1.xsd" parameterReloadSecs="5">
3
4 <providers>
5 <jms-jca-provider name="JBossMessaging" connection-factory="XAConnectionFactory">
6
7 <jms-bus busid="quickstartGwChannel">
8 <jms-message-filter
9 dest-type="QUEUE"
10 dest-name="queue/quickstart_jms_transacted_Request_gw"
11 transacted="true"
12 />
13 </jms-bus>
14 <jms-bus busid="quickstartEsbChannel">
15 <jms-message-filter
16 dest-type="QUEUE"
17 dest-name="queue/quickstart_jms_transacted_Request_esb"
18 transacted="true"
19 />
20 </jms-bus>
21 <activation-config>
22 <!-- The maximum number of times a message is redelivered before it is sent to the DLQ -->
23 <property name="dLQMaxResent" value="5"/>
24 </activation-config>
25
26 </jms-jca-provider>
27 </providers>
28
29 <services>
30 <service
31 category="JMSSecuredESB"
32 name="SimpleListener"
33 description="JMS Secured quickstart sample">
34 <listeners>
35 <jms-listener name="JMS-Gateway"
36 busidref="quickstartGwChannel"
37 is-gateway="true"/>
38 <jms-listener name="jmssecured"
39 busidref="quickstartEsbChannel"/>
40 </listeners>
41 <actions mep="OneWay">
42
43 <action name="printMessage" class="org.jboss.soa.esb.actions.SystemPrintln">
44 <property name="message" value="JMS Transacted Quickstart entered. Message body"/>
45 <property name="printfull" value="false"/>
46 </action>
47
48 <action name="insertDBAction" class="org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction">
49 <property name="datasource-name" value="java:JmsTransactedDB"/>
50 <property name="db-insert-sql" value="insert into jms_transacted_table(data_column) values(?)"/>
51 </action>
52
53 <!--
54 Will throw an Exception for the configured number of rollbacks. This should trigger the transaction to be
55 rolledback and the message placed back onto the JMS queue.
56 -->
57 <action name="throwExceptionAction" class="org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction">
58 <property name="rollbacks" value="5"/> <!-- if greater than dLQMaxResent then message goes to the JMS DLQ -->
59 </action>
60
61 <action name="printMessageDone" class="org.jboss.soa.esb.actions.SystemPrintln">
62 <property name="message" value="JMS Transacted Quickstart processed successfully. Message body"/>
63 <property name="printfull" value="false"/>
64 </action>
65
66 <!-- The next action is for Continuous Integration testing -->
67 <action name="testStore" class="org.jboss.soa.esb.actions.StoreMessageToFile"/>
68 </actions>
69 </service>
70 </services>
71
72 </jbossesb>
  • Line 2 - Before we go any further, we should look at the XSD (XML schema definition) for jboss-esb.xml files. The schema definition is available here: http://anonsvn.jboss.org/repos/labs/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.0.1.xsd. If you're going to be working with the SOA Platform, it's a good idea to become familiar with this XSD as all the elements that you work with in jboss-esb.xml are defined there. The two main elements are of course, providers and services. Let's look at the providers first. Two types of providers are supported. Schedule providers define schedule driven listeners that pull messages from queues at (you guessed it) scheduled time periods. In contrast, bus providers such as the jms-jca-providers that we're using in this quickstart, are associated with listeners that have messages pushed to them. [7]
  • Line 5 - Here's the start of our jms-jca-provider. Note that the XAConnectionFactory provides support for distributed transactions
  • Lines 7-13 - This is a reference to the channel and JMS queue that will be used when a JMS message that is sent to a JMS gateway to initiate the quickstart. Let's stop for a second and talk about the forms in which data is moved onto and across the JBossESB in the SOA Platform. One of the primary functions performed by JBossESB is the routing of messages between services. (I said it before, and I'll say it again; remember that, on an ESB, everything is either a message or a service.) The messages that JBoss ESB handles conform to org.jboss.soa.esb.message.Message. Service endpoints that can process messages in this format are described as being "ESB-aware." This is all well and good for services and applications that were designed and written with this message format in mind, but what about other services, including legacy applications, that communicate through other non-ESB aware data formats? JBossESB handles connecting services that communicate in non-ESB aware message formats through its gateway listeners. These listeners route incoming messages from outside the ESB to ESB services. The ESB's set of gateway listeners (generally referred to as "gateways") support a variety of message formats such as files, FTP, and JMS. These gateways accept messages in a non-ESB aware format onto the ESB and then convert them (actually, they wrap the message payload) into ESB-aware messages before sending them to their service's action pipeline.
  • Line 11 - Remember, we want the service to process messages within a transaction.
  • Lines 14-20 - Gateways are intended to enable the JBossESB to communicate to external data sources. The gateways themselves don't move data across the ESB. Accordingly, for each Gateway channel that we define, we have to define a corresponding ESB-aware channel to be used to route messages within the ESB.
  • Line 21 - The activation-config defines how we link to the JCA. In the case of this quickstart, the interesting property is: dLQMaxResent. As the comment indicates, the “DLQMaxResent” controls how many times the JCA adapter resends the message before it is sent to the dead letter queue. This is a JBossESB service that handles messages for transports when the messages cannot be delivered. (The JMS transport actually has its own dead letter queue as JBoss Messaging is shipped with a set of pre-configured destinations defined in destinations-service.xml including: <mbean code="org.jboss.jms.server.destination.Queue" name="jboss.messaging.destination:service=Queue,name=DLQ") In this quickstart, the JCA adapter will try to send the message 5 times.
After our providers are defined, we can define our services.

A service is where thing happen. Each service definition includes a set of listeners and a sequential set of actions referred to as an "action pipeline." The listeners "listen" for incoming messages for the service and route those messages to the action pipeline.

After the service name and category are defined on lines 31 and 32, we define the service's listeners:
  • Lines 35-37 - Here's the JMS gateway listener definition. Notice how it references the bus ID of the channel that we defined in earlier in the provider definition. Also, notice how it's "is-gateway" property is set to true to indicate that this listener is a gateway.
  • Lines 38-39 - And here is the ESB-aware listener that corresponds to the gateway listener.
So much for the listeners, now let's look at the actions.
  • Line 41 - This is the start of the definitions of the sequence of actions that we referred to as the action pipeline. The message exchange pattern (or "mep") indicates that the messages are not being exchanged in a request/response pattern, but rather are being sent in only one direction. (In contrast, web services exposed on the ESB would follow a synchronous request/response pattern.)
  • Lines 43-46 - This action invokes the simplest of the JBossESB's many useful out-of-the-box actions (http://soa.dzone.com/articles/works-great-right-out-box) to write some messages to the server log.
  • Lines 48-51 - Here's where things start to get interesting. This custom action writes a row to our database table. Remember the "java:JmsTransactedDBdatasource" that we defined? Here's where gets used.
  • Line 57 - Remember how we talked about how to cause a rollback of a transaction in the action pipeline to occur? We configured the .esb to include a transaction, and now we raise a RuntimeException in the action pipeline. The action defined here raises an exception of a type that we define in the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction class. The ActionProcessingPipeline will catch the exception and pass it up the stack to its caller, the JMS/JCA adapter. The Adapter then causes the transaction to rollback and the message is redelivered.
  • Line 58 - When we defined the jms-jca-provider's activation-config, we specified a dLQMaxResent value of 5 to indicate that if the transaction was rolled back more than 5 times, the message processed in the transaction should be sent to the JMS dead letter queue. In line 58, we also set the value of the rollbacks property equal to 5, to ensure that the transaction can complete after 5 rollbacks, and the message is not sent to the dead letter queue service. As the comment helpfully explains, setting the rollbacks property to be greater than dLQMaxResent will send the message to the dead letter queue service.
  • Lines 61-64 This action is only reached after the rollbacks have been performed and the transaction is able to complete. The action writes the message body to the server log.
Yogi Berra once said that "In theory there is no difference between theory and practice. In practice there is."[8] OK. Enough theory, let's run the quickstart and watch what happens.

After the SOA-P server is started, the quickstart, is deployed with this command: ant deploy

When the quickstart is deployed, the following is to the server.log:
2010-04-07 09:26:22,117 INFO  [org.jboss.resource.connectionmanager.ConnectionFactoryBindingService] (HDScanner) Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=JmsTransactedDB' to JNDI name 'java:JmsTransactedDB'
2010-04-07 09:26:22,157 INFO [org.jboss.internal.soa.esb.dependencies.DatabaseInitializer] (HDScanner) Initializing java:/JmsTransactedDB from listed sql files
2010-04-07 09:26:22,326 INFO [org.jboss.soa.esb.listeners.deployers.mc.EsbDeployment] (HDScanner) Starting ESB Deployment 'Quickstart_JMS_Transacted.esb'

Note how the JmsTransactedDB database is initialized by org.jboss.internal.soa.esb.dependencies.DatabaseInitializer class the when the quickstart is deployed.

Next, we run the quickstart with this command: ant runtest

When the ant runtest target is executed, the following chain reaction is started:

The org.jboss.soa.esb.samples.quickstart.jmstransacted.test.SendJMSMessage class sends a (non-ESB aware) JMS message to the quickstart_jms_transacted_Request_gw queue. The message is routed through that queue's corresponding ESB-aware queue (quickstart_jms_transacted_Request_esb) to the SimpleListener service and the action pipeline is initiated.

The first time that the action pipeline is started, the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction class inserts a row into the database through the HSQLDB datasource. This results in the following messages being written to the server.log:

Then, the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction runtime exception is thrown.

Since this exception is thrown from inside the action pipeline, the JMS/JCA adapter rolls back the enclosing transaction. The exception is propagated to the JMS/JCA adapter as the exception is passed up the stack from the action pipeline to its the caller (which is the adapter). The exception is caught by the JBossESB's org.jboss.soa.esb.listeners.message.ActionProcessingPipeline, and is propagated up the stack to the JMS/JCA adapter (which is the action pipeline's caller). The adapter causes the transaction to rollback. This results in the row being removed from the database and in the original message being redelivered. Remember what we said about a transaction being an "all or nothing" event?

The transaction/rollback sequence then repeats until the configured number of rollbacks are performed. You can see a counter (maintained by the org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction class) of the number of times the action is attempted.

The printMessage action prints the message to the log:
2010-04-07 09:26:35,340 INFO  [STDOUT] (WorkManager(2)-9) JMS Transacted Quickstart entered. Message body:
2010-04-07 09:26:35,340 INFO [STDOUT] (WorkManager(2)-9) [Hello Transacted JMS World]].

And the insertDBAction action writes the row to the database - note the counter value of [1]:

2010-04-07 09:26:35,415 INFO  [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction] (WorkManager(2)-9) Successfully inserted [Hello Transacted JMS World] counter[1]] into jms_transacted_table

But wait! Then our exception is raised:
2010-04-07 09:26:35,488 ERROR [org.jboss.resource.adapter.jms.inflow.JmsServerSession] (WorkManager(2)-9) Unexpected error delivering message delegator->JBossMessage[5204569273565185]:PERSISTENT, deliveryId=0
java.lang.IllegalStateException: [Throwing Exception to trigger a transaction rollback]
at org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction.process(ThrowExceptionAction.java:75)

The JMS/JCA adapter rolls back the transaction, and the message is delivered again, and another row is written to the database. The counter is incremented to [2] this time and the exception is raised again.
2010-04-07 09:26:36,435 INFO  [STDOUT] (WorkManager(2)-10) JMS Transacted Quickstart entered. Message body:
2010-04-07 09:26:36,435 INFO [STDOUT] (WorkManager(2)-10) [Hello Transacted JMS World]].
2010-04-07 09:26:36,438 INFO [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction] (WorkManager(2)-10) Successfully inserted [Hello Transacted JMS World] counter[2]] into jms_transacted_table
2010-04-07 09:26:36,442 ERROR [org.jboss.resource.adapter.jms.inflow.JmsServerSession] (WorkManager(2)-10) Unexpected error delivering message delegator->JBossMessage[5204569273565185]:PERSISTENT, deliveryId=1
java.lang.IllegalStateException: [Throwing Exception to trigger a transaction rollback]
at org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction.process(ThrowExceptionAction.java:75)

When the rollback counter number is reached, the action pipeline completes and the transaction commits so that the row is finally written to the database. The following messages are written to the servler.log - This sequence repeats (5) times until the counter is passed and the row is actually written to the database:
2010-04-07 09:26:40,489 INFO  [STDOUT] (WorkManager(2)-14) JMS Transacted Quickstart entered. Message body:
2010-04-07 09:26:40,489 INFO [STDOUT] (WorkManager(2)-14) [Hello Transacted JMS World]].
2010-04-07 09:26:40,490 INFO [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.DBInsertAction] (WorkManager(2)-14) Successfully inserted [Hello Transacted JMS World] counter[6]] into jms_transacted_table
2010-04-07 09:26:40,490 INFO [STDOUT] (WorkManager(2)-14) JMS Transacted Quickstart processed successfully. Message body:
2010-04-07 09:26:40,490 INFO [STDOUT] (WorkManager(2)-14) [Hello Transacted JMS World]].

Now, as I mentioned in the beginning of this post, I work as a software QE engineer. One of the basic tenents of software QE is to never trust anything. Let's take a closer look at what happens when the quickstart runs.

Server logs are goldmines for gathering debugging information. Let's restart the server, but this time set the server logging level to DEBUG:
./run.sh -Djboss.server.log.threshold=DEBUG

And then rerun the quickstart. The server.log will be much more verbose this time, but if you look closely, you can see the rollback counter in action:
grep  nr-of-rollbacks server.log
2010-04-07 23:14:19,776 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-11) rollbackCounter [0], nr-of-rollbacks [5]
2010-04-07 23:14:20,804 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-12) rollbackCounter [1], nr-of-rollbacks [5]
2010-04-07 23:14:21,819 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-13) rollbackCounter [2], nr-of-rollbacks [5]
2010-04-07 23:14:22,833 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-14) rollbackCounter [3], nr-of-rollbacks [5]
2010-04-07 23:14:23,848 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-15) rollbackCounter [4], nr-of-rollbacks [5]
2010-04-07 23:14:24,858 DEBUG [org.jboss.soa.esb.samples.quickstart.jmstransacted.test.ThrowExceptionAction] (WorkManager(2)-16) rollbackCounter [5], nr-of-rollbacks [5]

Before we move on, let's look at one more thing and verify that only one row is actually written to the database. Now, the quickstart is very tidy and always cleans up after itself by removing rows from the database. Let's disable this cleanup by removing this target from the quickstart's build.xml file:
cat build.xml | grep truncate

And, let's start the server again, but with one additional command line option: sh ./run.sh -Djava.awt.headless=false

Why "headless=false?" After we run the quickstart again, the row that was written to the database should still be there. We'll look at it with the HSQL DB Manager service. This service requires the server to not be started in its default "headless" mode, as the service opens up a Java UI application. We invoke the DB Manager from the JMX console - see screenshot - the service tag is: database=jbossesb,service=Hypersonic.


Once the DM Manager application UI launches, we just connect to the "JmsTransactedDB" database - see screenshot - and execute a query to return all rows in the "jms_transacted_table" - see screenshot.

And, there is our one record!


Before we move on, it's worthwhile to note what we did NOT have to do to execute a transaction with the SOA Platform.

We did not have to write transaction specific code. The JBossESB in the Platform did the dirty work for us. All we had to do is to define the .esb application's configuration in its jboss-esb.xml file to include the transaction-relevant properties and reference a JMS/JCA adapter and the Platform did the rest. This is what makes middleware so useful. You could write the code to handle transactions all on your own, but wouldn't you rather concentrate on solving your own business problems instead of build infrastucture plumbing? [9]

Closing Thoughts

The value that using transactions adds to an application is pretty obvious (just keep your bank account in mind). The value added by middleware in implementing transactions should also be obvious. You could write your own code to handle atomicity, consistency, isolation and durability, but, this would not be an easy task, and it would also take away a lot of time from your being able to concentrate on solving your own business logic problems. The SOA Platform's support for transactions over the JBossESB enables you to provide a higher level of reliability and durablity to your service based applications.

References

[1] http://qconlondon.com/london-2010/file?path=/qcon-london-2010/slides/MarkLittle_TransactionsOverUsedOrJustMisunderstood.pdf

[2] Little, Mark, Maron, Jon, Pavlik, Greg. Transaction Processing: Design and Implementation, Upper Saddle RIver, new Jersey: Prentice Hall/Hewlett-Packard Professional Books, 2004.

[3] http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/5.0.0/html-single/Programmers_Guide/index.html#sect-SOA_ESB_Programmers_Guide-What_is_a_Service-InVM_Transport

[4] http://java.sun.com/j2ee/connector/

[5] http://java.sun.com/javaee/technologies/jta/index.jsp

[6] http://www.jboss.org/file-access/default/members/jbossas/freezone/docs/Server_Configuration_Guide/4/html/Connectors_on_JBoss-Configuring_JDBC_DataSources.html

[7] http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/5.0.0/html-single/Programmers_Guide/index.html#sect-SOA_ESB_Programmers_Guide-Configuration-Providers

[8] http://en.wikiquote.org/wiki/Yogi_Berra

[9] http://magazine.redhat.com/2008/03/11/what-is-middleware-in-plain-english-please

Acknowledgments

As always, I want to thank the JBoss SOA Platform team and community (especially Kevin Conner for his timely review input for this blog post).



Monday, March 22, 2010

SOA Platform 5.0 Announced

Big news today!

JBoss Enterprise SOA Platform v5.0 and JBoss Developer Studio V3.0 were formally announced today. This major release of the SOA Platform is built on JBoss Enterprise Application Platform v5 and provides multiple development productivity and management enhancements including new management consoles, updates to the enterprise services bus to support a new XSLT transformation tool, easier content based routing, enhanced protocol listeners, better web services integration, a new rules engine that can be managed by JBoss Enterprise BRMS (business rules management system), enhanced registry (UDDI v3). JBoss Developer Studio V3.0 is built on the latest major release of Eclipse 3.5 and includes enhancements to make it easier to create SOA services and data transformations.

The press release is here - http://www.redhat.com/about/news/prarchive/2010/soa-enhanced.html

And, Pierre Fricke has written a blog post on the SOA Platform release here: http://blogs.jboss.org/blog/pfricke/

Monday, September 14, 2009

Works Great Right Out of the Box

In reviewing my last two posts to this blog, I realized that I've actually been describing several of the SOA Platforms out-of-the-box actions in an ad hoc manner. I thought that this might be a good time to take a step back and review the platform's full set of out of the box actions.

Which got me thinking about how useful it is when a tool works "right of of the box" and how disappointing it can be when it doesn't.

In his book "Diary of a Wimpy Kid, The Last Straw,"[1] author Jeff Kinney has the main character, angst-ridden middle school student Greg Heffley, finds an ad for "personal hovercraft" in a comic book. Greg imagines himself as the coolest kid in school if he could get his own hovercraft, so he sends all his saved-up money away for one. He is, of course, disappointed when he opens the box and finds not a functioning personal hovercraft, but the plan to build one.

Step one in the plan reads: "Acquire an industrial twin-turbine engine."

In contrast, the SOA Platform provides an extensive set of actions[2] that work right out of the box. The SOA Platform also supports the creation of custom actions, but, when you are designing how your application will make use of the Platform, the place to start is the out-of-the-box actions. These have all been tested so you can depend on the results they will achieve, and they've also been documented and illustrated in the quickstart sample programs. (On a personal note, my introduction to the SOA Platform included my spending several hours building an action, when an out of the box action for the same task already existed!)

The out-of-the-box actions implemented in the SOA Platform are divided into the following functional groups:
  • Transformers and Converters - converting message data from one form to another
  • Business Process Management - integrating with JBoss jBPM
  • Scripting - automating tasks in supported scripting languages
  • Services - integration with EJBs
  • Routing - moving message data to the correct services
  • Notifier - sending data to ESB unaware destinations
  • Webservices/SOAP - the name says it all - support for Webservices
(There's also a Miscellaneous group that includes only one action - org.jboss.soa.esb.actions.SystemPrintln. This action prints of a message.)
Let's take a quick look at all of these actions, and then a more in-depth look at a couple of them.

Transformers and Converters

These actions are needed to enable services that expect data in different forms to communicate with each other. What transformers and converters do is to translate message payloads into different forms. Let's stop for a minute and review the contents of messages as they are passed over the JBossESB in the SOA Platform. Messages implement the org.jboss.soa.esb.message.Message interface. From the point of view of a service, the data in a message, in other words, the message "payload" consists of the message body plus attachments and properties. The default way for an action to get or set a message payload is the MessagePayloadProxy utility class. This class is important as it provides a standard way to get the message payload.

The actions in the Transformers and Converters actions group (yes, the names are pretty self-explanatory) are:
  • ByteArrayToString (org.jboss.soa.esb.actions.converters.ByteArrayToString) - This action converts a byte[] message payload into a Java String
  • LongToDateConverter (org.jboss.soa.esb.actions.converters.LongToDateConverter) - And this action converts a Java long message payload into a java.util.Date
  • ObjectInvoke (org.jboss.soa.esb.actions.converters.ObjectInvoke) - Things start to get a little more interesting with the ObjectInvoke converter. This action takes a serialized object, which constitutes the entire Message payload, and passes it to a configured processer class. The processed results become the new message payload. The action lets you use your own POJOs as the message payloads.
  • ObjectToCSVString (org.jboss.soa.esb.actions.converters.ObjectToCSVString) - This action converts a message into a comma separated string based on a list of property names that you supply.
  • ObjectToXStream (org.jboss.soa.esb.actions.converters.ObjectToXStream) - This action is similar to the ObjectInvoke action, except that it converts the object to XML using XStream[3]. You've probably already guessed that the XStreamToObject action converts XML to an object using XStream.
  • SmooksAction (org.jboss.soa.esb.smooks.SmooksAction) - This action enables you to use a powerful set of Smooks[4] operations within the SOA Platform. Transformations are probably the first type of operation that you think of with Smooks, but with the SmooksAction you can also make use of Smooks operations such as splitting and routing message payloads[5]. We'll take a closer look at this action later in the post. (Note that the SmooksTransformer action is being deprecated in favor of the SmooksAction action.)
  • PersistAction (org.jboss.soa.esb.actions.MessagePersister) - This action doesn't really transform or convert a message, it writes it to the persistent message store. The most common use of the action is that it is how the SOA Platform itself writes messages to the dead letter queue (DLQ), but it can be used in any situation when you want to store a message.
Business Process Management

This action group contains only one out of the box action (org.jboss.soa.esb.services.jbpm.actions.BpmProcessor), but that one action supports the JBossESB - jBPM integration in the SOA Platform. This integration enables your services to make calls into a jBPM process through the jBPM Command API. Of the commands in the command API, the following (3) are available for use from ESB:
  • NewProcessInstanceCommand creates a new ProcessInstance using a process definition that has already been deployed to jBPM. The process instance is left in the start state so that tasks referenced by start node are executed.
  • StartProcessInstanceCommand is the same as NewProcessInstanceCommand, except that the process instance that is created is moved from the start position to the first node in the process graph.
  • As its name implies, CancelProcessInstanceCommand cancels a process instance.
But, this is only part of the JBossESB - jBPM integration. The integration also supports the orchestration of services from jBPM processes. For a fuller description of the integration, see: http://jboss-soa-p.blogspot.com/2009/06/hanging-together-on-soa-platform.html

Scripting

This group of actions makes it possible for you to create custom actions using scripting languages. As of release 4.3 of the SOA Platform, scripting actions in Groovy and Bean Scripting Framework (BSF) are supported.
  • GroovyActionProcessor (org.jboss.soa.esb.actions.scripting.GroovyActionProcessor) - which uses Groovyscripts
  • ScriptingAction (org.jboss.soa.esb.actions.scripting.ScriptingAction) - which uses the Bean Scripting Framework and supports BeanShell, Jython and JRuby scripts
Both these actions accept the message payload and configuration data such as a Log4J logger as variables for the script that is executed by the action. In addition, they both support having scripting contained within the message itself.

Services

There's only one action in this group, but it's an important one.
  • The EJBProcessor (org.jboss.soa.esb.actions.EJBProcessor) action accepts a message and uses it to locate and invoke an EJB. (The SOA Platform supports both EJB2 and EJB3 through this action.) In order to find the correct EJB and invoke the correct method, you specify information such as the jndi-name, initial-context-factory, provider-url, method name and and other configuration specific to the EJB as message properties
Routing

One of the major tasks that the JBoss ESB in the SOA Platform performs is the routing of messages to services. (Remember that in an ESB, everything is either a message or a service.) In an earlier post, I described how to use JBoss Rules to perform content based routing (CBR), where the content of an incoming message determines its routing. The full set of routing actions are:
  • Aggregator (org.jboss.soa.esb.actions.Aggregator) - This is an interesting one. The Aggregator, as its name indicates, aggregates information together. This action enables you to combine information from multiple messages into one message. The Aggregator action is an implementation of the Aggregator Enterprise Integration Pattern[6].
  • EchoRouter (org.jboss.soa.esb.actions.routing.EchoRouter) - In contrast, the EchoRuter just logs and returns the incoming message.
  • HttpRouter (org.jboss.soa.esb.actions.routing.HttpRouter) - The HttpRouter routes the incoming message to a URL that you specify in the action definition.
  • JMSRouter (org.jboss.soa.esb.actions.routing.JMSRouter) - This action routes the incoming message to JMS. For example, to a JMS queue where a service can then retrieve the message asynchronously. In order to find the correct JMS queue or topic, you specify values for properties such as jndi-name, initial-context-factory, and jndi-URL in the action definition.
  • ContentBasedRouter (org.jboss.soa.esb.actions.ContentBasedRouter) - I described content based routing with JBoss Rules in an earlier post to this blog http://jboss-soa-p.blogspot.com/2009/07/when-content-knows-way-content-based.html). Content based routing is a flexible approach to message routing in that the content in a message is actually the determining factor in determining the route a message takes.
  • StaticRouter (org.jboss.soa.esb.actions.StaticRouter) - As its name implies, this router establishes static routes that do not change based on the content of the messages or a set of routing rules.
  • StaticWiretap (org.jboss.soa.esb.actions.StaticWiretap) - Maybe it's the comic book fan in me, but this is my favorite name for an action. There's something film noir-ish about a "wiretap." You can almost imagine Humphrey Bogart sitting it the back room of a bar listening in on a wiretapped SOA action. (On black and white film, of course.) In practice, it's not all that exciting. This action implements the Enterprise Integration Pattern for a wiretap[7]. The goal of a wiretap is to inspect each message, without affecting the operation of the action chain. This can be a useful action to use to aid in debugging a service.
Notifiers

Just as a gateway listener enables you to move ESB-unaware messages (in other words, messages in a format other than (org.jboss.soa.esb.message.Message) onto the SOA Platform's ESB, notifiers (org.jboss.soa.esb.actions.Notifier) enable you to move ESB-aware messages from the ESB to ESB-unaware services. The notifiers convert the ESB-aware messages into data into formats that these services can understand.

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. The following notification targets (the names are very self-explanatory) are supported by the SOA Platform:
  • NotifyConsole - Prints the contents of the ESB message to the server log
  • NotifyFiles - Prints the message contents to a file or files
  • NotifySQLTable - Inserts the message contents into an existing database table
  • NotifyFTP - Prints the message contents to a file and then sends the file to a server via FTP
  • NotifyQueues and NotifyTopics - Writes the message contents into a JMS message and adds the JMS message to a specified JMS queue or topic
  • NotifyEmail - Sends the message contents in a mail message
Webservices/SOAP

When you're considering web services and the SOA Platform, you actually have two separate goals to achieve; you want to be able to expose webservice endpoints as services and you also want to be able to invoke web services from actions defined in a service.

There are (3) out of the box actions that support web services:
  • SOAPProcessor (org.jboss.soa.esb.actions.soap.SOAPProcessor) - This action is only available for use on the SOA Platform embedded server. You use this action to expose webservice endpoints. To do this, you write a "service wrapper webservice" that conforms to the JSR 181[8] ("Web Services Metadata for the Java Platform") standard. JSR 181 enables you to create webservices from a POJO by adding annotations such as @WebService and @WebMethod to it. This service wrapper webservice wraps calls to your service and exposes that service through JBossESB listeners. Your service can also be accessed through other transports (FTP, JMS, etc.) supported by the JBossESB in the SOA Platform. These 3 types of JBossWS webservice endpoints can be exposed through JBoss ESB listeners using this action:

    • Webservice bundled into a JBossESB .esb archive - When the webservice .war is bundled into a deployed .esb archive
    • Webservice bundled into an .ear - When the .war is bundled into a deployed .ear
    • Standalone webservice - When the webservice .war is deployed

  • WISE SOAPClient (org.jboss.soa.esb.actions.soap.wise.SOAPClient) - This action uses the WISE[9] client service (org.jboss.wise.core.client.WSService) to create a JAX-WS (Java API for XML Web Services) client class and then call the target service. WISE stands for "Wise Invokes Services Easily" and enables the dynamic generation of a client to access web services, without your having to write the code for the client. (This is referred to as "zero-code web service invocation").
  • SOAPClient (org.jboss.soa.esb.actions.soap.SOAPClient) - This action, uses the soapUI Client Service (org.jboss.soa.esb.services.soapui.SoapUIClientService) to create a message and then route that message to the target service.
Examples

We've already walked through how to use some of the out of the box actions (Business Process Management, Routing, Notifiers) in earlier posts to this blog. Now, let's look at some examples of other types of out of the box actions. As always, the "quickstart" programs in the SOA Platform provide the example code.

Transformation

We'll start by looking at transformations and the SmooksAction out of the box action. But first, let's take a look at Smooks. It's common to refer to Smooks as a transformation engine, but that's not the full story. Smooks is really a more general purpose processing framework that is capable of dealing with with fragments of a message. Smooks accomplishes this with "visitor logic", where a "visitor" is Java code that performs a specific action on a specific fragment of a message. This enables Smooks to perform different actions on different fragments of messages. As is stated by the Smooks project[10]:

Smooks supports these types of message fragment processing:
  • Templating: Transform message fragments with XSLT or FreeMarker
  • Java Binding: Bind message fragment data into Java objects
  • Splitting: Split messages fragments and rout the split fragments over multiple transports and destinations
  • Enrichment: "Enrich" message fragments with data from databases
  • Persistence: Persist message fragment data to databases
  • Validation: Perform basic or complex validation on message fragment data
The SOA Platform SmooksAction out-of-the-box action provides you access to all these Smooks capabilities.

Let's look at a very simple example, the aptly named "transform_XML2XML_simple" quickstart. This quickstart performs a message transformation by applying an XSLT (EXtensible Stylesheet Language Transformations) to an XML message. The message is transformed into XML in a different form. The interesting parts of the quickstart's jboss-esb.xml file are:

25  <actions mep="OneWay">
26 <action name="print-before" class="org.jboss.soa.esb.actions.SystemPrintln">
27 <property name="message" value="[transform_XML2XML_simple] Message before transformation" />
28 </action>
29 <action name="simple-transform" class="org.jboss.soa.esb.smooks.SmooksAction">
30 <property name="smooksConfig" value="/smooks-res.xml" />
31 <property name="reportPath" value="/tmp/smooks_report.html" />
32 </action>
33 <action name="print-after" class="org.jboss.soa.esb.actions.SystemPrintln">
34 <property name="message" value="[transform_XML2XML_simple] Message after transformation" />
35 </action>
  • Line 25: This line is not specific to transformations, but it's worth mentioning. "mep" stands for "message exchange pattern." The pattern used by this quickstart is "one-way" in that the requester invokes a service (by sending it a message) and then does not wait for a response.
  • Lines 26-28, 33-35: These lines simply cause the message to be written to the server log before and after its transformation. (org.jboss.soa.esb.actions.SystemPrintln, is, incidentally, the only out-of-the-box action in the Miscellaneous action group.)
  • Line 29: Here's where we specify that we want to invoke a SmooksAction
  • Line 30: And here is the XSLT that will be executed. We'll examine this file in a moment.
  • Line 31: This line is actually not in the quickstart. I've added the reportPath property it so that we can review the resulting report. Note that generating this report does require some processing resources, so it should not be used in production environments. See below for screen-shots of this report.





Now, let's look at smooks-res.xml:
1  <?xml version='1.0' encoding='UTF-8'?>
2 <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.0.xsd">
3
4 <resource-config selector="OrderLine">
5 <resource type="xsl">
6 <![CDATA[<line-item>
7 <product><xsl:value-of select="./Product/@productId" /></product>
8 <price><xsl:value-of select="./Product/@price" /></price>
9 <quantity><xsl:value-of select="@quantity" /></quantity>
10 </line-item>]]>
11 </resource>
12 <param name="is-xslt-templatelet">true</param>
13 </resource-config>
14 </smooks-resource-list>
  • Line 2: The namespace referenced here is the Smooks XML Schema Definition
  • Line 4: The resource-config element corresponds to an org.milyn.cdr.SmooksResourceConfiguration object[11]
  • Lines 7-9: These XPath (XML Path Language) [12] statements locate the Product element's productId and price attributes and the OrderLine element's quantity attributes. XPath is used by XSLT to find or reference data in XML documents.
When you run this quickstart, you'll see the XML message as defined in SampleOrder.xml displayed before and after it undergoes the XSLT transformation.

Web Services

Another important feature of the SOA Platform is its ability to publish web services as services. Remember how we talked about how the JBossESB included in the SOA Platform made it possible to deploy web service endpoints as endpoints on the ESB? The services can then also be accessed through other transports (FTP, JMS, etc.) supported by the JBossESB in the SOA Platform.

We'll look first at the webservice_producer quickstart. This quickstart demonstrates how to expose a JSR181 web service endpoint on the JBossESB within the SOA Platform by using the org.jboss.soa.esb.actions.soap.SOAPProcessor action.

Let's begin by looking at these lines in jboss-esb.xml:
37  <actions>
38 <action name="print-before" class="org.jboss.soa.esb.actions.SystemPrintln">
39 <property name="message"
40 value="[Quickstart_webservice_producer] BEFORE invoking jbossws endpoint"/>
41 </action>
42 <action name="JBossWSAdapter" class="org.jboss.soa.esb.actions.soap.SOAPProcessor">
43 <property name="jbossws-endpoint" value="GoodbyeWorldWS"/>
44 </action>
45 <action name="print-after" class="org.jboss.soa.esb.actions.SystemPrintln">
46 <property name="message"
47 value="[Quickstart_webservice_producer] AFTER invoking jbossws endpoint"/>
48 </action>
49 <action name="testStore" class="org.jboss.soa.esb.actions.TestMessageStore"/>
50 </actions>
  • Lines 38 and 45: As we discussed earlier, the SystemPrintln action displays the message to the server.log
  • Lines 42,43: Here is where we invoke the SOAPProcessor action to generate the JSR181 web service from the specified class, GoodbyeWorldWS
17  @WebService(name = "GoodbyeWorldWS", targetNamespace="http://webservice_producer/goodbyeworld")
21 @WebMethod
22 public String sayGoodbye(@WebParam(name="message") String message) {
23
24 Message esbMessage = SOAPProcessor.getMessage();
25 if(esbMessage != null) {
26 System.out.println("**** SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());
28 }
29 System.out.println("Web Service Parameter - message=" + message);
30 return "... Ah Goodbye then!!!! - " + message;
31 }
(Remember how we talked about having to write a "service wrapper webservice" that conforms to the JSR 181 ("Web Services Metadata for the Java Platform") standard? You do this by adding @WebService and @WebMethod annotations.)
  • Line 17: The definition of the web service.
  • Lines 24-31: The definition of the method in the web service to be invoked when the action is triggered.
The following lines in jboss-esb.xml are tangential to the goal of demonstrating exposing the webservice endpoint. But it is interesting as is shows some of the paths and protocols that can be used to inserting a message into the JBossESB withing the SOA Platform:
6   <providers>
7 <jms-provider name="JBossMQ" connection-factory="ConnectionFactory">
8 <jms-bus busid="quickstartGwChannel">
9 <jms-message-filter dest-type="QUEUE" dest-name="queue/quickstart_webservice_producer_gw"/>
10 </jms-bus>
11 <jms-bus busid="quickstartEsbChannel">
12 <jms-message-filter dest-type="QUEUE" dest-name="queue/quickstart_webservice_producer_esb"/>
13 </jms-bus>
14 </jms-provider>
15
16 <jbr-provider name="JBR-Http" protocol="http" host="localhost">
17 <jbr-bus busid="Http-1" port="8765" />
18 </jbr-provider>
19
20 <jbr-provider name="JBR-Socket" protocol="socket" host="localhost">
21 <jbr-bus busid="Socket-1" port="8888" />
22 </jbr-provider>
23 </providers>
30 <listeners>
31 <jms-listener name="JMS-Gateway" busidref="quickstartGwChannel" is-gateway="true"/>
32 <jbr-listener name="Http-Gateway" busidref="Http-1" is-gateway="true"/>
33 <jbr-listener name="Socket-Gateway" busidref="Socket-1" is-gateway="true"/>
34 <jms-listener name="JMS-ESBListener" busidref="quickstartEsbChannel"/>
35 </listeners>
The last quickstart we'll look at is webservice_consumer1 - as you can guess by its name, this quickstart demonstrates how to access (or "consume") a JSR181 Web Service in an ESB action by using the org.jboss.soa.esb.actions.soap.SOAPClient action.

This quickstart requires that a web service be deployed for the SOAPClient to access. The webservice is very simple and is deployed in a .war. In HelloWorldWS.java we see the definition of the web service:
28  @WebService(name = "HelloWorld", targetNamespace = "http://webservice_consumer1/helloworld")
29 public class HelloWorldWS
30 {
31 @WebMethod
32 public String sayHello(@WebParam(name = "toWhom")
33 String toWhom)
34 {
35
36 String greeting = "Hello World Greeting for '" + toWhom + "' on " + new java.util.Date();
37
38 return greeting;
39
40 }
41 }

Pay attention to the "toWhome" parameter. We'll look for this in the web service request.

In MyRequestAction.java, we see that toWhome parameter added as the key in a hashmap for the msg body:
44  /*
45 * Convert the message into a webservice request map.
46 */
47 public Message process(Message message) throws Exception
48 {
49 logHeader();
50 String msgBody = (String) message.getBody().get();
51 HashMap requestMap = new HashMap();
52
53 // add parameters to the web service request map
54 requestMap.put("sayHello.toWhom", msgBody);
55
56 // The "paramsLocation" property was set in jboss-esb.xml to
57 // "helloworld-request-parameters"
58 message.getBody().add(requestMap);
59 System.out.println("Request map is: " + requestMap.toString());
60
61 logFooter();
62 return message;
63 }
  • Line 54 - The towhome parameter added as the key in a hashmap for the msg body. The key of the HashMap is an Object-Graph Navigation Language (OGNL)[13] expression that identifies the SOAP parameter to be populated with the key's corresponding value. OGNL is an expression language for getting and setting Java objects properties.
Then, MyResponseAction.java retrieves the message and displays the response - see lines 54-55:
44  /*
45 * Retrieve and output the webservice response.
46 */
47 public Message process(Message message) throws Exception
48 {
49
50 logHeader();
51
52 // The "responseLocation" property was set in jboss-esb.xml to
53 // "helloworld-response"
54 Map responseMsg = (Map) message.getBody().get(Body.DEFAULT_LOCATION);
55 System.out.println("Response Map is: " + responseMsg);
56
57 logFooter();
58 return message;
59 }

In jboss-esb.xml:

36  <actions mep="OneWay">
37 <action name="request-mapper"
38 class="org.jboss.soa.esb.samples.quickstart.webservice_consumer1.MyRequestAction">
39 </action>
40 <action name="soapui-client-action"
41 class="org.jboss.soa.esb.actions.soap.SOAPClient">
42 <property name="wsdl"
43 value="http://127.0.0.1:8080/Quickstart_webservice_consumer1/HelloWorldWS?wsdl" />
44 <property name="responseAsOgnlMap" value="true" />
45 <property name="SOAPAction" value="sayHello"/>
46 </action>
47 <action name="response-mapper"
48 class="org.jboss.soa.esb.samples.quickstart.webservice_consumer1.MyResponseAction">
49 </action>
50 <action name="testStore" class="org.jboss.soa.esb.actions.TestMessageStore"/>
51 </actions>
  • Line 41: The SOAPClient class makes the call to the webservice. This is the "zero-code web service invocation" that we mentioned earlier in the post.
  • Line 43: The reference to the web service's wsdl
  • Line 44: The parameter responseAsOgnlMap tells the JBossESB move the SOAP response data into that OGNL-based map and attach it to the message.
    Line 45: The reference to the method to be invoked in the web service.
Closing Thoughts

It can be daunting to attempt to create a new service based application from scratch. The SOA Platform's combination of out-of-the-box actions and quickstart can make this task much easier. Even without a jet turbine engine. ;-)

Resources

[1] Kinney, Jeff, Diary of a Wimpy Kid, The Last Straw, Amulet Books; 1st edition (January 2009), ISBN-10: 0810970686, ISBN-13: 978-0810970687

[2] http://www.redhat.com/docs/en-US/JBoss_SOA_Platform/4.3.GA/html/Programmers_Guide/chap-SOA_ESB_Programmers_Guide-Out-of-the-box_Actions.html

[3] http://xstream.codehaus.org/

[4] http://www.smooks.org/mediawiki/index.php?title=Main_Page

[5] http://www.milyn.org/javadoc/v1.0/smooks/org/milyn/container/plugin/PayloadProcessor.html

[6] http://www.enterpriseintegrationpatterns.com/Aggregator.html The patterns described at this site and in the corresponding book were created to assist designers and implementers in creating enterprise software solutions. The patterns are platform and language agnostic, so they can be very helpful across many systems.

[7] http://www.enterpriseintegrationpatterns.com/WireTap.html

[8] http://jcp.org/en/jsr/summary?id=181

[9] http://jboss.org/wise

[10] http://www.smooks.org/mediawiki/index.php?title=Why_Smooks_was_Created

[11] http://www.milyn.org/javadoc/v1.0/smooks/org/milyn/cdr/SmooksResourceConfiguration.html

[12] http://www.w3.org/TR/xpath

[13] http://www.opensymphony.com/ognl/

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.