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

Tuesday, August 17, 2010

Reposted in DZone

The post describing the JBossESB / Drools integration in the SOA Platform was just posted up on DZone:

http://java.dzone.com/articles/jbossesb-drools-integration

Thursday, July 8, 2010

The JBossESB-Drools Integration in the JBoss SOA Platform

As I've mentioned in previous posts and articles, one of the great strengths of the JBoss SOA Platform is the large number of integrations that it supports. Some of these integrations take the form of support for JBoss and third-party supplied JMS and UDDI providers or support for multiple JDKs and databases. The SOA Platform also supports an integration to jBPM for business process orchestration, and JBDS for application development.

In this post, I want to take a look at the SOA Platform's integration with JBoss Rules. In an earlier blog post (http://jboss-soa-p.blogspot.com/2009/07/when-content-knows-way-content-based.html), I discussed using Rules to implement content based routing over the JBossESB. In this post, we'll look at another aspect of the JBossESB-Rules integration; the creation of rules-based services.

Before we examine this integration in detail, let's take a quick look at JBoss Rules.

Drools and Rules


JBoss Rules as packaged in the SOA Platform is the commercialized version of the open source "Drools" project. (You can learn more about Drools at the project web site here: http://www.jboss.org/drools/) Drools is a unified and integrated solution for Business Rules, Business Processes Management, and Event Processing. The Drools project is organized into a number of sub-projects. When we refer to JBoss Rules in the SOA Platform, what we're primarily talking about is the Drools "Expert" sub-project. This sub-project consists of the Rules API, the Rules engine, and Rules editing and debugging tools.

Rules-based programming, as its name implies, is built on the ability to define decision points and keep them separate from other program logic. OK, that sounds interesting, but why would I want to use this? In other words, what's the big deal? Here are two reasons why this is important:
  • First, it enables you to separate your application's business logic and decision point handling from the application code. This means that your business process specialists can concentrate on the business logic rules and your programmers can concentrate on the application code. This makes application development and maintenance easier and more effective.
  • Second, and don't take this as a personal criticism of your programming skills or mine, but the since the rules engine is designed and optimized to process rules, it is more efficient than any massive if-then-else statement that you can write. So, your application's performance can be improved. The JBoss Rules engine makes use of the Rete algorithm http://en.wikipedia.org/wiki/Rete_algorithm for efficient Rules processing.
When you define a rule, the model that you follow is not "if-then-else," but rather "when" and "then." The two constructs that you use in a rule are:
  • The Condition - This is the left hand side of the rule and covers the "when" aspects of the rule.
  • The Consequence - This is the right hand side of the rule and covers the "then" aspects of the rule.
Rules are written in the Drools Rule Language (drl). This language can be extended into a Domain Specific Language (dsl) to support application-specific requirements such as medical or financial procedures and terminology.

The general template for a rule is:
1:  rule “a simple rule”
2: when (LHS)
3: you need a rules-based app
4: then (RHS)
5: build it with JBoss Rules
How does a rule access information? Through Rules' working memory. The information that rules perform operations take the form of Java beans that are referred to as "facts." (The elements in these facts are accessed through the getter and setter methods.) What happens is that facts are inserted into working memory, updated in working memory, or removed from working memory, those changes to the facts in working memory can cause the Rules' "when" conditions to be true and the rules to be executed.

It's important to note that unlike procedural programming, changes to facts can cause more than one rule to reach true conclusions and be available to be executed at once. What happens then? Well, instead of hardcoding a sequence of rules, the rules engine adds each matching rule to its "Agenda" of rules to be executed. If the Agenda includes more than one rule, the rule engine performs conflict resolution on the rules and determines the sequence in which the rules should be executed. This conflict resolution is based on the rules' salience (you define this as a property when you write the rules), how often the rule has fired in the past, complexity (the more complex a rule the more likely the rule engine will consider it to apply to the current situation), and the order in which the rules are loaded.

Invoking Rules from a JBossESB Service in the SOA Platform

The Rules - JBossESB integration in the SOA Platform enables you to access rules from an service's actions. This is supported by the org.jboss.soa.esb.actions.BusinessRuleProcessor and the org.jboss.soa.esb.actions.DroolsRuleService action classes.

The BusinessRuleProcessor class uses rules loaded from rules files. Generally, you use this class for simple rules services as loading large numbers of rules from large numbers of rules files is difficult to manage and not efficient.

For production environments, where you will have complicated rules services that deal with hundreds or even thousands of rules, it's better to use the DroolsRuleService. This service uses the RuleAgent to either access packages of rules from files, or from a Business Rules Management System (BRMS) .

The JBoss BRMS Platform (http://www.jboss.com/products/platforms/brms/) combines a central repository for rules, with a web based rules authoring interface, and rules management that provides import/export/archiving, audit trail or edits, error log, automated test development and execution, rules analysis, status tracking, and version control. The BRMS Platform rules authoring "guided editor" enables non-programmers to more easily create rules while the Platform makes it easy for rules administrartors can maintain large numbers of rules and control their development and use by users. Here's a screenshot:

The SOA Platform supports rules services that are either stateless or stateful. In the stateless model, messages sent to the services contain all the facts to be inserted into the rules engine working memory, before the rules are executed. In the stateful model, where the execution of the rules may take place in a session over an extended time period, several messages may be sent to a rule service, and the rules may fire and update either the message or the facts until a final message causes the service to end the session. The best way to explain and illustrate the Rules - JBossESB integration in the SOA Platform is with one of the Platform's "quickstart" example programs. Let's take a look.

Rules Services in Action - 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 "business_rules_service" quickstart. Before we walk through the configuration and execution of the quickstart, let's take a look at its (3) Rules files. It important to note that Rules are actually used in multiple ways in this quickstart as it simulates a customer making a purchase from an e-commerce site. The quickstart uses Rules to:
  • Calculate the priority of an incoming customer order
  • Calculate the discount to be applied to an order
  • And route the order to the appropriate service, based on the content of the order
It's also important to note that the quickstart illustrates how a message can be modified as it is processed through the SOA Platform's JBossESB action pipeline. Let's start with the "MyBusinessRules.drl" file, as it establishes the priority of the order. (Oh, wait. We'll jump ahead a bit here and explain that by default, every incoming order has a priority value of "1.")
1:  package com.jboss.soa.esb.routing.cbr
2:
3: #list any import classes here.
4: import org.jboss.soa.esb.message.Message;
5: import org.jboss.soa.esb.message.format.MessageType;
6: import org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.OrderHeader;
7: import org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.Customer;
8:
9: global java.util.List destinations;
10:
11:
12:
13: rule "Logging"
14: when
15: order: OrderHeader()
16: customer: Customer()
17: then
18: System.out.println("Customer Status: " + customer.getStatus());
19: System.out.println("Order Total: " + order.getTotalAmount());
20: end
21:
22: rule "Customer Platinum Status"
23: when
24: customer: Customer(status > 50)
25: order: OrderHeader(totalAmount > 50)
26: then
27: System.out.println("Platinum Customer - High Priority");
28: order.setOrderPriority(3);
29: end
30:
31: rule "Customer Gold Status"
32: when
33: customer: Customer(status > 10, status <= 50)
34: order: OrderHeader(totalAmount > 25)
35: then
36: System.out.println("Gold Customer - Medium Priority ");
37: order.setOrderPriority(2);
38: end
39:
Let's examine this Rules file line-by-line:
  • Line 1 - Similar to Java, a package is a related set of rules.
  • Lines 4-7 - These imports perform the same function as Java language imports. Note that we're importing both SOA Platform Message related packages and packages contained in the quickstart itself.
  • Line 9 - Make a mental note of this global definition as it will be used by the other Rules files and by the quickstart for content based routing.
  • Line 13-14 - The name for the first Rule and the start of its "when" clause.
  • Line 15 - This creates a Rule variable named "order" and initializes with the value of the OrderHeader fact (org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.OrderHeader) that is presented to the Rule.
  • Line 16 does the same for a Rule variable named customer and the Customer fact. Note that there is nothing conditional about these assignments. The rule will match every OrderHeader and Customer. This matching is actually an important concept to keep in mind as part of each "when" clause involves trying to match the facts passed to the rules.
  • Line 20 - A rule must always have an end statement.
  • Line 22 - This rule sets the status for the highest priority ("Platinum") customers.
  • Line 24-25 - Note the differences in these statements to those in lines 15 and 16. The rule will only be executed if:
    • Line 24 - The customer's status is greater that 50. If this is not the case, then the customer variable we set in line 16 will not be changed, and
    • Line 25 - The total amount of the order is also greater than 50. If this is not the case, then the order variable that we set in line 15 will not be changed.
  • Line 28 - Remember how I said that the default priority for each order was set to "1". Here's where we set the priority. Note that we are setting the value in the "order" Rule variable.
  • Lines 31-38 - This rule sets the status for the 2nd highest priority ("Gold") customers. What's interesting in this rule is line 33 as it includes two criteria that must both be met in order for the fact to match the rule.
The next Rules file that we'll look at picks up where MyBusinessRules.drl leaves off. This Rules file is named: MyBusinessRulesDiscount.drl

1:  package com.jboss.soa.esb.routing.cbr
2:
3: #list any import classes here.
4: import org.jboss.soa.esb.message.Message;
5: import org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.OrderHeader;
6: import org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.Customer;
7:
8: global java.util.List destinations;
9:
10:
11:
12: rule "Logging"
13: salience 10
14: when
15: order: OrderHeader()
16: customer: Customer()
17: then
18: System.out.println("Customer Status: " + customer.getStatus());
19: System.out.println("Order Total: " + order.getTotalAmount());
20: end
21:
22: rule "Customer Platinum Status"
23: salience 20
24: when
25: customer: Customer(status > 50)
26: order: OrderHeader(orderPriority == 3)
27: then
28: System.out.println("Platinum Customer - High Priority - Higher discount");
29: order.setOrderDiscount(8.5);
30: end
31:
32: rule "Customer Gold Status"
33: salience 20
34: when
35: customer: Customer(status > 10, status <= 50)
36: order: OrderHeader(orderPriority == 2)
37: then
38: System.out.println("Gold Customer - Medium Priority - discount ");
39: order.setOrderDiscount(3.4);
40: end
  • Lines 1-8 and the "Logging" rule should look familiar, so let's move on to the other rules defined in this file.
  • Line 13 - Remember how we talked about how rules are not executed in the exact sequence in which they can be viewed in a rules .drl file? This rule is assigned a salience property value of 10. Since this rule only prints out some logging information, it's assigned a lower salience than the other rules defined in the file.
  • Line 22 - This rule sets the discount level for the highest class of customers.
  • Line 23 - And, since we want to be sure that this rule fires, we assign it a salience property value of 20.
  • Lines 25-26 - This "when" clause is true when the rule is able to match both a Customer fact with a status greater than 50 and an OrderHeader fact with an orderPriority equal to 3. If both these conditions are true, then rule the customer and order variables are initialized from the Customer and and OrderHeader facts and the rule is fired.
  • Line 29 - Note that when the rule fires, the setOrderDiscount setter method is executed on the rule variable "order." The same setter method is executed on the OrderHeader fact in working memory. (Remember how we talked about how rules can both react to changes to facts in working memory and also cause changes on those facts? This is an example.) For the "Platinum" class of customers, we assign a generous discount.
  • Lines 32-39 - This rule follows the same pattern as the "Customer Platinum Status" rule. Note that we give the "Gold" class of customers a somewhat less generous discount. ;-)
