Wednesday, January 12, 2011

The JBossESB-Smooks Integration in the SOA Platform

One of the great strengths of the SOA Platform is its wealth of software integrations. In this post, we'll examine the JBoss ESB - Smooks integration in the SOA Platform and how it can be used to perform two of the primary operations or an ESB; message transformations and message routing:
  • Message transformation enables the JBossESB to translate message payloads (the message body plus attachments and properties) from one form to another so that the message can be processed by different services.
  • Message routing enables the JBossESB to move messages to files, to JMS assets such as queues, and over the ESB "bus" between services.
A 30-Second Introduction to 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 (http://www.smooks.org/mediawiki/index.php?title=Why_Smooks_was_Created):

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.

The JBossESB implements several out of the box actions to support message transformation and routing. SmooksAction (org.jboss.soa.esb.smooks.SmooksAction) enables you to use a powerful set of Smooks 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.

A Simple Transformation with Smooks

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:
1:  <actions mep="OneWay">  
2:  <action class="org.jboss.soa.esb.actions.SystemPrintln" name="print-before">  
3:  <property name="message" value="[transform_XML2XML_simple] Message before transformation">  
4:  </property>  
5:  <action class="org.jboss.soa.esb.smooks.SmooksAction" name="simple-transform">  
6:  <property name="smooksConfig" value="/smooks-res.xml">  
7:  <property name="reportPath" value="/tmp/smooks_report.html">  
8:  </property>  
9:  <action class="org.jboss.soa.esb.actions.SystemPrintln" name="print-after">  
10: <property name="message" value="[transform_XML2XML_simple] Message after transformation">  
11: </property>  
  • Line 1: 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 2-4, 9-24: 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 5: Here's where we specify that we want to invoke a SmooksAction
  • Line 6: And here is the XSLT that will be executed. We'll examine this file in a moment.
  • Line 7: 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) 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.

Content Based Routing with Smooks

The routing of data from one place to another is one of the most basic, and common, problems facing any networked software application. This routing can take many forms, such as email being sent to the correct recipient or network traffic being routed around the globe based on system names defined in DNS. In the context of an Enterprise Service Bus such as the JBossESB in the SOA Platform, where everything is either a message or a service, routing means getting messages delivered to the correct services.

JBossESB Routing Choices

