Sunday, April 14, 2013

Simplified XTS Context Propagation

In this blog post I'd like to introduce a new feature from our recent Narayana 5.0.0.M2 release. It was developed by our newest addition to the Transactions Team, Gytis Trikleris. This feature simplifies the way you write clients for WS-AT and WS-BA enabled Web services.

Previously the developer was responsible for setting up the client side handler chain. This has proven problematic as the code is rather cumbersome and it's easy to make a mistake. Here's an example of what the client code used to look like:



import org.jboss.jbossts.txbridge.outbound.JaxWSTxOutboundBridgeHandler;
import org.jboss.jbossts.txbridge.outbound.JaxWSHeaderContextProcessor;
...
MyService client = service.getPort(portName, MyService.class);

BindingProvider bindingProvider = (BindingProvider) client;

List handlers = new ArrayList(); 
handlers.add(new JaxWSTxOutboundBridgeHandler()); 
handlers.add(new JaxWSHeaderContextProcessor()); 

bindingProvider.getBinding().setHandlerChain(handlers);


In this example, the application has begun a JTA transaction and is now invoking a Web service. The service supports WS-AT, and the developer would like the JTA transaction to be distributed to this service. Therefore, the developer carefully constructs a client side handler chain and ensures that the bridging handler is invoked before the XTS handler. Get this order wrong and the bridging will fail. This code is required for every Web service port that needs to propagate a WS-AT or WS-BA transaction.

We still support the above way of writing clients and the behaviour of XTS in that case has not changed. However, we now have some additional ways to enable a distributed transaction over Web services.

Enable Globally

You can now enable transaction propagation for all Web service calls that are invoked within a JTA, WS-AT or WS-BA transaction. This is done with the 'defaultContextPropagation' property in the XTS subsystem config of the standalone-xts.xml.


<?xml version="1.0" encoding="UTF-8"?>
<subsystem xmlns="urn:jboss:domain:xts:1.0">
   <xts-environment url="..." />
   <default-context-propagation enabled="true" />
</subsystem>


As this is enabled by default (for standalone-xts.xml), calls to all Web services that support WS-AT or WS-BA will automatically receive the transaction context allowing them to participate in the distributed transaction.

The transaction context is simply ignored if the service does not support WS-AT or WS-BA. This is done by setting MustUnderstand=”false” on the 'CoordinationContext' SOAP header. Unfortunately, this may cause issues when invoking WS-AT or WS-BA enabled Web services on other vendors’ application servers. This is because the WS-Coordination specification states that MustUnderstand must be set to true. If you are affected by this issue, you will need to explicitly enable the transaction propagation as described in the next section.

There is a slight overhead associated with having an unused WS-AT transaction in place. We intend to investigate this as part of https://issues.jboss.org/browse/JBTM-1515



Enable/Disable per Web service port

The default context propagation policy can also be overridden on a per Web Service port basis. This allows the developer to easily state which Web Service clients must and must-not propagate the transaction context.

In the following example, the developer states that the current JTA transaction must be distributed over the Web Service calls on this port:



import org.jboss.jbossts.txbridge.outbound.JTAOverWSATFeature;
...
MyService client = service.getPort(portName, MyService.classnew JTAOverWSATFeature());


This is done through the standard JAX-WS WebServiceFeature facility. A JAX-WS WebServiceFeature allows meta-information to be added to a port that describe cross-cutting behaviour, such as logging, security or compression. In our case we use the 'JTAOverWSATFeature' to state that any JTA (or WS-AT) transaction should be distributed via calls on this client. Calls to this service will fail if the Web service does not support WS-AT or WS-BA (in this case, XTS sets MustUnderstand=true on the 'CoordinationContext' SOAP header as the developer has explicitly stated that it is required).

The developer may also state that the transaction must-not be distributed over calls to this Web service. This is done by setting the 'JTAOverWSATFeature' feature to disabled:



