SyntaxHighlighter

Friday, March 25, 2016

Integrate JBoss BPMSuite with JBoss DataGrid

JBoss BPMSuite provides multiple components to integrate the process-engine and processes with various kinds of back-end services and systems. Out-of-the-box the platform provides a WS-* web-services component, a RESTful services component and even an Apache Camel based integration component that allows one to utilise the extensive library of Camel integration components.

If no out-of-the-box integration is available, BPMSuite provides a simple extendible WorkItemHandler API that allows one to easily add 'custom' integration logic to the platform.

Recently I got a question from a colleague how to integrate BPMSuite with JBoss DataGrid, the Red Hat Enterprise in-memory DataGrid product based on the upstream Infinispan project (an extremely scalable, high available, key-value store and datagrid platform).

JBoss DataGrid provides a lightweight, high-performant, remoting protocol called HotRod. The JBoss DataGrid distribution provided by Red Hat provides a small JDG-client modules package that can be deployed as module-layer on JBoss EAP, and thus on JBoss BPMSuite.

To integrate BPMSuite with JDG, we first wrote a new InfinispanWorkItemHandler. This WorkItemHandler uses the aforementioned HotRod protocol to communicate with a remote JBoss DataGrid Server instance (or cluster). This WorkItemHandler can then be added to the BPMSuite project as a custom node task.

The JBoss DataGrid integration makes it possible for business processes to both store (PUT) key-value pairs in, and retrieve (GET) key-value pairs from a JBoss DataGrid cluster. This allows processes to reliably store and access high-volumes of in-memory data with high-speed and low-latency.

Furthermore, the integration allows the process designer to point to a single node JDG instance endpoint, or to provide a list of endpoints in a JDG cluster for high-availability and fail-over purposes

A canned demo based on Docker is available, which demonstrates the integration of JBoss BPMSuite and JBoss DataGrid: https://github.com/DuncanDoyle/bpms-datagrid-demo To be able to run the demo, both "Docker" and "docker-compose" are required.

The demo provides a very simple, technical BPMN2 process that demonstrates storing data in, and retrieving data from a JBoss Data Grid cluster (the demo uses a single node, but this can be easily expanded to a multi-node grid). The demo is not intended to demonstrate a real-life business process, but shows the possibilities a process designer has to integrate business processes with a JDG cluster using the InfinispanWorkItemHandler.


A 'key' has to be entered when the process starts. The first node tries to retrieve the 'value' from JDG. If the 'value' is not present, the process creates a HumanTask to enter a new 'value'. If the 'value' is present in the grid, the process creates a HumanTask that allows the user to change the 'value'. Finally, the new or updated value is stored in JDG.

To run the demo:
  • Copy the required distributions (JBoss EAP, JBoss BPMSuite, etc) to the "installs" directory.
  • Run the "prepare-demo.sh" script: this will download the required dependencies (PostgreSQL libraries, WorkItemHandler project, etc.), prepare the Maven repository for our WIH, etc.
  • Run the "build-docker-images.sh" script: this will build the 3 Docker images.
  • Start the demo using the "start-docker-containers.sh" script.

The demo consists of 3 Docker containers:
  • PostgreSQL: provides the JBoss BPMSuite database
  • JBoss DataGrid Server 6.6.0
  • JBoss BPMSuite 6.2.0.GA

Apart from demonstrating the integration between BPMSuite and DataGrid, the demo therefore also demonstrates:
  • Configuring and using an enterprise-class RDBMS (PostgreSQL) with JBoss BPMSuite.
  • Making external WorkItemHandlers available to JBoss BPMSuite (without the need to manually upload the artefact to the platform).
  • Adding additional JBoss EAP module-layers (PostgreSQL, DataGrid client) to JBoss BPMSuite to be used by the process-engine and processes.

Happy BPM'ing!

Monday, December 1, 2014

Securing the JBoss A-MQ management interfaces