The JBossESB supports multiple actions to route messages to services right out of the box (http://jboss-soa-p.blogspot.com/2009/09/works-great-right-out-of-box.html) such as:
  • 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.
  • 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 (http://www.eaipatterns.com/WireTap.html). 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.
JBossESB and Contest Based Routing

These are all very useful, but they are also all static in nature in that once you define the path for the messages, that's the path that the messages always take. For example, a static path could be used so that messages from the sales department service are always sent to the warehouse inventory control service. But, what if you had several warehouses each of which stored a different set of products? And what if you wanted to be able to vary the route of the messages dynamically? For example, what if you want the route the message takes to be based on the actual content in the message? Well, the JBossESB also supports multiple types of content based routing (CBR).

Two relatively lightweight approaches (http://jbossesb.blogspot.com/2009/10/content-based-routing-in-jbossesb-just.html) for content based routing are supported:
  • XPath Content Based Routing - Note that this is completely defined in the jboss-esb.xml file. No additional configuration files are needed.
  • Regex Content Based Routing - An external configuration file is used to define XPath expressions that govern the routing.
Now, if you are dealing with a more complex set of routing "rules," the JBossESB supports using JBoss Rules (http://jboss-soa-p.blogspot.com/2009/07/when-content-knows-way-content-based.html) to define the routing rules. Rules provides you with the rich feature set of JBoss Drools to control the dynamic routing of messages based on their content. JBoss Drools is a complete enterprise platform for rules-based application development, workflow, administration, and event processing. It also provides an integration with JBossESB to support content based routing. You define the content based routing algorithm in a set of rules.

But wait - there's more.

Content Based Routing with Smooks

You might think of JBoss Smooks (http://www.smooks.org/) as primarily a tool for performing XML transformations (for example, for transforming data from CSV to XML), but it can also be used for content based routing on the JBossESB in the SOA Platform. Some of the things that Smooks allows you to do with content based routing are:
  • Splitting of messages - Don't just route the whole message, but split out the parts of the message, say sales order items and route them to separate services.
  • Even more complex splitting of messages - For example, split out those sales order items and then combine each of them with data from other parts of the message (say, customer informatin) before performing the message routing.  This is more than just basic message fragment extraction, as the extracted data can be combined or otherwise processed.
  • Routing split message fragments in multiple formats - For example, routing XML to one service, Java to another, CSV data to still another.
  • Fast performance - Smooks is able to perform all the message splitting and routing (even for multiple destinations and multiple formats) in a single filtering pass of the message. This makes for fast performance as there is no need to evaluate multiple XPaths multile times on the same message. Smooks is also able to handle big (make that REALLY BIG as in > 50MB)  messages efficiently.
  • Complex conditionals - Smooks is not limited to just the conditionals that XPath supports.
The best way to understand and appreciate the advantages and flexibility of content based routing on the JBossESB with Smooks is to see it in action. Note that we'll be following a programming tradition as we'll take a quickstart as starting point, and expand it to fulfill our requirements.

The quickstart is named: smooks_file_splitter_router

Now, as the quickstart's name indicates, its objective is to demonstrate both splitting files and routing messages. The quickstart's "Splitter" service makes use of a File Gateway. Its file system listener ("fs-listener") uses the Smooks org.jboss.soa.esb.smooks.splitting.FileStreamSplitter to split an incoming XML message and route message fragments to the "Receiver" service.

Let's take a closer look at how this works, and then we'll expand on the quickstart to perform some additional content based routing.

The quickstart is initiated by the "runtest" ant target. This invokes the org.jboss.soa.esb.sample.quickstart.smooksfilesplitterrouter.InputOrderGenerator class which creates the "SampleOrder.xml" file for the File Gateway. SampleOrder.xml contains multiple orders and follows this form:
1:  <order id="332">  
2:  <header>  
3:  <customer number="123">Joe</customer>  
4:  </header>  
5:  <order-items>  
6:  <order-item id="1">  
7:  <product>1</product>  
8:  <quantity>2</quantity>  
9:  <price>8.80</price>  
10:  </order-item>  
11:  <order-item id="2">  
12:  <product>2</product>  
13:  <quantity>2</quantity>  
14:  <price>8.80</price>  
15:  </order-item>  
16:  <order-item id="3">  
17:  <product>3</product>  
18:  <quantity>2</quantity>  
19:  <price>8.80</price>  
20:  </order-item>  
21:  <order-item id="4">  
22:  <product>4</product>  
23:  <quantity>2</quantity>  
24:  <price>8.80</price>  
25:  </order-item>  
26:  <order-item id="5">  
27:  <product>5</product>  
28:  <quantity>2</quantity>  
29:  <price>8.80</price>  
30:  </order-item>  
31:  <order-item id="6">  
32:  <product>6</product>  
33:  <quantity>2</quantity>  
34:  <price>8.80</price>  
35:  </order-item>  
36:  <order-item id="7">  
37:  <product>7</product>  
38:  <quantity>2</quantity>  
39:  <price>8.80</price>  
40:  </order-item>  
41:  <order-item id="8">  
42:  <product>8</product>  
43:  <quantity>2</quantity>  
44:  <price>8.80</price>  
45:  </order-item>  
46:  <order-item id="9">  
47:  <product>9</product>  
48:  <quantity>2</quantity>  
49:  <price>8.80</price>  
50:  </order-item>  
51:  </order-items>  
52:  </order>  

The best way to understand the operation of the quickstart is to take a closer look at the providers and services defined in the jboss-esb.xml file. Note that in the case of this quickstart, jboss-esb.xml is actually generated from jboss-esb-unfiltered.xml at run time to include environment specific information such as directory names. Here's jboss-esb-unfiltered.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.1.0.xsd"  
3:    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4:    xsi:schemaLocation="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.1.0.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.1.0.xsd"  
5:    parameterReloadSecs="5">  
6:     
7:    <providers>  
8:        <fs-provider name="FSprovider1">  
9:            <fs-bus busid="smooksFileChannel">  
10:                <fs-message-filter  
11:                    directory="@INPUTDIR@"  
12:                    input-suffix=".xml"  
13:                    work-suffix=".esbWorking"  
14:                    post-delete="true"  
15:                    post-directory="@OUTPUTDIR@"  
16:                    post-suffix=".sentToEsb"  
17:                    error-delete="false"  
18:                    error-directory="@ERRORDIR@"  
19:                    error-suffix=".IN_ERROR"  
20:                  />  
21:            </fs-bus>  
22:        </fs-provider>  
23:    
24:    </providers>  
25:    
26:    <services>  
27:    
28:      <!--  
29:          Splitter Service...  
30:      -->  
31:      <service category="QS" name="Splitter" description="Splitter Service" invmScope="GLOBAL">  
32:          <listeners>  
33:          <!-- Splitting the message at the gateway via the FileStreamSplitter composer class allows us to  
34:                   handle huge messages... -->  
35:              <fs-listener name="FileGateway" busidref="smooksFileChannel" is-gateway="true" schedule-frequency="2">  
36:                  <property name="composer-class" value="org.jboss.soa.esb.smooks.splitting.FileStreamSplitter"/>  
37:                  <property name="splitterConfig" value="/smooks-config.xml"/>  
38:              </fs-listener>  
39:          </listeners>  
40:          <actions mep="OneWay">  
41:              <action name="print" class="org.jboss.soa.esb.actions.SystemPrintln">  
42:                  <property name="message" value="[Splitter] Message Split complete"/>  
43:              </action>  
44:       
45:              <!-- The next action is for Continuous Integration testing -->  
46:              <action name="testStore" class="org.jboss.soa.esb.actions.TestMessageStore"/>  
47:          </actions>  
48:      </service>  
49:        
50:      <!--  
51:          Receiver Service...  
52:      -->  
53:      <service category="QS" name="Receiver" description="Receiver Service" invmScope="GLOBAL">  
54:          <actions mep="OneWay">  
55:              <action name="print" class="org.jboss.soa.esb.actions.SystemPrintln">  
56:                  <property name="message" value="[Receiver] Message Fragment Received"/>  
57:              </action>  
58:          </actions>  
59:      </service>  
60:       
61:    </services>  
62:        
63:  </jbossesb>  

  • Lines 7-24 - This is the definition of the file system gateway. What's interesting to note here are:
  • Lines 11-12 - Here's the input directory. Based on its configuration, the listener will listen for files with an .xml extension that are created in the INPUT directory.
  • Line 13 - While the file gateway is processing the file, it is renamed to have an extension of "esbWorking".
  • Lines 14-16 - After the file is processed, it is renamed again, this time to have an extension of "sentToEsb," and then it is deleted.
  • Lines 17-19 - And, if an error occurs, the file is renamed to have an extension of "IN_ERROR" and is saved in the error directory.
The "Splitter" service is defined in lines 31-48. The most interesting lines are:
35:  <fs-listener name="FileGateway" busidref="smooksFileChannel" is-gateway="true" schedule-frequency="2">  
36:      <property name="composer-class" value="org.jboss.soa.esb.smooks.splitting.FileStreamSplitter"/>  
37:      <property name="splitterConfig" value="/smooks-config.xml"/>  
38:  </fs-listener>  

  • Line 35 - Here's the start of the file gateway definition.
  • Line 36 - Note that when the file gateway detects the presence of a file, it invokes the Smooks org.jboss.soa.esb.smooks.splitting.FileStreamSplitter class. And what does this class do with the file?
  • Line 37 - It splits the message into fragments, based on the conditions and actions defined in the smooks-config.xml file.
The message fragments are then routed to the Receiver service, which simply writes the message to the server.log.

The smooks-config.xml file is where the file splitting and routing is defined, so let's take a closer look there:

1:  <?xml version="1.0"?>  
2:  <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"  
3:          xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd"  
4:          xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd"  
5:          xmlns:esbr="http://www.jboss.org/xsd/jbossesb/smooks/routing-1.0.xsd">  
6:   
7:      <params>          
8:          <param name="stream.filter.type">SAX</param>  
9:      </params>  
10:   
11:     <conditions>  
12:         <!-- route the even numbered order items -->  
13:         <condition id="routeItem"><!-- orderItem.itemId % 2 == 0 --></condition>  
14:     </conditions>  
15:   
16:     <!-- Capture some data from the message into the bean context... -->  
17:     <jb:bean beanId="header" class="java.util.Hashtable" createOnElement="order">  
18:         <jb:value property="orderId" data="order/@id"/>  
19:         <jb:value property="customerNumber" data="header/customer/@number"/>  
20:         <jb:value property="customerName" data="header/customer"/>  
21:     </jb:bean>  
22:     <jb:bean beanId="orderItem" class="java.util.Hashtable" createOnElement="order-item">  
23:         <jb:value property="itemId" data="order-item/@id"/>  
24:         <jb:value property="productId" data="order-item/product"/>  
25:         <jb:value property="quantity" data="order-item/quantity"/>  
26:         <jb:value property="price" data="order-item/price"/>  
27:     </jb:bean>  
28:   
29:     <!-- On each order-item, apply a template to the data captured into the bean context,  
30:          binding the templating result back into the bean context under the  
31:          beanId "orderItemFragment" to be routed by the following ESB Router... -->  
32:     <ftl:freemarker applyOnElement="order-item">  
33:         <condition idRef="routeItem" />  
34:         <ftl:template>/orderitem-split.ftl</ftl:template>  
35:         <ftl:use>  
36:            <ftl:bindTo id="orderItemFragment" />  
37:         </ftl:use>  
38:     </ftl:freemarker>  
39:   
40:     <!-- On each order-item, route the "orderItemFragment" bean to the  -->  
41:     <esbr:routeBean beanIdRef="orderItemFragment" toServiceCategory="QS" toServiceName="Receiver" routeOnElement="order-item">  
42:         <condition idRef="routeItem" />  
43:     </esbr:routeBean>  
44:  
45: </smooks-resource-list>  
As we described a minute ago, the two main actions being performed are the splitting of the incoming message into fragments, and then the routing of the resulting fragments to services. Let's look at the how the splitting is performed. Recall that the sampleOrder.xml file included these XML elements.
 <order id='332'>  
 <header>  
 <customer number="123">Joe</customer>  
 </header>  
 <order-items>  
 <order-item id='1'>  
 <product>1</product>  
 <quantity>2</quantity>  
 <price>8.80</price>  
 </order-item>  
 (followed by more order-items)  

Remember how we talked about Smooks' ability to process large messages efficiently? At this point, it's important to note that when Smooks splits the XML message, it processes each orderItem one at a time, and it only keeps one orderItem in memory at a time. This is one way in which Smooks is able to efficiently process large messages.
  • Lines 17-21 - As the comments indicate, this captures data from the message, and extracts the orderId, customerName and customerNumber properties (note the use of xPath to navigate the original XML message) into a header (org.jboss.soa.esb.sample.quickstart.smooksfilesplitterrouter.Header) bean.
  • Lines 22-27 - And, this does the same for the orderItems.
  • Lines 30-38 - And here, Smooks takes the header and orderItems beans, and uses a Freemarker (http://freemarker.sourceforge.net/) template to create an orderItemFragment bean that has this organization:
1:   <orderitem id="${orderItem.itemId}" order="${header.orderId}">  
2:       <customer>  
3:           <name>${header.customerName}</name>  
4:           <number>${header.customerNumber}</number>  
5:       </customer>  
6:       <details>  
7:           <productId>${orderItem.productId}</productId>  
8:           <quantity>${orderItem.quantity}</quantity>  
9:           <price>${orderItem.price}</price>  
10:       </details>  
Note that Smooks is able to combine elements from both the header and orderItems beans into an orderItemFragment bean.

So much for the splitting, now let's take a look at how Smooks routes the message fragments to services.
  • Lines 11-14 - First, Smooks establishes a condition ("routeItem") that must be met in order for the message to be routed. In this example, the value of the orderItem.itemId element must be an even number.
  • Lines 41 - And here's where the routing actually starts. This line defines the attributes for the JBoss ESB router ("routeBean"). The attributes defined for this quickstart are:
  • beanIdRef  - this is the references of the bean that will be routed to the intended service
  • toServiceCategory - The category of that service, as defined in the quickstart's jboss-esb.xml file
  • toServiceName - And the name of that service ("Receiver"), again, as defined in the quickstart's jboss-esb.xml file
  • routeOnElement - And, finally the content element used to determine the route for messages to follow - in the case of the quickstart, this is the order-item
A good way to look at content based routing with Smooks is that new messages are built from data in fragments in the original source message. In the case of the quickstart, we want to route data from each in an message.  In this case, since the routeOnElement is the order-item, when the Smooks processing filter reaches the end of every order-item fragment, it will route a message to "somewhere" i.e. the end of the fragment is used to trigger the routing. (Many thanks to Tom F. for this summary!)
  • Line 42 - The ID of the condition that must match in order for the routing to occur. This ID ("routeItem") maps to the routing condition defined in line 13.
One attribute that is not explicitly defined by in this quickstart is "routeBefore." This attribute enables you to control when the routing occurs. This can be either (at the start) a specific message fragment is processed by Smooks, or after (at the end) it has processed the fragment. The default value is after, which is why the quickstart does not explicitly define the attribute.

Note that the full set of attributes that control routing to JBossESB services with Smooks are defined in the routing-1.0.xsd file that is deployed to a SOA Platform server in: smooks.esb/META-INF/xsd/jbossesb/smooks/routing-1.0.xsd

Closing Thoughts

OK, let's review what happened. The quickstart demonstrated two features of the integration between Smooks and the JBossESB in the SOA Platform; file splitting and routing. First, the quickstart took a single, and potentially very large XML file, and split its content into two separate beans, then combined elements of those two beans into a new bean. When Smooks performed this splitting and transformation of data from one form to another, it did so in a serial fashion, where only one instance was in memory at a time, so that it could efficiently handle even large numbers of instances. Then, Smooks found an element in the newly created bean, and based on the content of that element, routed the newly created bean in a message to an ESB service.

And - note that in order for this quickstart to accomplish these tasks, it was not necessary to write large amounts of new custom code. A Smooks configuration file and a FreeMarker template file, coupled with a call to the org.jboss.soa.esb.smooks.splitting.FileStreamSplitter class was all that was needed in the quickstart. Smooks and the JBossESB in the SOA Platform did the rest!

Acknowledgements

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

References
 
 

Monday, September 27, 2010

Cross Posted to DZone

The SOA Platform - JBossESB - SAML post was just cross posted on DZone:

http://java.dzone.com/articles/asserting-security-jbossesb

Monday, September 20, 2010

Asserting Security - The JBossESB Integration with SAML in the JBoss SOA Platform

Introduction - When Two User Accounts is One Too Many

Have you ever noticed how whenever you visit a new doctor, you have to fill out a new medical history, longhand and on paper? Sometimes you make a mistake or sometimes someone else makes a mistake transcribing the information. Why can't the information be shared in a secure way? The same type of problem exists with computer systems, web applications, and passwords. If you frequently access multiple remote systems or applications, you invariably have to manage multiple user accounts. (At one of my past jobs, I actually had 17 user accounts across several systems.  The killer was that the accounts all had different password complexity rules and expiration schedules.)

What's happening here is that you have authentication and authorization information in different security "domains". What's needed in our internet and web-based world, is a standard way to securely (and easily!) share user accounts between systems. In other words, we need a way to perform "single sign-on" (SSO) between multiple security domains. What you want to be able to do is to have the different applications and sites that you rely on, regardless of each application's implementation or operating environment, to be able to safely share your login information, without you having to login repeatedly, or maintain multiple different accounts.

One solution to this problem is the "Security Assertion Markup Language" (SAML).

The Standard: SAML

SAML was created by the Security Services Technical Committee of the Organization for the Advancement of Structured Information Standards (OASIS - http://www.oasis-open.org).

Wikipedia (http://en.wikipedia.org/wiki/SAML) defines SAML as: '...an XML-based standard for exchanging authentication and authorization data between security domains, that is, between an identity provider (a producer of assertions) and a service provider (a consumer of assertions)...'
Ref: Organization for the Advancement of Structured Information Standards (OASIS - http://www.oasis-open.org)

Let's dissect that deceptively short definition into smaller pieces. Each of these pieces covers one of SAML's important characteristics and the motivations for its creation.
  • XML based - One of the goals of the SAML is for it to be a standard that can be used by different applications and systems to replace proprietary security methods. If you're going to create a standard, then basing it on XML is a good start!
  • Exchanging data - The ultimate goal is of course to support the secure exchange of data between applications and systems and users. In the context of security, the applications, systems, and users are referred to as "principals.")
  • Authentication and Authorization - These two aspects of security are related, but refer to different tasks. Authentication refers to verifying the identity of the principle. Authorization refers to verifying that the principal is permitted to perform a specific action.
  • Security domains - "Federation" is an important concept to keep in mind when you talk about security domains. In the context of a political system, federation refers to the separating of overlapping of areas of control between the states that make up a greater federation. It may be that each state is able to make laws specific to the state, but they must also abide by the laws of the greater federation. (Ever hear of Fort Sumter? http://www.nps.gov/fosu/) It's like that when security domains are federated too. You might want to establish a large-scale domain for all your employees, so that they can all use the same login information to access multiple shared systems such as vacation trackers or a corporate intranet, while at the same time, you can establish smaller domains so that restricted groups of those employees can use that same login information to access purchasing or payroll information.
  • Identity Provider - In the context of using SAML, an identity provider supplies authentication information when and where requested by a principal. Note that this in contrast to security systems that rely on cookies stored and supplied by the client web browsers, or ticketing systems such as Kerberos where the client supplies a ticket to a service. In SAML, a Identity Providers are the producers of security "assertions."
  • Service Provider - In the context of using SAML, service providers rely on Identity Providers. When the principal requests its identity to be confirmed, an Identity Provider sends this information, in the form of a SAML sercurity assertion. So, in SAML, Service Providers are the consumers of the security assertions.
But, while it is a standard, it is not an implementation. For a SAML implemention, we need to look at JBoss PicketLink.

The Implementation: JBoss PicketLink

PicketLink (http://jboss.org/picketlink) is the JBoss Identity Management (IdM) project. PicketLink is actually built on the JBoss "PicketBox" (formerly known as "JBoss Security") framework. PicketBox (http://www.jboss.org/picketbox) provides developers with a framework where these security functions can be easily configured: authentication, authorization, auditing, and mapping (Principal to Roles and Attributes).

PicketLink is an "umbrella" project that pulls together multiple components to support various security and Identity Management requirements. As listed on the PicketLink project site, these components are:
  • AuthZ (a authorization framework)
  • Federated Identity (remember our discussion of federated security domains a minute ago?)
  • Identity Management (this provides a common framework for handing users and groups and supports using identity repositories such as LDAP)
  • Negotiation (this provides SPNego*/Kerberos based Desktop single sign-on)
  • XACML  (an implementation of the Oasis eXtensible Access Control Markup Language - this provides fine-grained authorization capabilities)
* Simple and Protected GSSAPI (Generic Security Services Application Programming Interface) Negotiation Mechanism - yes, that is a mouthful.



Security Token Service (STS)

The SAML integration with the JBossESB in the SOA Platform is supported by the PicketLink Security Token Service (PicketLinkSTS). PicketLink provides login modules (https://community.jboss.org/wiki/PicketLinkSTSLoginModules) two of which JBossESB uses.

The primary focus of the ESB integration is for PicketLink to provide security modules and SAML Token support for ESB services. (http://community.jboss.org/wiki/JBossESBSAMLSupport) The ESB service configures the org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule class to issue (produce) a SAML Security Token. 

The best way to look at this integration is to see it in action. So, we'll "learn by doing" by examining it in a SOA Platform "quickstart."

The Quickstart

I never get tired of saying that one of the great features of the SOA Platform is its extensive, and always growing, set of "quickstart" programs. These programs go far beyond being simple examples as they clearly illustrate various features supported by the Platform. They also serve as a great resource for writing your own applications. For our example, we'll look at the "security_saml" quickstart.

This quickstart illustrates the SOA Platform JBossESB's support for SAML support as provided by PicketLink's Security Token Service (PicketLinkSTS). As the quickstart's readme file states:

  The following (4) procedures will be demonstrated by this quickstart:
  1. Using the PicketLink Project's STSIssuingLoginModule to issue a SAML Assertion from PicketLinkSTS, then
  2. Using the PicketLink Project's STSValidatingLoginModule to validate a SAML Assertion from PicketLinkSTS, then
  3. Injecting the SAML Assertion into a SOAP Message, then
  4. Using SOAPProcessor to invoke an external Web Service that is secured by PicketLinkSTS.
Before we walk through the actions that quickstart performs, let's examine the configuration and binary files that control these procedures. Because the quickstart has to handle the issuing and validation of the
SAML Assertion in addition to actually configuring the quickstart's web service and sending a message to that service, there are a larger number of configuration, execution, and test data related XML files than are used in some other quickstarts.

But, hey, don't panic! We'll examine the files in detail and describe how they are used and also, how they all tie together. Let's divide and conquer and examine these files in the following logical groups:

Group #1 - PicketLinkSTS related files
  • picketlink-sts-client.properties
  • login-config-unfiltered.xml
  • jboss-service.xml
  • jboss-wsse-client.xml
 Group #2 - Test data related files
  • soap-request.xml
  • smooks-saml-injector-template.xml
  • |smooks-saml-injector.xml
Group #3 - ESB archive related files 
  • deployment.xm
  • jbm-queue-service.xml
  • jboss-esb.xml
  • jbossesb-properties.xml
NOTE: We'll hop around a bit between these groups of files, depending on the relationships between the files.

Group #1 - PicketLinkSTS related files

Let's start with the PicketLink's Security Token Service related files.

The picketlink-sts-client.properties file defines the STS configuration for the STSIssuingLoginModule and STSValidatingLoginModule.

1:  serviceName=PicketLinkSTS  
2:  portName=PicketLinkSTSPort  
3:  endpointAddress=http://localhost:8080/picketlink-sts/PicketLinkSTS  
4:  username=admin  
5:  password=admin  

  • Line 1 - The service name. Make a mental note of this as we'll come back to this in a minute.
  • Line 3 - The endpoint of service. This will be active once we deploy the quickstart.
  • Line 4-5 - This username and password are actually only used by the STSValidatingLoginModule.  In contrast, the STSIssuingLoginModule uses callbacks to retrieve the username and password from the authentication request, which is extracted from the SOAP Security header. Where is the SOAP Security header defined? Read on.
Like most of the SOA Platform quickstarts, the security_saml quickstart initiates its actions by generating a message that then is sent over the Platform's JBossESB to selected services. The soap-request.xml file defines the SOAP request that is sent.

1:  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:good="http://security_saml/goodbyeworld" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">  
2:    <soapenv:Header>  
3:      <wsse:Security>  
4:     <wsse:UsernameToken>  
5:      <wsse:Username>admin</wsse:Username>  
6:      <wsse:Password>admin</wsse:Password>  
7:     </wsse:UsernameToken>  
8:    </wsse:Security>  
9:    </soapenv:Header>  
10:    <soapenv:Body>  
11:      <good:sayGoodbye>  
12:       <message>Goodbye!!</message>  
13:      </good:sayGoodbye>  
14:    </soapenv:Body>  
15:   </soapenv:Envelope>  

  • Lines 4-7 - These are the lines that we're interested in for this file. The "UsernameToken" security header information is used by the STSIssuingLoginModule as the username/credential for the user for whom a security token should be issued.
Speaking of the STSIssuingLoginModule, let's shift gears and look at the "quickstart-specific-assemblies" ant target in the build.xml file. This target is called when the quickstart is deployed.

1:   <target name="quickstart-specific-assemblies">  
2:     <!-- Overriden from the target of the same name in base-build.xml. -->  
3:     <!-- Called by the "deploy" target. Don't call directly!! -->  
4:    
5:     <!-- Build the webservice... -->  
6:       <war warfile="build/${ant.project.name}.war"  
7:         webxml="${basedir}/war/resources/WEB-INF/web.xml">  
8:         <fileset dir="${basedir}/war/view">  
9:           <include name="**/*"/>  
10:         </fileset>  
11:       </war>  
12:    
13:       <copy file="${basedir}/login-config-unfiltered.xml" tofile="${basedir}/login-config.xml" filtering="true" overwrite="true">  
14:               <filterset>  
15:                   <filter token="STS_CONFIG_FILE_PATH" value="${basedir}/picketlink-sts-client.properties"/>  
16:               </filterset>  
17:           </copy>  
18:     </target>  

  • Lines 6-11 create the .war file that is used to deploy the quickstart's target web service. We'll come back to this service in a bit.
  • For now, let's look at the element on lines 13-17. What's happening here is that the login-config-unfiltered.xml file is used as a template to create a login-config.xml file that will be used by the quickstart's services. The processing of the element replaces the "STS_CONFIG_FILE_PATH" string in login-config-unfiltered.xml with the value defined in line 44.
What's the point of doing this templating? After all, why not just edit the login-config.xml file directly? It's a safety measure and is typically used with ant to build a project so that you can easily and safely make changes to a project and perhaps several configuration files with one command. This is much safer than trying to remember all the places that you have to edit the same information.

In our case, this results in a login-config.xml file that looks like this:


1:  <policy>  
2:      <application-policy name="saml-issue-token">  
3:      <authentication>  
4:        <login-module code="org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule" flag="required">  
5:          <module-option name="configFile">/jboss/local/502_CR3_signed/jboss-soa-p.5.0.0/jboss-as/samples/quickstarts/security_saml/picketlink-sts-client.properties</module-option>  
6:          <module-option name="endpointURI">http://security_saml/goodbyeworld</module-option>  
7:          <module-option name="password-stacking">true</module-option>  
8:        </login-module>  
9:        <login-module code="org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule" flag="required">  
10:           <module-option name="configFile">/jboss/local/502_CR3_signed/jboss-soa-p.5.0.0/jboss-as/samples/quickstarts/security_saml/picketlink-sts-client.properties</module-option>  
11:           <module-option name="password-stacking">useFirstPass</module-option>  
12:         </login-module>  
13:       </authentication>  
14:     </application-policy>  
15:    
16:     <application-policy name="saml-validate-token">  
17:       <authentication>  
18:         <login-module code="org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule" flag="required">  
19:           <module-option name="configFile">/jboss/local/502_CR3_signed/jboss-soa-p.5.0.0/jboss-as/samples/quickstarts/security_saml/picketlink-sts-client.properties</module-option>  
20:           <module-option name="useOptionsCredentials">true</module-option>  
21:         </login-module>  
22:       </authentication>  
23:     </application-policy>  
24:   </policy>  

What's happening in this file? We're defining security domains. Remember how we talked about the quickstart demonstrating how to either issue (produce) or validate (consume) a SAML Security Token.  Each has an applicaiton policy defined. For JBoss servers, security domains are configured with elements files named "login-config.xml."
  • Line 2 - Here's the start of the domain that is used to issue the SAML Security Token.
  • Line 4 - A reference to the STSIssuingLoginModule that we discussed earlier.
  • Line 5 - And here's a pointer to the picketlink-sts-client.properties file that we discussed a minute ago. (Yes, all these configuration files do tie together.)
  • Line 6 - The endpoint of the target web service is: http://security_saml/goodbyeworld - note that the endpointURI in STS properties is actually just the "domain" to which the security token will be applicable. Imagine you have two systems where access to one does not necessarily imply access to the other, yet they both want to use the same STS. You would look at the endpointURI to verify the token is for the relevant system. The system itself may include many web services that are secured as a collective.
  • Line 7 - Each login module gets a shared map. When the "password-stacking" option is set to "useFirstPass," with this option, the login module looks for username/pass in that shared map instead of using the callback handler.
  • Line 9 - And, here's the start of the domain that is used to validate the SAML Security Token.
  • Line 16 - And here's the start of the domain that is used to validate the SAML Security Token.
  • Line 20 - What's interesting to note here is the use of the "useOptionsCredentials" login module. Unlike "password-stacking," this option causes the login module to use credentials from its properties file. In the case of our quickstart, this is the picketlink-sts-client.properties file. In this context, "true" allows the login module to store the password in the shared map, in anticipation of its later use by another login module through "useFirstPass".
OK, our login credentials are happily defined. What's next? The security domains defined in login-config are static. They are read by the server when the server is started or an application is deployed. Let's look now at the jboss-service.xml file:

1:  <server>  
2:    <mbean code="org.jboss.security.auth.login.DynamicLoginConfig" name="org.jboss.esb.samples.quickstarts.security_saml:service=LoginModule">  
3:      <attribute name="AuthConfig">login-config.xml</attribute>  
4:      <depends optional-attribute-name="LoginConfigService">jboss.security:service=XMLLoginConfig</depends>  
5:      <depends optional-attribute-name="SecurityManagerService">jboss.security:service=JaasSecurityManager</depends>  
6:    </mbean>  
7:  </server>  
8:    

  • Line 2 - As its name indicates, org.jboss.security.auth.login.DynamicLoginConfig enables us to dynamically deploy a security domain. In the case of our quickstart, the MBean makes use of the XMLLoginConfig service (line 4) and the SecurityManagerService (line 5).
And the last of the security-related is the jboss-wsse-client.xml file. This file defines the JBoss WebService configuration that the quickstart client will use to access the web service. The configuration defined in this file will cover all requests sent by the client, and all responses received by the client. Note that this has really nothing to do with SAML/STS. The quickstart has configured the ESB service to support WS-Security UsernameToken, so that we can then use this user/password to authenticate against STS and request a token.

1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <jboss-ws-security xmlns="http://www.jboss.com/ws-security/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:   xsi:schemaLocation="http://www.jboss.com/ws-security/config http://www.jboss.com/ws-security/schema/jboss-ws-security_1_0.xsd">  
4:   <config>  
5:     <username/>  
6:   </config>  
7:  </jboss-ws-security>  

  • Lines 4-6 - This config element indicates that all requests and responses must contain a username.
For more information on Web Service Security, see: http://docs.sun.com/source/819-0788/xwss2.html, http://community.jboss.org/wiki/WSSecurityConfig, http://community.jboss.org/wiki/jbossws-ws-securityoptions

Group #2 - Test data related files

We discussed the soap-request.xml a few minutes ago. Let's look at the smooks-saml-injector-template.xml and smooks-saml-injector.xml files now. These files are used to transform a message, to inject the SAML assertion into a SOAP message that is sent using WS Security.

As its name indicates, the smooks-saml-injector-template.xml file is a temlplate and is used by the smooks-saml-injector.xml file. Let's look at smooks-saml-injector.xml

1:   <?xml version="1.0"?>  
2:   <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">  
3:    
4:     <import file="smooks-saml-injector-template.xml">  
5:     <param name="selector">/Envelope/Header/Security</param>  
6:     <param name="selectorNamespace">http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd</param>  
7:     <param name="securityNamespace">http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd</param>  
8:     <param name="headerNamespace">http://schemas.xmlsoap.org/soap/envelope/</param>  
9:   </import>  
10:      
11:    <import file="smooks-saml-injector-template.xml">  
12:      <param name="selector">/Envelope/Header</param>  
13:      <param name="selectorNamespace">http://schemas.xmlsoap.org/soap/envelope/</param>  
14:      <param name="securityNamespace">http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd</param>  
15:      <param name="headerNamespace">http://schemas.xmlsoap.org/soap/envelope/</param>  
16:      <param name="addXmlNs">false</param>  
17:    </import>  
18:     
19:   </smooks-resource-list>  

What we see in this file are two separate elements. Each defines the selector, security and header namespaces that are used to define the security domain. But, what's the reason behind the second ? And why does it refer to http://schemas.xmlsoap.org/soap/envelope instead of http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd? The reason is that sometimes there is no need to inject the namespaces as the wsse:security may be qualified already. That's the reason for the second block.

Before we look at group #3 (the ESB related files), let's take a look at the web service and the web service client used by the quickstart .

The web service itself is pretty basic - it's defined in GoodbyeWorldWS.java


1:   package org.jboss.soa.esb.samples.quickstart.securitysaml.webservice;  
2:    
3:   import javax.jws.WebService;  
4:   import javax.jws.WebMethod;  
5:   import javax.jws.Oneway;  
6:   import javax.jws.WebParam;  
7:   import javax.jws.HandlerChain;  
8:   import javax.jws.soap.SOAPBinding;  
9:    
10:   import org.jboss.soa.esb.message.Message;  
11:   import org.jboss.soa.esb.message.Body;  
12:   import org.jboss.soa.esb.actions.ActionUtils;  
13:   import org.jboss.soa.esb.actions.soap.SOAPProcessor;  
14:    
15:   @HandlerChain(file="handlerchain.xml")  
16:   @WebService(name = "GoodbyeWorldWS", targetNamespace="http://security_saml/goodbyeworld")  
17:   public class GoodbyeWorldWS {  
18:    
19:     @WebMethod  
20:     public String sayGoodbye(@WebParam(name="message") String message) {  
21:       Message esbMessage = SOAPProcessor.getMessage();  
22:       System.out.println("Succesfully invoked WS!");  
23:       System.out.println("SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());  
24:       return "... Ah Goodbye then!!!! - " + message;  
25:     }  
26:    
27:     @WebMethod  
28:     public String sayAdios(String message) {  
29:       Message esbMessage = SOAPProcessor.getMessage();  
30:       if(esbMessage != null) {  
31:         System.out.println("**** SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());  
32:       }  
33:       System.out.println("Web Service Parameter - message=" + message);  
34:       return "... Adios Amigo!!!! - " + message;  
35:     }  
36:       
37:     @WebMethod  
38:     @Oneway  
39:     public void sayGoodbyeWithoutResponse(@WebParam(name="message") String message) {  
40:    
41:       Message esbMessage = SOAPProcessor.getMessage();  
42:       if(esbMessage != null) {  
43:         System.out.println("**** SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());  
44:       }  
45:       System.out.println("Web Service Parameter - message=" + message);  
46:     }  
47:       
48:   }  

If this program looks more like a POJO (a "plain old java object") with annotations than a web service, well, you're correct!

One of the features of the JBossESB in the SOA Platform is how it enables you to easily expose a web service's endpoints on the ESB. To do this, you write a "service wrapper webservice" that conforms to the JSR 181 ("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
When we examine the jboss-esb.xml file, we'll see that the JBossESB's SOAPProcessor (org.jboss.soa.esb.actions.soap.SOAPProcessor) is used to expose the web service's -endpoints.

But wait, there's more!

On line 15, we see this annotation:  @HandlerChain(file="handlerchain.xml")

A handler is a message interceptor to do processing of incoming and outgoing messages. SOAP handlers provide methods to access SOAP messages.
A HandlerChain is a list of handlers. The handlerchain referenced on line 15 is defined as follows in the handlerchain.xml file.

1:   <?xml version="1.0" encoding="UTF-8"?>  
2:   <jws:handler-config xmlns:jws="http://java.sun.com/xml/ns/javaee">  
3:       <jws:handler-chains>  
4:           <jws:handler-chain>  
5:               <jws:handler>  
6:                   <jws:handler-class>org.picketlink.identity.federation.core.wstrust.handlers.STSSaml20Handler</jws:handler-class>  
7:               </jws:handler>  
8:           </jws:handler-chain>  
9:       </jws:handler-chains>  
10:   </jws:handler-config>  

  • Line 6 - What's interesting in this file is line 6. The org.picketlink.identity.federation.core.wstrust.handlers.STSSaml20Handler handler class enables the quickstart to insert the SAML security assertion into the message as it is processed by the web service.
OK. That's the web service. Now let's look at the quickstart's client code in HttpClient.java

1:  package org.jboss.soa.esb.samples.quickstart.securitysaml.test;  
2:    
3:  import org.jboss.remoting.Client;  
4:  import org.jboss.remoting.InvokerLocator;  
5:  import org.jboss.internal.soa.esb.util.StreamUtils;  
6:  import org.jboss.remoting.transport.http.HTTPMetadataConstants;  
7:    
8:  import java.util.HashMap;  
9:  import java.util.Map;  
10:    
11:  public class HttpClient  
12:  {  
13:    // Default locator values  
14:    private static String transport = "http";  
15:    private static String host = "localhost";  
16:    private static int port = 5400;  
17:    private static String payload;  
18:    
19:    public void makeInvocation(String locatorURI) throws Throwable  
20:    {  
21:     InvokerLocator locator = new InvokerLocator(locatorURI);  
22:     System.out.println("Calling remoting server with locator uri of: " + locatorURI);  
23:    
24:     Client remotingClient = new Client(locator);  
25:     remotingClient.connect();  
26:    
27:     Map metadata = new HashMap();  
28:     metadata.put("TYPE", "POST");  
29:     remotingClient.invokeOneway(payload, metadata );  
30:    
31:     System.out.println("Sent http post to server.");  
32:     Integer responseCode = (Integer) metadata.get(HTTPMetadataConstants.RESPONSE_CODE);  
33:     String responseMessage = (String) metadata.get(HTTPMetadataConstants.RESPONSE_CODE_MESSAGE);  
34:     System.out.println("Response code from server: " + responseCode);  
35:     System.out.println("Response message from server: " + responseMessage);  
36:     remotingClient.disconnect();  
37:    
38:    }  
39:    
40:    public static void main(String[] args) throws Exception  
41:    {  
42:     if(args != null && args.length == 4)  
43:     {  
44:       transport = args[0];  
45:       host = args[1];  
46:       port = Integer.parseInt(args[2]);  
47:           payload = StreamUtils.getResourceAsString(args[3], "UTF-8");  
48:     }  
49:     String locatorURI = transport + "://" + host + ":" + port;  
50:     HttpClient client = new HttpClient();  
51:     try  
52:     {  
53:       client.makeInvocation(locatorURI);  
54:     }  
55:     catch(Throwable e)  
56:     {  
57:       e.printStackTrace();  
58:     }  
59:    }  
60:  }  

There's nothing unusual here as the client simply sends an HTTP POST request to a target URL. How does this client get executed? Through this target defined in the build.xml file.

1:  <target name="runtest" depends="compile"  
2:   description="sends a HTTP request to the JBossRemoting gateway">  
3:    <echo>Http Client</echo>  
4:   <java fork="yes" classname="org.jboss.soa.esb.samples.quickstart.securitysaml.test.HttpClient" failonerror="true">  
5:     <arg value="http"/>  
6:     <arg value="localhost"/>  
7:     <arg value="9888"/>  
8:     <arg value="soap-request.xml"/>  
9:     <classpath refid="exec-classpath"/>  
10:   </java>  
11:  </target>  
12:    

  • Line 7 - OK. There's nothing unusual here either. Or, is there? Look at line #7. Where did port 9888 get defined? After all, the the server is running on port 8080. We'll find the answer when we examine the quickstart's jboss-esb.xml file next.
(And, last of all, the DisplaySubjectAction.java class is a customer ESB action called by services to display the javax.security.auth.Subject extracted from the message.)

Group #3 - ESB archive related files

The deployment.xml, jbm-queue-service.xml, and jbmq-queue-service.xml files all reference the JMS queues and topics that will be needed by the quickstart. The jbossesb-properties.xml file is used to define properties used by the quickstart and to over-ride server-wide settings.

The file that we want to look at in detail is jboss-esb.xml. This file defines the quickstart's services configuration, including listeners and actions, and the providers.

(Note that while we'll be examining most of the contents of the jboss-esb.xml file in detail, we'll be doing it in pieces or fragments so that it's easier to follow. The line numbers in each of these fragments will therefore be different from the actual (whole) file.)

Let's start with the providers. As the name indicates, these elements define resources provided by by the ESB to the quickstart.

1:  <providers>  
2:     <jms-provider name="JBossMessaging" connection-factory="ConnectionFactory">  
3:       <jms-bus busid="quickstartGatewayChannel">  
4:         <jms-message-filter dest-type="QUEUE" dest-name="queue/quickstart_security_saml_gw"/>  
5:      </jms-bus>  
6:      <jms-bus busid="quickstartEsbChannel">  
7:        <jms-message-filter dest-type="QUEUE" dest-name="queue/quickstart_security_saml_esb"/>  
8:      </jms-bus>  
9:    </jms-provider>  
10:      
11:     <jbr-provider name="JBR-Http" protocol="http" host="localhost">  
12:          <jbr-bus busid="Http-1" port="9888" />  
13:     </jbr-provider>  
14:      
15:  </providers>  

  • Line 2 - Here's the start of the JMS provider. This is where we will see the definitions of JMS queues and topics.
  • Line 3 - Note that each queue has a "busid" value defined for it. We'll see the busid's used in the service definitions in a moment.
  • Line 7 - Remember how we discussed how each ESB-unaware queue required a corresponding ESB-aware queue to be defined?
  • Line 11 - This is the definition for a JBoss remoting provider. This makes use of JBoss Remoting (JBR) into JBoss ESB as a transport option and supports HTTP (and SSL, which we're not using in this quickstart). What's interesting here is that the jbr-provider enables us to define the port (remember how we saw the reference to port 9888 in the "runtest" ant target in the build.xml file?) that the provider provides (pardon the pun) to a service.
The remainder of the jboss-esb.xml file defines the quickstart's (3) services. We'll look at the configuration of the services now, and then we'll look at the actual operation of the services when we walk through what happens when we run the quickstart.


1:  <service category="SamlSecurityQuickstart" name="issueTokenService"  
2:      invmScope="GLOBAL"  
3:      description="This service demonstrates how a service can be configured to issue and validate a security token">  
4:      
5:      <security moduleName="saml-issue-token" callbackHandler="org.jboss.soa.esb.services.security.auth.login.JBossSTSIssueCallbackHandler">  
6:        <!-- disable the security context timeout so that our security context is re-evaluated -->  
7:        <property name="org.jboss.soa.esb.services.security.contextTimeout" value="0"/>  
8:      </security>  
9:      
10:     <listeners>  
11:       <jms-listener name="JMSGatewayListener" busidref="quickstartGatewayChannel" is-gateway="true"/>  
12:         <jbr-listener name="Http-Gateway" busidref="Http-1" is-gateway="true">  
13:           <property name="synchronous" value="false"/>  
14:         </jbr-listener>  
15:    </listeners>  
16:     
17:   <actions mep="OneWay">  
18:      
19:      <!-- Uncomment if you'd like to print the current Subject  
20:        <action name="printSubject" class="org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction"/>  
21:        -->  
22:       
23:      <action name="routeAction" class="org.jboss.soa.esb.actions.StaticRouter">  
24:        <property name="destinations">  
25:          <route-to service-category="SamlSecurityQuickstart" service-name="securedSamlService"/>  
26:        </property>  
27:      </action>  
28:      
29:    </actions>  
30:  </service>  

  • Line 1: Service name and category are used to differentiate the services. Every service has a category and name attribute. JBossESB uses these attributes to register a service's listeners (gateway listeners or ESB-aware listeners) endpoints in a service registry.
  • Line 2 - The InVM transport enables services to communicate using the same JVM and without having to go through a second non-gateway provider. This makes things more efficient as the services don't have use any networking protocols between JVM instances. The "GLOBAL" value indicates that the this service can be invoked using the InVM transport using the same JVM.
  • Line 5 - Remember how we defined the appplication-policy name in login-config.xml?  (Hint: The policy name was "saml-issue-token.") That's right. This is the moduleName. The callbackHandler defined here overrides the one defined in jbossesb-properties.xml. Remember that the username and password can be provided by a callback. In the case of this service the callback is JBossSTSIssueCallbackHandler.  The callback handler retreives the username and password of the user for whom a security token should be issued.
  • Line 7 - Setting the org.jboss.soa.esb.services.security.contextTimeout property to zero ensures that every time the service is invoked the security context is re-evaluated.
  • Line 10 - Here's where the definition of the services listeners begins. These are well-named as they "listen" for incoming messages, based on the designated queue and protocol.
  • Line 12 - Remember how we reviewed the "Http-1" busidref in the "providers" section of the jboss-esb file? Well, here's where it's used. The JMS queue associated with this busidref is "listened to" for incoming messages. When a message is received, the service's action pipeline (in this case a pipeline of only one action) is executed. The reference to "gateway" indicates that the message originates outside of the ESB and is passed into the ESD through this "gateway."
  • Line 17 - The service's actions' definitions start here. The message exchange pattern (or "mep" for short) is asynchronous as the service will not wait for a response before moving on.
  • Line 20 - We'll uncomment this line later on so we can see the security subject displayed in the server log.
  • Line 25 - What happens to the message after the service issues the security assert? It's routed to the next service defined in the jboss-esb.xml file. In this case it's a static route, but the ESB actually supports other routing options too, including content based routing. (http://jboss-soa-p.blogspot.com/2009/07/when-content-knows-way-content-based.html) 
Next, let's look at the service to which the first service routes messages.

1:  <service category="SamlSecurityQuickstart" name="securedSamlService"  
2:    invmScope="GLOBAL"  
3:    description="This service demonstrates that an ESB service can be configured to only validate a security token.">  
4:      
5:    <security moduleName="saml-validate-token" callbackHandler="org.jboss.soa.esb.services.security.auth.login.JBossSTSTokenCallbackHandler">  
6:        <!-- disable the security context timeout so that our security context is re-evaluated -->  
7:        <property name="org.jboss.soa.esb.services.security.contextTimeout" value="0"/>  
8:    </security>  
9:      
10:    <actions mep="OneWay">  
11:      
12:      <!-- Uncomment if you'd like to print the current Subject  
13:         <action name="printSubject" class="org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction"/>  
14:      -->  
15:      
16:      <action name="routeAction" class="org.jboss.soa.esb.actions.StaticRouter">  
17:        <property name="destinations">  
18:          <route-to service-category="SamlSecurityQuickstart" service-name="sendExternal"/>  
19:        </property>  
20:     </action>  
21:      
22:    </actions>  
23:  </service>  

There are two interesting lines in this service definition.
  • Line 5 - In contrast to the first service, this service uses the org.jboss.soa.esb.services.security.auth.login.JBossSTSTokenCallbackHandler class as a call back handler. 
  • Line 18 - Routes the message to the next service.
1:  <service category="SamlSecurityQuickstart" name="sendExternal"  
2:    invmScope="GLOBAL"  
3:    description="This service demonstrates that an ESB service can be configured to only validate a security token and call an external WS with the security token.">  
4:      
5:    <security moduleName="saml-validate-token" callbackHandler="org.jboss.soa.esb.services.security.auth.login.JBossSTSTokenCallbackHandler"/>  
6:      
7:    <actions mep="OneWay">  
8:      
9:      <!-- Uncomment if you'd like to print the current Subject  
10:       <action name="printSubject" class="org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction"/>  
11:     -->  
12:      
13:     <action name="addSamlSecurityHeader" class="org.jboss.soa.esb.smooks.SmooksAction">  
14:       <property name="smooksConfig" value="/smooks/smooks-saml-injector.xml" />  
15:     </action>  
16:      
17:     <action name="JBossWSAdapter" class="org.jboss.soa.esb.actions.soap.SOAPProcessor">  
18:       <property name="jbossws-endpoint" value="GoodbyeWorldWS"/>  
19:     </action>  
20:      
21:   </actions>  
22:  </service>  

What's different from the second service in this service is:
  • Lines 13-15 - This action invokes the Smooks script to inject the SAML assert into the message.
  • Lines 17-18 - And finally, the target web service is invoked via the org.jboss.soa.esb.actions.soap.SOAPProcessor out of the box JBossESB action.
Running the Quickstart

OK - let's run the quickstart and watch it in action. Running the quickstarts is a deceptively simple task in that they generally requires only a small number of steps to execute. In the case of the security_saml quickstart, we only have to execute these (2) ant commands:

1:  ant deploy  
2:  ant runtest  

But - hang on  - let's examine in detail just what each command actually does.

ant deploy

Many of the quickstarts require some "set up" work in order to run. In the case of the security_saml quickstart this set up involves deploying not only the quickstart, but also the PicketLink Security Token Server (STS) .war file that is included in the quickstart. Here's the deploy target in the quickstart's build.xml file.

1:  <target name="quickstart-specific-predeploys">  
2:    <copy file="${basedir}/picketlink-sts.war" todir="${org.jboss.esb.server.deploy.dir}"/>  
3:  </target>  

Here's the server log when the quickstart is deployed.

1:  23:04:13,512 INFO [TomcatDeployment] undeploy, ctxPath=/Quickstart_Security_Saml  
2:  23:04:13,547 INFO [EsbDeployment] Stopping 'Quickstart_Security_Saml.esb'  
3:  23:04:13,837 INFO [Http11Protocol] Stopping Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
4:  23:04:13,844 INFO [JBossRemotingGatewayListener] JBoss Remoting Gateway listener 'listener' stopped.  
5:  23:04:14,075 INFO [EsbDeployment] Destroying 'Quickstart_Security_Saml.esb'  
6:  23:04:14,075 WARN [ServiceMessageCounterLifecycleResource] Calling cleanup on existing service message counters for identity ID-6  
7:  23:04:14,075 WARN [SmooksResource] Closing smooks resources for identity ID-6  
8:  23:04:14,213 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb_reply] stopped  
9:  23:04:14,215 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb] stopped  
10:  23:04:14,216 INFO [QueueService] Queue[/queue/quickstart_security_saml_gw] stopped  
11:  23:04:14,487 INFO [QueueService] Queue[/queue/quickstart_security_saml_gw] started, fullSize=200000, pageSize=2000, downCacheSize=2000  
12:  23:04:14,518 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb] started, fullSize=200000, pageSize=2000, downCacheSize=2000  
13:  23:04:14,550 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb_reply] started, fullSize=200000, pageSize=2000, downCacheSize=2000  
14:  23:04:14,571 INFO [EsbDeployment] Starting ESB Deployment 'Quickstart_Security_Saml.esb'  
15:  23:04:15,870 INFO [CoyoteInvoker] Using org.apache.coyote.http11.Http11Protocol for http (coyote) invoker protocol handler.  
16:  23:04:15,871 INFO [Http11Protocol] Initializing Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
17:  23:04:15,871 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
18:  23:04:15,871 INFO [JBossRemotingGatewayListener] JBoss Remoting Gateway listener 'listener' started.  
19:  23:04:16,090 INFO [TomcatDeployment] deploy, ctxPath=/Quickstart_Security_Saml  

Remember the JBoss Remoting Gateway that we configured to run on port 9888? Here it is:

1:  23:04:15,871 INFO [Http11Protocol] Initializing Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
2:  23:04:15,871 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
3:  23:04:15,871 INFO [JBossRemotingGatewayListener] JBoss Remoting Gateway listener 'listener' started.  

OK, let's run the quickstart and watch it in action!

1:  runtest:  
2:     [echo] Http Client  
3:     [java] Calling remoting server with locator uri of: http://localhost:9888  
4:     [java] Sent http post to server.  
5:     [java] Response code from server: 200  
6:     [java] Response message from server: OK  

Well - that's a bit anti-climatic after all this setup work. Let's and take a more in-depth look at what just happened.

The quickstart begins by sending a message to the JBR-remoting gateway that we defined on port 9888. We can see this in the build.xml file:

1:  <target name="runtest" depends="compile"  
2:    description="sends a HTTP request to the JBossRemoting gateway">  
3:    <echo>Http Client</echo>  
4:    <java fork="yes" classname="org.jboss.soa.esb.samples.quickstart.securitysaml.test.HttpClient" failonerror="true">  
5:       <arg value="http"/>  
6:       <arg value="localhost"/>  
7:       <arg value="9888"/>  
8:       <arg value="soap-request.xml"/>  
9:       <classpath refid="exec-classpath"/>  
10:    </java>  
11:  </target>  

At this point, we'll un-comment the lines in the jboss-esb.xml file that invoke the org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction - and we'll restart the server with logging set to the DEBUG level. If you spend your time developing or testing software, then the ability to "up" the logging level provides you with a goal mine of potential debugging information. Note that for the purposes of this essay, we'll look at selected log messages as logging at the DEBUG level can be very(!) verbose.

The first service defined in the jboss-esb.xml ("issueTokenService") file is located:

1:  2010-09-07 20:46:43,801 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Going to query the registry for name pattern [issueTokenService]  
2:  2010-09-07 20:46:43,898 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Service name: issueTokenService  
3:  2010-09-07 20:46:43,898 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Description: This service demonstrates how a service can be configured  
4:   to issue and validate a security token  

The incoming message is received by the JBR gateway:

1:  2010-09-07 20:46:43,898 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Key id: uddi:juddi.apache.org:1e71f7ae-e72d-4a38-a39c-cad7c1b1d250  
2:  2010-09-07 20:46:43,900 DEBUG [org.jboss.soa.esb.client.ServiceInvoker] (JBossRemoting Server Oneway(3)-1) EPR=InVMEpr [ PortReference < <wsa:Address  
3:  invm://53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365/false?false#10000/>, <wsa:ReferenceProperties jbossesb:passByValue :  
4:  false/>, <wsa:ReferenceProperties jbossesb:type : urn:jboss/esb/epr/type/invm/> > ] XML=<wsa:From xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"><wsa:Address>invm:  
5:  //53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365/false?false#10000</wsa:Address><wsa:ReferenceProperties>  
6:  <jbossesb:passByValue xmlns:jbossesb="http://schemas.jboss.com/ws/2007/01/jbossesb">false</jbossesb:passByValue><jbossesb:type xmlns:jbossesb="http://schemas.jboss.com/ws/2007  
7:  /01/jbossesb">urn:jboss/esb/epr/type/invm</jbossesb:type></wsa:ReferenceProperties></wsa:From>  
8:  2010-09-07 20:46:43,907 DEBUG [org.jboss.soa.esb.client.ServiceInvoker] (JBossRemoting Server Oneway(3)-1) EPR=EPR: PortReference < <wsa:Address http://localhost:9888/> > XML=<wsa:From  
9:  xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"><wsa:Address>http://localhost:9888</wsa:Address></wsa:From>  
10:  2010-09-07 20:46:43,924 DEBUG [org.jboss.soa.esb.filter.FilterManager] (JBossRemoting Server Oneway(3)-1) FilterManager calling org.jboss.internal.soa.esb.message.filter.MetaDataFilter@8e85e1.onOutput  
11:  2010-09-07 20:46:43,924 DEBUG [org.jboss.soa.esb.filter.FilterManager] (JBossRemoting Server Oneway(3)-1) FilterManager calling org.jboss.internal.soa.esb.message.filter.GatewayFilter@f77a23.onOutput  

And the message is passed to the ESB-aware queue:

1:  2010-09-07 20:46:43,924 DEBUG [org.jboss.internal.soa.esb.couriers.transport.InVMTransport] (JBossRemoting Server Oneway(3)-1) Delivering message to 53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365  
2:  2010-09-07 20:46:43,924 DEBUG [org.jboss.internal.soa.esb.couriers.transport.InVMTransport] (JBossRemoting Server Oneway(3)-1) Delivering message direct to 53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365 queue  
3:  2010-09-07 20:46:43,926 DEBUG [org.jboss.internal.soa.esb.couriers.transport.InVMTransport] (Thread-29) Pickup of message from 53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365  

The assert is created:

1:  2010-09-07 20:46:46,078 DEBUG [org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule] (pool-24-thread-1) Added Credential :SamlCredential[<Assertion  
2:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#" ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
3:  IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID  
4:   NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"  
5:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z" NotOnOrAfter="2010-09-08T02:46:45.294Z"  
6:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml  
7:  /goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:SignatureMethod  
9:  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
10:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-  
11:  signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:dsig="http://www.w3.org/2000/09  
12:  /xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue  
13:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:   xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
20:  </dsig:Signature></Assertion>]  
21:  2010-09-07 20:46:46,081 DEBUG [org.jboss.internal.soa.esb.services.security.PrivateCryptoUtil] (pool-24-thread-1) SealAlgorithm 'TripleDES' keySize '168'  
22:  2010-09-07 20:46:46,173 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-24-thread-1) executing processor 0  
23:  org.jboss.soa.esb.listeners.message.OverriddenActionLifecycleProcessor@1ae20eb  

The issueTokenService displays the subject assert:

1:  2010-09-07 20:46:46,175 INFO [STDOUT] (pool-24-thread-1) Subject in MyListenerAction : Subject:  
2:    Public Credential: SamlCredential[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"  
3:  ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185" IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation  
5:  Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z"  
6:  NotOnOrAfter="2010-09-08T02:46:45.294Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml/goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org  
9:  /2000/09/xmldsig#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-  
10:  8e32-b0bd3cd80185" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09  
11:  /xmldsig#enveloped-signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference>  
13:  </dsig:SignedInfo><dsig:SignatureValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
20:  </dsig:Signature></Assertion>]  
21:  2010-09-07 20:46:46,178 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-24-thread-1) executing processor 1 org.jboss.soa.esb.actions.StaticRouter@826935  

The message is routed to the 2nd service "securedSamlService"):

1:  2010-09-07 20:46:46,179 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (pool-24-thread-1) Going to query the registry for name pattern [securedSamlService]  

Where the assert is validated:

1:  2010-09-07 20:46:46,791 DEBUG [org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule] (pool-25-thread-1) Validation result: true  

And the subject is displayed again:

1:  2010-09-07 20:46:46,833 INFO [STDOUT] (pool-25-thread-1) Subject in MyListenerAction : Subject:  
2:    Public Credential: SamlCredential[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"  
3:   ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185" IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation  
5:  Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z"  
6:  NotOnOrAfter="2010-09-08T02:46:45.294Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml/goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org  
9:  /2000/09/xmldsig#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-  
10:  8e32-b0bd3cd80185" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09  
11:  /xmldsig#enveloped-signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference>  
13:  </dsig:SignedInfo><dsig:SignatureValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
20:  </dsig:Signature></Assertion>]  

And the message is routed to the sendExternal service:

1:  2010-09-07 20:46:46,833 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-25-thread-1) executing processor 1 org.jboss.soa.esb.actions.StaticRouter@1596c3a  
2:  2010-09-07 20:46:46,834 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (pool-25-thread-1) Going to query the registry for name pattern [sendExternal]  
3:  2010-09-07 20:46:47,391 DEBUG [org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule] (pool-26-thread-1) Added Credential :SamlCredential[<Assertion  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#" ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
5:  IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID  
6:   NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z" NotOnOrAfter="2010-09-08T02:46:45.294Z"  
8:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml  
9:  /goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
10:  <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:SignatureMethod  
11:  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-  
13:  signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:dsig="http://www.w3.org/2000/09  
14:  /xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue  
15:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
16:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
17:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
18:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
19:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
20:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
21:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
22:  </dsig:Signature></Assertion>]  
23:  2010-09-07 20:46:47,392 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-26-thread-1) executing processor 0  
24:  org.jboss.soa.esb.listeners.message.OverriddenActionLifecycleProcessor@1ac4773  

The subject is displayed a final time:

1:  2010-09-07 20:46:47,392 INFO [STDOUT] (pool-26-thread-1) Subject in MyListenerAction : Subject:  
2:    Public Credential: SamlCredential[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"  
3:  ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185" IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation  
5:  Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z"  
6:  NotOnOrAfter="2010-09-08T02:46:45.294Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml/goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org  
9:  /2000/09/xmldsig#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-  
10:  8e32-b0bd3cd80185" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09  
11:  /xmldsig#enveloped-signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference>  
13:  </dsig:SignedInfo><dsig:SignatureValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:   xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo></dsig:Signature></Assertion>]  

Smooks inserts the assert into the message:

1:  2010-09-07 20:46:47,916 DEBUG [org.milyn.util.ClassUtil] (pool-26-thread-1) Adding org.milyn.javabean.decoders.BigDecimalDecoder to list of classes  
2:  2010-09-07 20:46:47,917 DEBUG [org.milyn.util.ClassUtil] (pool-26-thread-1) Adding org.milyn.javabean.decoders.BigIntegerDecoder to list of classes  
3:  2010-09-07 20:46:47,918 DEBUG [org.milyn.util.ClassUtil] (pool-26-thread-1) Adding org.milyn.javabean.decoders.BooleanDecoder to list of classes  
4:  (lots more lines like this)  
5:    

And the web service is accessed:

1:  2010-09-07 20:46:48,415 DEBUG [org.jboss.ws.core.EndpointInvocation] (pool-26-thread-1) transformPayloadValue: org.jboss.ws.core.soap.SOAPBodyElementDoc -> org.jboss.soa.esb.samples.quickstart.securitysaml.webservice.jaxws.SayGoodbye  
2:    </soapenv:Header>  
3:    <soapenv:Body>  
4:     <good:sayGoodbye>  
5:       <message>Goodbye!!</message>  
6:     </good:sayGoodbye>  
7:    </soapenv:Body>  
8:  </soapenv:Envelope>  

Closing Thoughts

OK, let's review what happened.

The quickstart demonstrate these (4) procedures:
  1. Using the PicketLink Project's STSIssuingLoginModule to issue a SAML Assertion from PicketLinkSTS, then
  2. Using the PicketLink Project's STSValidatingLoginModule to validate a SAML Assertion from PicketLinkSTS, then
  3. Injecting the SAML Assertion into a SOAP Message, then
  4. Using SOAPProcessor to invoke an external Web Service that is secured by PicketLinkSTS.
And, notice what the quickstart did not have to do - it did not have to include a large amount of custom-written code to issue or validate the SAML Assertion. PicketLinkSTS and the JBossESB in the SOA Platform did the heavy lifting.

Acknowledgements

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

References