This article assumes that all of the prerequisites from previous week's article are in place i.e. WebLogic 12.2.1.3 is installed, the WsAtExampleService is deployed, and WS-AT is enabled for your WebLogic domain.
First lets see what happens when we try to call the WsAtExampleService directly using SoapUI. The response is a Fault that looks like this...
SOAP Fault
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
<S:Body>
<ns1:Fault xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.w3.org/2003/05/soap-envelope">
<ns1:Code>
<ns1:Value>ns1:Receiver</ns1:Value>
</ns1:Code>
<ns1:Reason>
<ns1:Text xml:lang="en">transaction context is required to be inflowed</ns1:Text>
</ns1:Reason>
</ns1:Fault>
</S:Body>
</S:Envelope>
What does that mean? Simply that the service doesn't have a transaction to work with - this is because we specified Transactional.TransactionFlowType.MANDATORY for the @Transactional annotation value on WsAtExampleService i.e. it requires to be run within a transaction. Since SoapUI isn't cable of creating a transaction itself, we get this Fault back.
In order to call a WS-AT service that has the MANDATORY flow type, we must invoke it within a transaction. This typically means we need to have another, coordinating, webservice that runs in WebLogic, creates transactions, and calls participating services. So lets see how that's done.
First we need a client/proxy for the WsAtExampleService. This is generated using wsimport. I'm going for generating Java source rather than building a compiled JAR file, but whichever option you choose is up to you.
wsimport
wsimport -extension -keep -Xnocompile http://127.0.0.1:7001/WsAtExample-1.0/WsAtExample?WSDL
The client/proxy classes will be used by the JAX-WS runtime to connect to the WsAtExampleService. Below is the code for the coordinator service. It is essentially a wrapper around the participant service, this is just an example after all; a real-world coordinator service would do more work e.g. running database updates or calling multiple participant services within the same transaction.
Java
package net.igorkromin;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import javax.xml.ws.BindingType;
import net.igorkromin.wsat.participant.*;
import weblogic.wsee.wstx.wsat.Transactional.TransactionFlowType;
import weblogic.wsee.wstx.wsat.Transactional.Version;
import weblogic.wsee.wstx.wsat.TransactionalFeature;
@WebService(
name="WsAtCallExamplePortType",
portName="WsAtCallExamplePort",
serviceName="WsAtCallExampleService",
targetNamespace="http://igorkromin.net/wsat/coordinator"
)
@SOAPBinding(
style=SOAPBinding.Style.DOCUMENT,
use=SOAPBinding.Use.LITERAL,
parameterStyle=SOAPBinding.ParameterStyle.WRAPPED
)
@BindingType(value = javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
public class WsAtCallExampleImpl {
public WsAtCallExampleImpl() {
super();
}
@WebMethod()
public void doAtomicOp()
throws Exception
{
Context ctx = null;
UserTransaction txn = null;
try {
ctx = new InitialContext();
txn = (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
txn.begin();
TransactionalFeature wsatFeature = new TransactionalFeature();
wsatFeature.setFlowType(TransactionFlowType.MANDATORY);
wsatFeature.setVersion(Version.WSAT12);
WsAtExampleService wsatService = new WsAtExampleService();
WsAtExamplePortType wsatPort = wsatService.getWsAtExamplePort(wsatFeature);
wsatPort.doOp();
if (txn.getStatus() == javax.transaction.Status.STATUS_MARKED_ROLLBACK) {
throw new RuntimeException("Transaction marked for rollback");
}
txn.commit();
}
catch (Exception e) {
txn.rollback();
throw e;
}
}
}
The doAtomicOp() method in this service is a little more complicated than the doOp() method in the participant service, but what it does is fairly straight forward. First we look up a UserTransaction from JNDI. It may seem like magic, but WebLogic handles it all behind-the-scenes and each request will have its own instance of a UserTransaction. This class is key to creating a distributed/global transaction, which again WebLogic takes care of, we only need to call begin() to start the transaction and WebLogic's JTA does the heavy lifting.
Once the transaction has begun we create a TransactionalFeature with the version and flow type that matches our participant service (strictly, it doesn't have to match of course, depends on how you want to handle transactions but this example focuses on having mandatory transactions.) Then the Service and Port for the participant service are created, passing in the TransactionalFeature. Now we're ready to call the participant service!
There's nothing special about calling the participant service at this point, simply invoke doOp() on the port object. All WS-AT coordination and transaction inflow are handled by WebLogic behind the scenes.
Once the participant service returns a response (or throws an exception) the transaction we started is handled as appropriate. Successful responses will end up calling commit() on the UserTransaction, failures will call rollback(). Note that prior to calling commit() there is a check for the STATUS_MARKED_ROLLBACK status on the transaction. If this status value is set, the only possible option is calling rollback(), so we throw a RuntimeException to make sure that the rollback() method is called from the catch block. There are other ways of handling this state but for this example it is the simplest.
Again, WebLogic does all of the heavy lifting in the background once the transaction is concluded from our code. Magic, right?!
So how do we build this service? The POM file is pretty much identical to the participant service, only service names are updated. The previous article goes into the nuances of specifying WebLogic versions for dependencies correctly so I won't go into that again here.
Now the coordinating service can be called and invoked as usual. It will in turn invoke the participating service within a transaction. You can add multiple participating service invocations to the coordinator and their data operations will either be committed or rolled back atomically, spanning multiple application servers and possibly databases.
Both the participating (wsgen_jaxws_wsat_example) and coordinating (wsgen_jaxws_wsat_call_example) service example projects can be found over at GitHub here - https://github.com/ikromin/misc/tree/master/j2ee.
-i