Recently I had to install and configure a JBoss A-MQ (ActiveMQ) environment where security of the management interfaces (Hawtio console, JMX, CLI) was one of the key requirements. Although not particularly hard to implement, it took some time to find the information on how to to do this. The journey for information included reading documentation, analysing JIRAs and stepping through the Karaf classes with a remote debugger. I figured it would be a good idea to document the steps in a blog-post.

Requirements

The security requirements for the management interfaces were simple:
  • All connections should use one-way SSL.
  • Use the central LDAP as the identity provider.
Security of actual JBoss A-MQ transport connectors was out-of-scope for this task.

We will first look at how to connect the JBoss A-MQ system to an LDAP (in this case Apache Directory) environment. After that, we will secure the interfaces via SSL.

Authentication with LDAP

The JBoss A-MQ user manual has an in-depth section on how to enable LDAP authentication on A-MQ: https://access.redhat.com/documentation/en-US/Red_Hat_JBoss_A-MQ/6.1/html/Security_Guide/files/ESBSecurityLDAPAuthentPlugin.html

In this example we will use the following JAAS configuration, which connects to an Apache Directory Server on my local machine:



  
    
      initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
      connection.username=uid=admin,ou=system
      connection.password=jboss@01
      connection.protocol=
      connection.url = ldap://localhost:10389
      user.base.dn = ou=users,ou=testunit,dc=example,dc=com
      user.filter = (uid=%u)
      user.search.subtree = true
      role.base.dn = ou=groups,ou=testunit,dc=example,dc=com
      role.filter = (uniqueMember=%dn)
      role.name.attribute = cn
      role.search.subtree = true
      authentication = simple
    
  



Notice the name and rank attributes of the jaas:config element in the configuration. The JAAS domain name of the default Karaf login-module is 'karaf' and uses the usernames, passwords and roles defined in the "{jboss-a-mq}/etc/users.properties" file. By giving our LDAP JAAS configuration the same name as the default configuration, i.e. 'karaf', and by giving it a higher rank, we override the default configuration with our LDAP configuration. This enables LDAP authentication for all modules within the JBoss A-MQ system that use the 'karaf' JAAS domain, i.e. the Hawtio console, the JBoss A-MQ and the JMX interface.

Also note the %dn placeholder in the role.filter configuration. This is somewhat an undocumented feature that allows one to use the Distinguished Name of the user in the role.filter instead of the username (%u, the username used to login to the system). This is required when the LDAP system uses the DN instead of the user-id (uid) for group membership entries (e.g. Microsoft ActiveDirectory).

Karaf Administrator Role

Now that we can authenticate our users via LDAP, we need to make sure that only the users in the admin group have administrator access in JBoss A-MQ. This is done via the karaf.admin.role property defined in "{jboss-a-mq}/etc/system.properties". This is by default set to 'admin'. In enterprise environments, group names will usually have a more descriptive name then 'admin'. I.e., they might include the name of the system, the name of the platform, etc. In our example, the administrator group for JBoss A-MQ is called 'AMQAdmin'. In the LDAPLoginModule we defined earlier, we've already configured the role.base.dn, role.filter, role.name.attribute and  role.search.subtree properties so that the login-module retrieves our user's roles from LDAP (i.e. the group memberships in LDAP are mapped onto JAAS role principals). Therefore, we need to configure our karaf.admin.role to match our AMQAdmin LDAP group's CN (Comman Name), which is 'AMQAdmin'.

karaf.role.admin=AMQAdmin

Hawtio Admin Role verification

Now that we've configured the default JAAS domain to use LDAP as the identity provider, our JBoss A-MQ Hawtio management console also uses LDAP authentication. Out-of-the-box however, Hawtio does not check the user's role for administrator rights. This means that any user that can access the system with an LDAP login will get full administrative accessm, even if he/she is not part of the AMQAdmin group in LDAP. To enable authorization verification in Hawtio, we need to add the following 2 properties to the "{jboss-a-mq}/etc/system.properties" configuration file:

hawtio.role=${karaf.admin.role}
hawtio.rolePrincipalClasses=org.apache.karaf.jaas.boot.principal.RolePrincipal,org.apache.karaf.jaas.modules.RolePrincipal