import org.jboss.jbossts.txbridge.outbound.JTAOverWSATFeature;
...
MyService client = service.getPort(portName, MyService.classnew JTAOverWSATFeature(false));


The use of 'JTAOverWSATFeature' overrides whatever default context propagation is set to in the standalone-xts.xml.

Feedback

We've tried to come up with a solution that doesn't impact existing applications, whilst also being as intuitive as possible for developers of new applications. If you are having difficulties with this approach or if it simply doesn't make sense, please get in touch in the usual ways (comments on this post or via the forum).


Acknowledgements

I'd like to say a bit thank you to Alessio Soldano and the JBossWS team. They provided a lot of advice and also added new features to the JBossWS SPI to support these features.

Monday, April 8, 2013

API Improvements for Compensation-based Transactions

In a recent post I talked about API improvements we are introducing for applications that use ACID transactions. In this post I’ll cover what are we doing for users of compensation based transactions.

Even though we only have WS-BA for compensation-based transactions at the moment, we are still following the principle that the same transaction API should be used to develop the application, regardless of what transport is actually used to distribute the transaction. This will become more important when we support compensations over other technologies, such as REST or JBoss remoting.

Unfortunately there is no standard API for compensation based transactions, so we needed to develop our own. This API is still in the early stages of development. However, we are keen to get community feedback, so we made the early version available as part of our recent Narayana 5.0.0.M2 release.

I’ll cover the basics of the API in this post; so as to give you a feel for what we are proposing. You can take a look at the Narayana quickstarts for more complete examples. We also intend to blog more on this subject in the coming months as we develop our ideas further.

I’ve omitted a recap on compensation-based transactions and when you need them. This will be the subject of a future blog posting.

Example

Hopefully this example will give you an idea of how the new API works

The following code comprises part of a "Warehouse Service", implementad as an EJB exposed as a JAX-WS Web Service:


@Compensatable
@Stateless
//JAX-WS annotations omitted
public class WarehouseServiceImpl implements WarehouseService {
{
    @DataManagement private Map txDataMap;
    @PersistenceContext protected EntityManager em;

    @WebMethod
    @ServiceRequest
    public void shipItem(String item, String address) throws {
        //Use em to add order to DB
        txDataMap.put("orderID", orderID);
    }

    @Compensate
    private void cancelOrder() {
        Integer orderID = txDataMap.get("order");
        //Use em to lookup order by ID and cancel it
    }
}

The @Compensatable annotation is used to state that methods on the class should be invoked in a compensation-based transaction. This is similar to the @TransactionAttribute provided by JTA.
The ‘shipItem’ method represents the business logic of the service. It has a corresponding compensation handler which can be used to undo this work.

When ‘shipItem’ is invoked, a new entry is added to the database. This update is done in a regular JTA transaction that commits when the method completes successfully. The ID of the order is saved in the ‘txDataMap’ to be used later by the compensation handler.

The @DataManagement injected Map stores state for the lifetime of the transaction. The state is isolated to the transaction and is garbage collected when the transaction ends. In future releases, this data will also be available at recovery time.

@Compensate annotates the compensation handler for undoing the work done in the business method. In this example, the compensation action is to look up the ID of the order and then make an update to the DB to mark it as cancelled. This DB update is done in a separate transaction that commits when ‘cancelOrder’ completes successfully.

The current release only supports a single @Compensation method for all @ServiceRequest methods in the class. Subsequent releases will remove this limitation.

Getting Started

Hopefully you are now eager to get started and want to know where to go next! Here are our suggestions:

* Download and try the Quickstarts from here.
* Provide feedback and get help through our forum.
* Track the progress of issues here.
* Subscribe to this blog.
* Fork the Narayana repo and contribute. Of course, we always welcome community contribution. We can advise on good issues for new contributors, or you can suggest a feature that interests you.



Acknowledgements

I'd like to say a bit thank you to Alessio Soldano and the JBossWS team. They provided a lot of advice and also added new features to the JBossWS SPI to support these features.