The third and final Rules file used by the quickstart controls the content-based routing used by the quickstart to route messages to services. In the SOA Platform, Rules is one of the supported mechanisms for implementing content based routing. In contrast to more static routing approaches, this form of message routing is relies on the content in the messages to dictate the route that a message takes. (For background on content based routing, please refer to this blog post: http://jboss-soa-p.blogspot.com/2009/07/when-content-knows-way-content-based.html )

This rules file is aptly named: MyRoutingRules.drl
1:  package com.jboss.soa.esb.routing.cbr
2:
3: #list any import classes here.
4: import org.jboss.soa.esb.message.Message;
5: import org.jboss.soa.esb.message.format.MessageType;
6: import org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.OrderHeader;
7:
8: #declare any global variables here
9: global java.util.List destinations;
10:
11:
12: rule "Highest Priority Orders"
13:
14: when
15: OrderHeader( orderPriority == 3 )
16: then
17: System.out.println("HIGHEST PRIORITY");
18: destinations.add("SuperSpecialCustomerService");
19:
20: end
21:
22: rule "Medium Priority Orders"
23:
24: when
25: OrderHeader( orderPriority == 2 )
26: then
27: System.out.println("Medium Priority");
28: destinations.add("SpecialCustomerService");
29: end
30:
31: rule "Low Priority Orders"
32:
33: when
34: OrderHeader( orderPriority == 1 )
35: then
36: System.out.println("Low Priority");
37: destinations.add("RegularCustomerService");
38: end
39:
  • Lines 1-6 - Once again, these import lines should look familiar.
  • Line 9 - Make note of the destinations List. We'll see this used when the messages are routed.
  • The rules in this rules file are pretty straight-forward. In each of the three rules we add the appropriate destination, based on the orderPriority in the OrderHeader, where that destination is a service that the quickstart deploys to the JBossESB in the SOA Platform.
OK, those are the rules that we'll use in the quickstart. Let's now step through the quickstart as it is run.

To deploy the quickstart, execute this ant target:
1:  ant deploy
And, we then see this written to the server log:
1:  22:20:57,568 INFO [QueueService] Queue[/queue/quickstart_Business_Rules_Request_GW] started, fullSize=200000, pageSize=2000, downCacheSize=2000
2: 22:20:57,580 INFO [QueueService] Queue[/queue/quickstart_Business_Rules_Request_ESB] started, fullSize=200000, pageSize=2000, downCacheSize=2000
3: 22:20:57,621 INFO [QueueService] Queue[/queue/quickstart_Business_Rules_ConciergeManager] started, fullSize=200000, pageSize=2000, downCacheSize=2000
4: 22:20:57,632 INFO [QueueService] Queue[/queue/quickstart_Business_Rules_DistributionManager] started, fullSize=200000, pageSize=2000, downCacheSize=2000
5: 22:20:57,643 INFO [QueueService] Queue[/queue/quickstart_Business_Rules_BasicShipping] started, fullSize=200000, pageSize=2000, downCacheSize=2000
6: 22:20:57,682 INFO [EsbDeployment] Starting ESB Deployment 'Quickstart_business_rules_service.esb'
And to run it, exceute this ant target:
1:  ant runtest
When the quickstart is run, here's what happens, step by step. 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.

Step 1 - Create a Message and Pass it Through a Gateway to the Deployed Quickstart Application

Like many of the SOA Platform quickstarts, the business_rules_service quickstart initiates its actions when a message is inserted into a queue that is being watched by a gateway listener. What's a gateway? On the JBossESB in the SPA Platform, everything is either a service that generates or consumes messages, or a message. That is, a message that is in the form (org.jboss.soa.esb.message) that the ESB understands. Services that can understand messages in this form are referred to as being "ESB-aware."

How can you connect other, and potentially older, legacy applications over the ESB? By using gateways. A gateway (org.jboss.soa.esb.listeners.gateway) is a service that acts as a bridge between an ESB-aware and an ESB-unaware client and service. Gateways translate information between ESB and non-ESB message formats and EPRs. (EPR stands for endpoint reference.) Gateways are listener processes in that they "listen" for incoming communications. They are different from ESB-aware listeners in that they accept data in different formats such as objects in files or SQL tables. ESB-aware listeners can only accept messages in the org.jboss.soa.esb.message format.

The SOA Platform supports these gateways:
  • file gateways: local filesystem, ftp, sftp and ftps
  • JMS
  • HTTP/HTTPS
  • email (POP3)
  • SQL table
  • Hibernate
In the case of this quickstart, we'll use a JMS gateway to receive the JMS message. The gateway queue, and its corresponding ESB-aware queue are defined in the jms-provider section of the jboss-esb.xml file:
1:  <jms-bus busid="quickstartGwChannel">
2: <jms-message-filter dest-type="QUEUE"
3: dest-name="queue/quickstart_Business_Rules_Request_GW" />
4: </jms-bus>
5: <jms-bus busid="quickstartEsbChannel">
6: <jms-message-filter dest-type="QUEUE"
7: dest-name="queue/quickstart_Business_Rules_Request_ESB" />
8: </jms-bus>
And the listener is defined at the top of the "Business_Rules_Service" service definition:
1:   <service category="Business_RulesServices"
2: name="Business_Rules_Service" description="The main entry point">
3: <listeners>
4: <!-- Gateway -->
5: <jms-listener name="TheGateway"
6: busidref="quickstartGwChannel" is-gateway="true" />
7: <jms-listener name="TheESBChannel"
8: busidref="quickstartEsbChannel" >
9: </jms-listener>
10: </listeners>
11: <actions mep="OneWay">
  • Note that on line 6, we identify the listener as a gateway.
  • Also note that on line 11, we define the mep, or "message exchange pattern." In the case of this quickstart, the pattern is "OneWay" which indicates that the message pattern is asynchronous. We're sending messages, but not waiting around (or blocking) for a response.
How do we generate this message? Take a look at the "runtest" target, specifically the classname, in the quickstart's ant build.xml file:
1:  <target name="runtest" depends="compile"
2: description="willl receive JMS message to tigger the actions in the ESB">
3: <echo>Runs Test JMS Sender</echo>
4: <java fork="yes" classname="org.jboss.soa.esb.samples.quickstart.businessrules.test.SendJMSMessage" failonerror="true">
5: <classpath refid="exec-classpath" />
6: </java>
That's right - we're sending a JMS message with a program named "SendJMSMessage." It's hard to get simpler than that. ;-)

In order to simulate a realistic customer order in the message, SendJMSMessage builds the message from the quickstart's SampleOrder.xml file:
1:  <Order orderId="1" orderDate="Wed Nov 15 13:45:28 EST 2006" statusCode="0"
2: netAmount="59.97" totalAmount="64.92" tax="4.95">
3: <Customer userName="user1" firstName="Harry" lastName="Fletcher" state="SD"/>
4: <OrderLines>
5: <OrderLine position="1" quantity="1">
6: <Product productId="364" title="The 40-Year-Old Virgin " price="29.98"/>
7: </OrderLine>
8: <OrderLine position="2" quantity="1">
9: <Product productId="299" title="Pulp Fiction" price="29.99"/>
10: </OrderLine>
11: </OrderLines>
12: </Order>
The SendJMSMessage class reads this file, creates a message object of type javax.jms.ObjectMessage (remember, this is an ESB-unaware message), and writes it to the queue (queue/quickstart_Business_Rules_Request_GW) on which our JMS gateway is listening. The listener receives the message and the ESB converts it to an ESB-aware message and then passes it onto the ESB through the queue/quickstart_Business_Rules_Request_ESB queue.

Now it starts to get more interesting.

Remember that our rules rely on certain types of facts (which are JavaBeans) being available in working memory. Where do these facts come from? We'll create them out of that message.

Step 2 - Transform the Message into Beans

The problem is that we have to have a way to create those facts out of the information in the message. Luckily, one of the tasks that the JBossESB in the SOA Platform performs is "transformation." The next action in the quickstart's action pipeline uses the smooks processing engine (http://www.smooks.org/) and the JBossESB's out-of-the-box "SmooksAction" action to perform the transformation of the information in the message into facts:
1:  <action name="transform"
2: class="org.jboss.soa.esb.smooks.SmooksAction">
3: <property name="smooksConfig" value="/smooks-res.xml" />
4: <property name="resultType" value="JAVA" />
5: </action>
The source code for the facts (remember that these are JavaBeans and have getter and setter methods) is in these source files in the quickstart:

In: src/org/jboss/soa/esb/samples/quickstart/businessrules/dvdstore
  • Customer.java
  • OrderHeader.java
  • OrderItem.java
The quickstart uses the smooks-res.xml file to perform the transformation. Let's take a look at this file.
1:  <?xml version='1.0' encoding='UTF-8'?>
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:
5: <!-- Populate the OrderHeader -->
6: <jb:bean beanId="orderHeader" class="org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.OrderHeader" createOnElement="order">
7: <jb:value property="orderId" data="Order/@orderId" />
8: <jb:value property="orderDate" data="Order/@orderDate" decoder="Calendar">
9: <jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
10: <jb:decodeParam name="locale-language">en</jb:decodeParam>
11: <jb:decodeParam name="locale-country">US</jb:decodeParam>
12: </jb:value>
13: <jb:value property="statusCode" data="Order/@statusCode" />
14: <jb:value property="netAmount" data="Order/@netAmount" />
15: <jb:value property="totalAmount" data="Order/@totalAmount" />
16: <jb:value property="tax" data="Order/@tax" />
17: </jb:bean>
18:
19: <!-- Populate the Customer -->
20: <jb:bean beanId="customer" class="org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.Customer" createOnElement="customer">
21: <jb:value property="userName" data="customer/@userName" />
22: <jb:value property="firstName" data="customer/@firstName" />
23: <jb:value property="lastName" data="customer/@lastName" />
24: <jb:value property="state" data="customer/@state" />
25: </jb:bean>
26:
27: <!-- Populate the OrderItem list -->
28: <jb:bean beanId="orderItemList" class="java.util.ArrayList" createOnElement="orderlines">
29: <jb:wiring beanIdRef="orderItem" />
30: </jb:bean>
31:
32: <!-- Populate the OrderItem instance -->
33: <jb:bean beanId="orderItem" class="org.jboss.soa.esb.samples.quickstart.businessrules.dvdstore.OrderItem" createOnElement="orderlines/orderline">
34: <jb:value property="position" data="orderline/@position" />
35: <jb:value property="quantity" data="orderline/@quantity" />
36: <jb:value property="productId" data="orderline/product/@productId" />
37: <jb:value property="title" data="orderline/product/@title" />
38: <jb:value property="price" data="orderline/product/@price" />
39: </jb:bean>
40:
41: </smooks-resource-list>
That's right - it's using XPath (http://www.w3.org/TR/xpath/) to parse the information in the message into Customer (starting at line NN), OrderHeader (starting at line NN), and OrderItem.java (starting at line NN) JavaBeans and a java.util.ArrayList object that contains a list of the orders.

Step 3 - And Add the Beans Back into the Message

What happens next? Well, we have the original message, and some JavaBeans. But, remember that on the JBossESB in the SOA Platform, everything is either a message or a service. What we need is some way to get those JavaBeans back into the message. The way that we do this is with the next action in the action pipeline:
1:  <action name="map_order_components" class="org.jboss.soa.esb.actions.scripting.GroovyActionProcessor">
2: <property name="script" value="/map_order_components.groovy" />
3: </action>
GroovyActionProcessor is another of the SOA Platform's out-of-the-box actions. The groovy script referenced by the action takes the JavaBeans that we just created and adds them (note the use of the beanId's that we defined in the smooks-res.xml file) back into the message with property names that match the bean IDs. The script is very short - lines 3 and 4 add the orderHeard and customer to the message body:
1:    // Need to map down the orderHeader and customer beans onto the message
2: // to make them available to the ObjectMapper...
3: message.getBody().add("orderHeader", message.getBody().get().get("orderHeader"));
4: message.getBody().add("customer", message.getBody().get().get("customer"));
Now we have the JavaBeans in the message. What happens next? The quickstart updates the customer status (it's set to a value of "0" in the JMS message that started the quickstart) with the "UpdateCustomerStatus" custom action:
1:  <!-- Update Customer Status -->
2: <action name="updateCustomerStatus"
3: class="org.jboss.soa.esb.samples.quickstart.businessrules.UpdateCustomerStatus">
4: <property name="status" value="60"/>
5: </action>
We'll set this to a value of 60 as the customer is in the platinum customer class. (Hint: For extra credit, try the quickstart with different status values.)

Here's the output in the log - this line is printed by the org.jboss.soa.esb.samples.quickstart.businessrules.UpdateCustomerStatus custom action:
1:  21:42:15,793 INFO [STDOUT] { Updated customer status to 60}
Step 4 - Process the Message with the BusinessRulesProcessor

OK - now we can see the BusinessRulesProcessor execute in the next action:
1:    <!-- Use the BRP to calculate the order priority -->
2: <action
3: class="org.jboss.soa.esb.actions.BusinessRulesProcessor"
4: name="BRP">
5: <property name="ruleSet"
6: value="MyBusinessRules.drl" />
7: <property name="ruleReload" value="true" />
8: <property name="object-paths">
9: <object-path esb="body.orderHeader" />
10: <object-path esb="body.customer" />
11: </property>
12: </action>
13:
14: <action name="reviewMessage1"
15: class="org.jboss.soa.esb.samples.quickstart.businessrules.ReviewMessage">
16: <property name="stuff" value="After Order Priority"/>
17: </action>
  • Line 1 - Comments are always good things! ;-)
  • Line 2 - Here's the start of the action that makes use of the BusinessRulesProcessor.
  • Line 3 - And here's the reference to the BusinessRulesProcessor class.
  • Lines 5-6 - And, here's the reference to the Rules file that we want to execute.
  • Line 7 - This property causes the Rule to be if the file changes.
  • Lines 8-11 - And, here are the objects that we added to the message - remember the map_order_components.groovy file?
So, what just happened? We made the orderHeader and customer objects available to the Rules defined in MyBusinessRules.drl and executed the Rules. The net effect of this is that the priority of the order defined in the message should have changed. The next action in the action pipeline writes this to the log:
1:  21:42:15,931 INFO [STDOUT] Platinum Customer - High Priority
2: 21:42:15,932 INFO [STDOUT] Customer Status: 60
3: 21:42:15,932 INFO [STDOUT] Order Total: 64.92
The first three lines are printed by the rules. Remember how the rules all included System.out.prinln statements? And how the logging rule that prints the customer status and order total has a lower value salience property than the rules that set and print the customer priority? That explains the order in which the statements are printed to the log.

And then these lines are printed by the org.jboss.soa.esb.samples.quickstart.businessrules.ReviewMessage cusom action:
1:  21:42:15,932 INFO [STDOUT] { ================ After Order Priority
2: 21:42:15,933 INFO [STDOUT] Customer: user1,Harry,Fletcher,SD,60
3: 21:42:15,933 INFO [STDOUT] Order Priority: 3
4: 21:42:15,933 INFO [STDOUT] Order Discount: 0.0
5: 21:42:15,933 INFO [STDOUT] Order Total: 64.92
6: 21:42:15,933 INFO [STDOUT] } ================ After Order Priority
The rule "Highest Priority Orders" defined in MyBusinessRules.drl has set the order priority to 3.

Next, the quickstart calls the BusinessRulesProcessor again, this time to determine the order discount:
1:   <!-- Use the BRP to calculate the order discount -->
2: <action
3: class="org.jboss.soa.esb.actions.BusinessRulesProcessor"
4: name="BRP2">
5: <property name="ruleSet"
6: value="MyBusinessRulesDiscount.drl" />
7: <property name="ruleReload" value="true" />
8: <property name="object-paths">
9: <object-path esb="body.orderHeader" />
10: <object-path esb="body.customer" />
11: </property>
12: </action>
13:
14: <action name="reviewMessage2"
15: class="org.jboss.soa.esb.samples.quickstart.businessrules.ReviewMessage">
16: <property name="stuff" value="After Order Discount"/>
17: </action>
  • Line 6 - The rule "Customer Platinum Status" defined in the MyBusinessRulesDiscount.drl file is executed as our customer has both the highest priority as well as the status value of 60. That rule and the logging rule print out these statements to the log in this order:
1:  21:42:16,062 INFO [STDOUT] Platinum Customer - High Priority - Higher discount
2: 21:42:16,063 INFO [STDOUT] Customer Status: 60
3: 21:42:16,063 INFO [STDOUT] Order Total: 64.92
Next, the org.jboss.soa.esb.samples.quickstart.businessrules.ReviewMessage custom action is executed to print these statements to the log:
1:  21:42:16,063 INFO [STDOUT] { ================ After Order Discount
2: 21:42:16,063 INFO [STDOUT] Customer: user1,Harry,Fletcher,SD,60
3: 21:42:16,064 INFO [STDOUT] Order Priority: 3
4: 21:42:16,064 INFO [STDOUT] Order Discount: 8.5
5: 21:42:16,064 INFO [STDOUT] Order Total: 64.92
6: 21:42:16,064 INFO [STDOUT] } ================ After Order Discount
And there we see our generous 8.5% discount.

Next, the quickstart prints the entire message to the log with this out of the box action:
1:  <action name="sout" class="org.jboss.soa.esb.actions.SystemPrintln" />
And here's how it appears in the server.log:
1:  21:42:16,064 INFO [STDOUT] Message structure:
2: 21:42:16,064 INFO [STDOUT] [{orderHeader=1, java.util.GregorianCalendar[time=1163616328000,areFieldsSet=true,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="US/Eastern",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=US/Eastern,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2006,MONTH=10,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=15,DAY_OF_YEAR=?,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=?,AM_PM=?,HOUR=?,HOUR_OF_DAY=13,MINUTE=45,SECOND=28,MILLISECOND=?,ZONE_OFFSET=-18000000,DST_OFFSET=0], 0, 59.97, 64.92, 4.95, , orderItemList=[1,1,364,The 40-Year-Old Virgin ,29.98, 2,1,299,Pulp Fiction,29.99], orderItem=2,1,299,Pulp Fiction,29.99, customer=user1,Harry,Fletcher,SD,60}].
That's a little hard to read. Let's take out the order date/time so that we can better see the customer and orderHeader Beans that we added to the message:
1:  {
2: orderHeader=1, 0, 59.97, 64.92, 4.95,
3: orderItemList=[1,1,364,The 40-Year-Old Virgin ,29.98, 2,1,299,Pulp Fiction,29.99],
4: orderItem=2,1,299,Pulp Fiction,29.99,
5: customer=user1,Harry,Fletcher,SD,60
6: }
Step 5 - Route the Message to its Destination Based on its Content

At this point, the quickstart has finished using the BusinessRulesProcessor, but it's not yet done using Rules. Remember how I said that on the JBossESB in the SOA Platform everything is either a message or a service? Well, one of the main functions performed by the ESB is to route messages to the correct service. These routes can be static, or they can be dynamic, based on the content of a message. Here's where content based routing with Rules comes in. The routing rules are defined in the MyRoutingRules.drl file. Remember how the rules in this file designated the "destinations" of the messages? This next action uses invokes the org.jboss.soa.esb.actions.ContentBasedRouter class to route the messages to their intended destinations.
1:  1  <!-- Use the CBR to route the "scored" order to the appropriate service team -->
2: 2 <action
3: 3 class="org.jboss.soa.esb.actions.ContentBasedRouter"
4: 4 name="ContentBasedRouter">
5: 5 <property name="ruleSet" value="MyRoutingRules.drl" />
6: 6 <property name="ruleReload" value="true" />
7: 7 <property name="destinations">
8: 8 <route-to
9: 9 destination-name="SuperSpecialCustomerService"
10: 10 service-category="ConciergeManager" service-name="ConciergeService" />
11: 11 <route-to
12: 12 destination-name="SpecialCustomerService"
13: 13 service-category="DistributionManager" service-name="DistributionService" />
14: 14 <route-to
15: 15 destination-name="RegularCustomerService"
16: 16 service-category="BasicShipping" service-name="ShipperService" />
17: 17 </property>
18: 18 <property name="object-paths">
19: 19 <object-path esb="body.orderHeader" />
20: 20 <object-path esb="body.customer" />
21: 21 </property>
22: 22 </action>
  • Lines 107-109, 110-112, and 113-115 define the routes. Since our message now has the highest priority, it is routed to the ConciergeService.
Here's what the log shows us. Again, the first line is printed by the rule:
1:  21:42:16,210 INFO [STDOUT] HIGHEST PRIORITY
And the remaining lines are printed by the org.jboss.soa.esb.samples.quickstart.businessrules.ReviewMessage custom action as invoked by the ConciergeService.
1:  21:42:16,309 INFO [STDOUT] { ================ Concierge
2: 21:42:16,309 INFO [STDOUT] Customer: user1,Harry,Fletcher,SD,60
3: 21:42:16,309 INFO [STDOUT] Order Priority: 3
4: 21:42:16,309 INFO [STDOUT] Order Discount: 8.5
5: 21:42:16,310 INFO [STDOUT] Order Total: 64.92
6: 21:42:16,310 INFO [STDOUT] } ================ Concierge
Well, the customer's order was delivered to the very posh ConciergeService, and the quickstart's execution is complete.

Closing Thoughts

OK, let's review what happened. The quickstart defines multiple business rules to examine and modify a message as it is processed by actions executed by ESB services, then it routes that message to the correct destination service. The rules are maintained in .drl files, separate from the services' custom action code, which makes it easier to maintain them.

And, notice what the quickstart did not have to do - the rules were executed and the content based routing was performed through out of the box actions provided by the SOA Platform's JBossESB. This made it possible for the quickstart to concentrate on the Rules business logic.

Acknowledgements

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


References