This configures the Hawtio console to grant access to the same role as we've configured earlier in the karaf.admin.role property: AMQAdmin

We've now configured LDAP authentication and authorization on all management interfaces, so let's test our setup to verify our configuration. We can access the Hawtio console: http://localhost:8181/hawtio Now that we've configured authentication and authorization of our management interfaces, it is time to configure SSL on all interfaces.

PAX Web

PAX Web is the servlet-container system for the Karaf OSGi platform. The servlet-container implementation it uses is Jetty. It is configured via the "{jboss-a-mq}/etc/org.ops4j.pax.web.cfg" configuration file. To disable Jetty's standard HTTP connector and enable HTTPS (without client authentication), we therefore need to set the correct configuration in "org.ops4j.pax.web.cfg".

First we need to create a Java keystore that will be used by Jetty's HTTP connector. In this example we will create a keystore with self-signed keys and certificates. In  an enterprise environment, one would most likely use keys and certificates signed by a(n) (intermediate) CA.

We will use the Java 'keytool' (which is provided with the JDK) to create the keystore. We'll use the following command:

keytool -genkey -noprompt -alias amq -keyalg RSA -validity 365 -keystore amq-server.keystore -storetype JKS -keypass jboss@01 -storepass jboss@01 -dname "CN=Duncan Doyle, OU=GPS, O=Red Hat, L=Rotterdam, ST=Zuid-Holland, C=NL"


This creates the keystore "amq-server.keystore" with a self-signed certificate, with "jboss@01" as the keystore-password and key-password.

We now replace the properties in "org.ops4j.pax.web.cfg" with the following properties:

# Disable the HTTP connector

org.osgi.service.http.enabled=false


# Enable the HTTPS connector on port 8143

org.osgi.service.http.secure.enabled=true

org.osgi.service.http.port.secure=8143


#Configure the keystore

org.ops4j.pax.web.ssl.keystore={karaf.etc}/amq-server.keystore

org.ops4j.pax.web.ssl.keystore.type=JKS

org.ops4j.pax.web.ssl.password=jboss@01

org.ops4j.pax.web.ssl.keypassword=jboss@01

# Do not enable client authentication, we're using one-way SSL

org.ops4j.pax.web.ssl.clientauthwanted=false

org.ops4j.pax.web.ssl.clientauthneeded=false


org.ops4j.pac.web.config.file=etc/jetty.xml


Restart JBoss A-MQ and open the Hawtio console: https://localhost:8143/hawtio . notice that you have to accept the security exception (our keystore is self-signed and therefore not trusted by the browser).

Try to access Hawtio via the HTTP connector: http://localhost:8181/hawtio . Hawtio should no lonlger be accessible on this port.

JMX and SSL

Now that we've secured the Hawtio web-console, we need to secure the JMX interface using SSL. The JMX configuration of the Karaf JMX connector can be found in "{jboss-a-mq}/etc/org.apache.karaf.management.cfg".

As can be seen in the configuration, the JMX system uses the default 'karaf' JAAS domain for authentication (configured in the jmxRealm property), and thus already uses LDAP for authentication. Second, the JMX system by default uses the value of the karaf.admin.role property as the role that is authorized to access Karaf via JMX. We configured this role earlier and have set it to 'AMQAdmin'.

Therefore, the only thing that we need to configure is changing the default transport of the JMX connector to SSL. The JMX connector is configured in the class org.apache.karaf.management.ConnectorServerFactory, which for keystore and truststore management uses the org.apache.karaf.jaas.config.KeystoreManager. To enable SSL on the JMX connector, we first need to deploy the correct keystore and truststore to the Karaf runtime, which will be picked up and made available by the KeystoreManager. Next, we need to configure the ConnectorServerFactory to point to the correct keystore and key.

We're going to reuse the keystore we created for the HTTPS connection. To deploy this keystore to the KeystoreManager, we're going to create a file called "{jboss-a-mq}/etc/jaas-keystore-module.xml" with the following content:


        






Now we need to make sure that the keystore is deployed onto the system before it is required by the ConnnectorServerFactory via the  KeystoreManager. We therefore need to control the deployment order of these modules. This can be done by creating a file called "{jboss-a-mq}/etc/org.apache.felix.fileinstall-jaas-keystore-module.cfg" with the following content:

felix.fileinstall.dir=${karaf.base}/etc
felix.fileinstall.filter = jaas-keystore-module\\.xml
felix.fileinstall.poll = 1000
felix.fileinstall.noInitialDelay = true
felix.fileinstall.log.level = 3
felix.fileinstall.start.level = 25

This filter the files called jaas-keystore-module.xml in the {karaf.base}/etc directory and increases its startlevel so it will be processed first.

Finally, we can configure the JMX ConnectorServerFactory to use SSL by adding the following properties to the file "{jboss-a-mq}/etc/org.apache.karaf.management.cfg":

# Enable SSL on the JMX connector
secured=true
keyStore=jmxKeyStore
trustStore=jmxKeyStore
keyAlias=servercert

Note that we use the keyStore and keyAlias configured in our jaas-keystore-module.xml file.

Restart JBoss A-MQ and connect to the platform using JConsole. Make sure to provide JConsole a truststore that contains the public certificate that belongs to the private key we use for the JMX SSL connection. This can be done by  starting up JConsole with the following command-line:

jconsole -J"-Djavax.net.ssl.trustStore=jconsole.truststore" -J-Djavax.net.ssl.trustStorePassword=jboss@01

Next, connect  with the following connection string:

service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root

Provide your LDAP credentials and connect to the Karaf/JBoss A-MQ JMX interface. If all is configured correctly you should see the following screen.


Conclusion

This article has shown how to secure a vanilla JBoss A-MQ platform to be used in a production environment. Most of this information can be found in various places (where some info is better hidden than other), but we hope that by documenting all these steps in a single place we've eased to process of setting up a secured JBoss A-MQ environment.

Wednesday, October 3, 2012

jBPM5: Calling WebServices from your business processes

In one of my previous blog posts on Business Process Management (BPM) I outlined the important role that BPM plays in a Service Oriented Architecture (SOA), and how a well defined SOA is a prerequisite for successful Cloud implementations.

The jBPM5 engine, part of the JBoss BRMS platform, is a highly flexible and versatile BPM engine based on open standards like BPMN2 and WS-HumanTask. This article will show how the jBPM5 platform can integrate with WebServices by utlizing the JAX-WS API.

The jBPM5 platform provides a simple, but very powerful, extension mechanism to add domain specific service nodes and their implementations to a business process. This allows us to plug our own, custom, logic into the business process runtime system. The extension mechanism is based on the jBPM5 WorkItemHandler API. The WorkItemHandler interface declares 2 methods which need to be implemented by the domain specific WorkItemHandler implementation:
  • public void executeWorkItem(WorkItem workItem, WorkItemManager workItemManager): Is called by the process engine when a service node is executed.
  • public void abortWorkItem(WorkItem workItem, WorkItemManager workItemManager): Can be implemented to signal the workitem to be aborted.
In this example we will utilize this WorkItemHandler API to implement our own custom service nodes which use the JAX-WS API to call external WebServices. This article will not cover how to define and use custom nodes in your business process editor (i.e. the JBoss BRMS BPMN2 Web editor, the JBoss Tools Eclipse BPMN2 editor, etc.). For inforrmation on how to define and use custom process nodes in your BPMN2 process, please consult: 
The source codes of the examples in this blogpost are available in my Github repository. The code also contains the WorktemDefinitions file which contains the definitions of the 'PaymentService' and 'JAX-WS-Dispatcher-Service' workitems. These definitions can, for example, be used in the JBoss BRMS system to add the custom service nodes to the BPMN2 web-editor palet.

The Process
The process used in this example is a simple process which calls a PaymentService to check whether a payment-type (e.g. VISA, MasterCard, etc.) is valid and which, if the payment-type is valid, calls an OrderService to place a specific order. The process contains additional ScriptTask nodes which provide debug output which is written to System.out.





The BPMN2 process definition of this specific process can be found here.

In this example, the process runs in a simple Java client application (the 'Simple_jBPM_Process' project), which creates the jBPM5 KnowledgeBase, containing the BPMN2 process definition, creates the StatefulKnowledgeSession, registers our custom JAX-WS WorkItemHandlers and starts the process instance. See the 'org.jboss.ddoyle.howtojboss.jbpm5webservices.Main' class for the example code.


WebServices
This example contains a WAR project (the 'SimpleWebServicesWeb' project) that can be directly deployed on a JBoss BRMS 5.3 platform (or EAP 5.1.x with the JBossWS-CXF WebService stack) and which contains both the implementation of the 'PaymentService' and the 'OrderService'. The WSDL files of these services can either be obtained from the project's 'src/main/resources' folder or, at runtime, via the JBossWS console at http://localhost:8080/jbossws/services.


JAX-WS
JAX-WS provides two client APIs which can be used to execute WebService calls, the Dispatch API and the Proxy API.

The Proxy API provides a Java proxy object representing the WebService endpoint to be called. The JAXB2 library is utilized to marshall and unmarshall Java objects to and from XML. This client mode can be used when one wants to operate on Java objects.

The Dispatch API is a dynamic API in which the client code which uses this API is responsible for the creation of the XML payload or even the entire SOAP message that will be sent to the WebService endpoint. This API can be used when one wants to operate on the XML message level.

In this example we will use both client modes. We will call the 'PaymentService' using a JAX-WS Proxy client and the 'OrderService' via a JAX-WS Dispatch client.

JAX-WS Proxy Client: PaymentServiceJaxWsProxyWorkItemHandler
JBoss BRMS (and any other JBoss platform for that matter) provides a utility that is able to generate the JAX-WS annotated Java code from a given WSDL file. This utility, called 'wsconsume', is located in the 'jboss-as/bin' directory of your JBoss BRMS  (or EAP) platform. The utility can be used as follows: ./wsconsume.sh -kn {WSDL-file}. This will generate the JAX-WS annotated client and server code which can be used to both implement the webservice provider and a webservice consumer. Both the 'Simple_jBPM_Process' (consumer) and the 'SimpleWebServicesWeb' (provider) contain the JAX-WS code generated from the 'PaymentService' and 'OrderService' WSDL files.

The 'PaymentServiceJaxWsProxyWorkItemHandler' uses the generated JAX-WS Proxy client to call the PaymentService. As can be seen from the code, the WorkItemHandler is, because it directly uses the generated PaymentService proxy classes, tightly coupled to the PaymentService. I.e. This WorkItemHandler implementation is specifically created to call a PaymentService WebService. In general, a WorkItemHandler which uses a JAX-WS Proxy client can only be used to call that specific service. This implies that one has to define and create a WorkItemHandler per service, and thus a domain specific process node per service. 

The jBPM5 WorkItemHandler API provides the ability to pass parameters from the jBPM5 process instance to the WorkItemHandler via a parameter Map. In the jBPM5 BPMN2 process definition, a mapping needs to be defined on the 'PaymentService' node which maps the process instance data onto the parameters required by the WorkItemHandler. 

In the case of the 'PaymentServiceJaxWsProxyWorkItemHandler', 2 parameters can (must) be passed to the WorkItemHandler: 
  • input: a String object containing the input value for the PaymentService.
  • endpointAddress: the URL of the location of the PaymentService. 
The service response data is returned to the process instance via the 'paymentServiceResponse' parameter in the returned parameter Map. This response can then be mapped back onto a process instance variable.

The 'endpointAddress' parameter allows us to configure the actual location of the PaymentService endpoint. This makes it possible to use the same WorkItemHandler to point to different endpoint locations of the PaymentService, which is specifically useful when one has to deploy the process in multiple environments (i.e. Development, Test, Production).


JAX-WS Dispatch Client: JaxWsDispatcherWorkItemHandler
The 'JaxWsDispatcherWorkItemHandler' uses the JAX-WS Dispatch API to, in this process definition, call the OrderService. The JAX-WS Dispatch API allows us to work on XML structures directly. The responsibility of providing the correct XML SOAP-message payload (or the entire SOAP-message itself, depending on whether the Dispatch client is used in PAYLOAD or MESSAGE mode) lies now with the code that uses the Dispatch API.

In this example we've tried to make this WorkItemHandler as generic as possible, so it can be used to call any webservice and can be reused in various projects. This is done by parameterizing the name and location of the service to be called, as well as by providing a simple 'injection' mechanism to inject the RequestMapper and ResponseMapper objects responsible for marshalling and unmarshalling the XML payload from and to jBPM5 WorkItemHandler parameters (which are passed via the WorkItemHandler parameter Map).

The WorkItemHandler requires the following parameters to be passed by the process instance:
  • serviceNamespace: the namespace of the service to be called. I.e. The namespace defined in the services' WSDL file.
  • serviceName: The name of the service as defined in the services' WSDL file.
  • portTypeNamespace: The namespace of the PortType.
  • portTypeName: The name of the PortType.
  • soapAction: The soapAction value of the service operation to be called.
  • endpointAddress: The URL of the service endpoint.
  • requestMapper: The name of the request-mapper which will be used to lookup a RequestMapper instance via the 'RequestMapperFactory'.
  • responseMapper: The name of the response-mapper which will be used to lookup a ResponseMapper instance via the 'ResponseMapperFactory'.
  • input: The input data for the WebService call.
The service response data is returned to the process instance via the 'response' parameter in the returned parameter Map.

These parameters make this WorkItemHandler very generic. One can call different services, inject specific RequestMappers and ResponseMappers, etc.  The picture below shows how the mapping of jBPM5 process instance data to WorkItemHandler parameters is configured in the JBoss BRMS BPMN2 web editor:



If required, the WorkItemHandler can be extended to support additional WS-* features like WS-Security, WS-Addressing, etc.


Running the example.
To run the example, first the project containing the OrderService and PaymentService implementations needs to be build using Maven. To do this, execute the command 'mvn clean install' in the 'SimpleWebServicesWeb' directory. This will build the WAR file and add it to your local Maven repository. Deploy this WAR file on a JBoss BRMS 5.3 platform (or any JBoss platform with a JBossWS-CXF WebServices stack) of which the HTTP connector is bound to 'localhost:8080'. The WSDL files of the WebServices can now be accessed at:

Next, build the 'Simple_jBPM_Process' client project by again executing 'mvn clean install' in the 'Simple_jBPM_Process' directory. This will produce a JAR file which contains all the jBPM5 and Drools dependencies required to run the process instance.

To run the client, simply execute the command 'java -jar Simple_jBPM_Process-0.0.1-SNAPSHOT.jar JBoss_T-Shirt VISA' from a terminal. If everything exectutes correctly, this will produce the following output:

Loading StatefulKnowledgeSession.
Starting Process Instance.
Payment type: VISA
PaymentService response: VALID
Payment approved!
Oct 4, 2012 12:39:43 AM org.jboss.ddoyle.howtojboss.jbpm5webservices.workitemhandlers.JaxWsDispatcherWorkItemHandler executeWorkItem
INFO: Calling Service: http://impl.orderservice.howtojboss.ddoyle.jboss.org/:SimpleOrderServiceService
Oct 4, 2012 12:39:43 AM org.jboss.ddoyle.howtojboss.jbpm5webservices.workitemhandlers.JaxWsDispatcherWorkItemHandler executeWorkItem
INFO: Received response from Service: http://impl.orderservice.howtojboss.ddoyle.jboss.org/:SimpleOrderServiceService
Order 'JBoss_T-Shirt' submitted successfully
Finished Process Instance with id: 1

If another payment-type then VISA is entered, the process will show the following output.

Loading StatefulKnowledgeSession.
Starting Process Instance.
Payment type: MasterCard
PaymentService response: INVALID
Payment not approved. Order cancelled.
Finished Process Instance with id: 1


Conclusion
jBPM5 is a highly versatile and flexible Business Process Management platform which can be easily extended, customized and integrated with a vast array of external systems. In this example we combined the JAX-WS API and the jBPM5 WorkItemHandler API to integrate WebServices with our business processes. The WorkItemHandler API is a simple, yet powerful, construct that can be used to integrate business processes with almost any (external) system, as long as that system provides a Java client API (or as long as a Java client can be written for it). This implies that we can use the same mechanism to, from our  business process, call RESTful services, send JMS messages to message queues (e.g. JBoss HornetQ), etc. As such, the jBPM5 platform can be easily integrated in almost any IT landscape.

Friday, August 10, 2012

Auditing in JBoss Enterprise Portal Platform (EPP)

One of our Red Hat JBoss Enterprise Portal Platform (EPP) customers recently had a requirement to audit all access to their EPP environment. The requirements were:
  1. Audit which users login to the portal, and at what time.
  2. Audit which portlets are being accessed by the user.

The first requirement was implemented by utilising the auditing functionality of the JBoss Security framework. As JBoss EPP is based on JBoss Enterprise Application Platform (EAP), auditing can be implemented in the same way as explained in this blog-post by my colleague Anil Saldhana: http://server.dzone.com/articles/security-auditing-jboss. Furthermore, due to the fact that this security auditing framework is easily extendible, security auditing can be tailored to one's specific needs. In fact, in the current project we implemented a customised version of the JBoss Security AuditProvider interface to reduce the verbosity of the auditing and to log the audit information to a back-end system instead of the default Log4J log-file.

The second requirement can however not be implemented using the Security Auditing framework. Although the framework is able to log which URLs are accessed by the user, it is unable to provide more detailed information on which portlets are actually being accessed. Luckily, the JSR-286 Portlet Specification introduced so called Portlet Filters. Portlet Filters allow one to intercept and filter individual PortletRequests, and as such provide a good entry-point for auditing portlet requests. Furthermore, the JBoss Enterprise Portal Platform allows one to configure systemwide portlet filter-chains, i.e. filter-chains that will be applied to all portlets deployed on the platform, via the {jboss-epp}/jboss-as/server/{profile}/conf/gatein/portlet.xml configuration file. This allows us to control the auditing of portlets from a single point within the system.

The JBoss EPP PortletAuditFilter project provides a first implementation of a simple auditing component for JBoss EPP, that can audit which users access which portlets, including the lifecycle method being called. The current implementation is still limited, as it only provides a filter that will log the audit information to a Log4J log-file. However, the component has been designed to be extended. By extending from AbstractPortletAuditingFilter, and implementing the getLogger() AbstractFactoryMethod, you can provide your own AuditLogger implementation. This allows you to, for example, log the audit information in a database, a data-grid, or any other form of storage. The Log4JPortletAuditingFilter provides a nice starting point for your own custom implementation.

Please consult the README.txt file for installation and configuration instructions.

Although this is a very basic tool, it satisfies our current portlet auditing requirement. Possible future improvements might include filters to whitelist or blacklist auditing information for specific portlets in order to give the user more control over the verbosity of the audit logging.

Monday, February 20, 2012

Autowiring your JBoss ESB Actions with Spring

JBoss ESB is powerful, easy to use, opensource SOA infrastructure. It combines a wide range of technologies to rapidly create powerful services that can process message from various resources. It  provides out-of-the-box suppport for message validation, transformation, routing, notifcation, etc.

Two areas in which functionality and support had been lacking over the years were:
  • Support for unit testing: because your custom actions (ESB units of logic) needed to extend from the JBoss ESB framework classes, actions were hard to unit test.
  • Spring integration: Although standards like EJB3 (3.1) and CDI are gaining popularity, a lot of companies and developers still use the Spring Framework to provide Dependency Injection (DI) functionality. Although JBoss ESB does provide an AbstractSpringAction to use Spring in an ESB context, the support is still very basic.
The first issue, unit testing your JBoss ESB actions, has been solved with the support for annotations introduced in JBoss ESB 4.9. The annotations make it possible to create a JBoss ESB action by annotating a simple POJO. This eases the development of custom actions, but more importantly, it makes unit testing your actions a lot easier, as one does not have to worry about JBoss ESB specific classes and functionality anymore (e.g. you do not have to build a ConfigTree instance anymore as actions can now have a default constructor).

The second issue was solved by the AbstractSpringAction, but using this action has a number of downsides:
  • One has to extend the AbstractSpringAction, tying your logic to JBoss ESB again. Something we tried to prevent with the ESB annotations.
  • A Spring ApplicationContext is created by every single ESB action that extends the AbstractSpringAction. Re-using a Spring ApplicationContext in multiple actions is not possible. This puts a higher (and unnecessary) demand on resources.
We would like to be able to define a Spring ApplicationContext which can be re-used by multiple actions and we would like to be able to retrieve Spring beans from this application context without having to write a lot of plumbing code and without having to extend from JBoss ESB specific classes. Ideally we would like to inject our Spring beans into our actions using annotations, for example via Spring's @Autowired annotation.

By utilizing a number of different Spring utility classes and methods, this functionality is however not really hard to implement. By re-using the approach of Spring's ContextLoader class to load a parent context, we can make sure that a Spring ApplicationContext is loaded only once, but can be referenced many times. The support is based on the correct usage of Spring's ContextSingletonBeanFactoryLocator, which has been specifically designed to access shared BeanFactory instances and allows you to retrieve a reference to an ApplicationContext (BeanFactory). If the ApplicationContext is referenced for the first time, Spring will initialize it, when all references have been destroyed, Spring will destroy it.

The Spring annotation support in JBoss ESB actions is implemented by using functionality from Spring's AutowireCapableBeanFactory. This bean factory contains the method autowireBean(Object exitistingBean), which allows us to programmatically instruct Spring to autowire any existing Object, including JBoss ESB action instances. Every Spring ApplicationContext is able to retrieve such an AutowireCapableBeanFactory.

My JBossEsbSpringIntegration GitHub repository contains a sample project which combines these approaches to autowire a JBoss ESB Action using Spring. The AbstractAutowiredSpringAction is the base class which provides the ApplicationContext loading and autowiring support. Your custom action needs to extend from this class to be autowired. Although your custom action still needs to extend from an abstract base class, this base class is built using JBoss ESB annotations, which prevents your logic from being tied into the JBoss ESB framework.

I've provided an example class called TestDIAction which shows this approach. Because AbstractAutowiredSpringAction uses the JBoss ESB @Initialize and @Destroy annotations to retrieve and destroy the Spring BeanFactory (ApplicationContext) reference, these annotations can not be used in your custom action. Therefore the abstract class provides 2 empty non-final methods, doInitialize()  and doDestroy(), which can be overridden in your action class to execute additional initialize and destroy functionality.

One configuration parameter is required when configuring your action in the jboss-esb.xml deployment descriptor, the context-key parameter, which should contain the bean-id of the ApplicationContext to be used, defined in the beanRefContext.xml file. An optional configuration option, locator-factory-selector, can be used to configure the BeanFactoryLocator instance from which the ApplicationContext is obtained. The default value is 'classpath*:beanRefContext.xml'. An example is again provided in the project, see the 'jboss-esb.xml' file in the JBossEsbSpringIntegration project.

The AbstractAutowiredSpringAction can be packaged in a separate JAR and can be included as a library in your JBoss ESB and/or JBoss SOA-Platform instance. This is an initial attempt to implement annotated dependency injection support in JBoss ESB. Feel free to use the code and let me know what you think.

Special thanks go to Dominique Vandensteen for showing me Spring's AutowireCapableBeanFactory functionality.