Metro v2.0 has been released
A new version of the open-source Java web services stack Metro v2.0 has been released today. This new release brings several notable new features, such as Configuration Management (check the short info), Persistent RM, experimental Declarative tubeline assembler (see here) as well as new enhancements in WS-Security, Trust and SecureConversation areas.
For the full documentation of all Metro features check the Metro user guide. Metro v2.0 runs on Tomcat, GlassFish V2 and is also shipped with GlassFish V3 — the first application server to support Java EE 6 — which was, same as Metro v2.0, released today.
Metro Reliable Messaging Configuration Summary
I finally found time to write up a summary of all features that can be configured in Metro Reliable Messaging. Please check the tables bellow. Enjoy...
| Prefix | Namespace |
|---|---|
| wsp | http://www.w3.org/ns/ws-policy |
| wsrmp10 | http://schemas.xmlsoap.org/ws/2005/02/rm/policy |
| wsrmp | http://docs.oasis-open.org/ws-rx/wsrmp/200702 |
| net30rmp | http://schemas.microsoft.com/net/2005/02/rm/policy |
| net35rmp | http://schemas.microsoft.com/ws-rx/wsrmp/200702 |
| sunrmp | http://sun.com/2006/03/rm |
| sunrmcp | http://sun.com/2006/03/rm/client |
| metro | http://java.sun.com/xml/ns/metro/ws-rx/wsrmp/200702 |
| Feature name | Description | WS-RM 1.0 compatible assertion | WS-RM 1.1+ compatible assertion |
|---|---|---|---|
| Enable Reliable Messaging + version | Specifies that WS-ReliableMessaging protocol MUST be used when sending messages. Defines also the version of the WS-RM protocol to be used. | /wsrmp10:RMAssertion | /wsrmp:RMAssertion |
| Sequence Inactivity Timeout | Specifies the time interval beyond which either RM Source or RM Destination may terminate the RM sequence due to inactivity. The default setting is 600,000 milliseconds (10 minutes). A web service endpoint will always terminate session whose inactivity timeout has expired. Specified in milliseconds. | /wsrmp10:RMAssertion/wsrmp10:InactivityTimeout | /net35rmp:InactivityTimeout |
| Acknowledgement interval | Specifies the duration after which the RM Destination will transmit an acknowledgement. If omitted, there is no implied value. Specified in milliseconds. | /wsrmp10:RMAssertion/wsrmp10:AcknowledgementInterval | /net35rmp:AcknowledgementInterval |
| Retransmission Interval | Specifies how long the RM Source will wait after transmitting a message and before retransmitting the message. If omitted, there is no implied value. Specified in milliseconds. | /wsrmp10:RMAssertion/wsrmp10:BaseRetransmissionInterval/sunrmcp:ResendInterval | /metro:RetransmissionConfig/metro:Interval |
| Retransmission Interval Adjustment Algorithm | Specifies that the retransmission interval will be adjusted using a specific (e.g. exponential back-off) algorithm. |
("Exponential backoff" algorithm only) | /metro:RetransmissionConfig/metro:Algorithm |
| Maximum Retransmission Count |
| N/A | /metro:RetransmissionConfig/metro:MaxRetries |
| Close sequence timeout | By default, the call to proxy.close() will not return until all messages have been acknowledged. RM close timeout is the interval (in milliseconds) that the client runtime will block waiting for a call to close() to return. If there are still unacknowledged messages after this interval is reached, and the call to close has returned, an error will be logged about messages being lost. |
(client side only) | /metro:CloseSequenceTimeout |
| Acknowledgement request interval | Defines the suggested minimum time that the sender (RM Source) should allow to elapse between sending consecutive Acknowledgement request messages to the RM Destination. | /sunrmcp:AckRequestInterval | /metro:AckRequestInterval |
| Bind RM sequence to security token | Defines the requirement that an RM Sequence MUST be bound to an explicit token that is referenced from a wsse:SecurityTokenReference in the CreateSequence message. | N/A | /wsrmp:RMAssertion/wsp:Policy/wsrmp:SequenceSTR |
| Bind RM sequence to secured transport | Defines the requirement that an RM Sequence MUST be bound to the session(s) of the underlying transport-level protocol used to carry the CreateSequence and CreateSequenceResponse message. (When present, this assertion MUST be used in conjunction with the sp:TransportBinding assertion.) | N/A | /wsrmp:RMAssertion/wsp:Policy/wsrmp:SequenceTransportSecurity |
| Exactly once delivery | Each message is to be delivered exactly once; if a message cannot be delivered then an error MUST be raised by the RM Source and/or RM Destination. The requirement on an RM Source is that it SHOULD retry transmission of every message sent by the Application Source until it receives an acknowledgement from the RM Destination. The requirement on the RM Destination is that it SHOULD retry the transfer to the Application Destination of any message that it accepts from the RM Source until that message has been successfully delivered, and that it MUST NOT deliver a duplicate of a message that has already been delivered. | default | /wsrmp:RMAssertion/wsp:Policy/wsrmp:DeliveryAssurance/wsp:Policy/wsrmp:ExactlyOnce |
| At Most once delivery | Each message is to be delivered at most once. The RM Source MAY retry transmission of unacknowledged messages, but is NOT REQUIRED to do so. The requirement on the RM Destination is that it MUST filter out duplicate messages, i.e. that it MUST NOT deliver a duplicate of a message that has already been delivered. | N/A | /wsrmp:RMAssertion/wsp:Policy/wsrmp:DeliveryAssurance/wsp:Policy/wsrmp:AtMostOnce |
| At Least once delivery | Each message is to be delivered at least once, or else an error MUST be raised by the RM Source and/or RM Destination. The requirement on an RM Source is that it SHOULD retry transmission of every message sent by the Application Source until it receives an acknowledgement from the RM Destination. The requirement on the RM Destination is that it SHOULD retry the transfer to the Application Destination of any message that it accepts from the RM Source, until that message has been successfully delivered. There is no requirement for the RM Destination to apply duplicate message filtering. | /sunrmcp:AllowDuplicates | /wsrmp:RMAssertion/wsp:Policy/wsrmp:DeliveryAssurance/wsp:Policy/wsrmp:AtLeastOnce |
| InOrder delivery | Messages from each individual Sequence are to be delivered in the same order they have been sent by the Application Source. The requirement on an RM Source is that it MUST ensure that the ordinal position of each message in the Sequence (as indicated by a message Sequence number) is consistent with the order in which the messages have been sent from the Application Source. The requirement on the RM Destination is that it MUST deliver received messages for each Sequence in the order indicated by the message numbering. This DeliveryAssurance can be used in combination with any of the AtLeastOnce, AtMostOnce or ExactlyOnce assertions, and the requirements of those assertions MUST also be met. In particular if the AtLeastOnce or ExactlyOnce assertion applies and the RM Destination detects a gap in the Sequence then the RM Destination MUST NOT deliver any subsequent messages from that Sequence until the missing messages are received or until the Sequence is closed. | /sunrmp:Ordered | /wsrmp:RMAssertion/wsp:Policy/wsrmp:DeliveryAssurance/wsp:Policy/wsrmp:InOrder |
| Flow Control | Enables or disables the flow control feature. When enabled, this feature works in conjunction with the Max Buffer Size setting to determine the maximum number of messages for sequence that can be stored at the endpoint awaiting delivery to the application. Messages may have to be withheld from the application if ordered delivery is required and some of their predecessors have not arrived. If the number of stored messages reaches the threshold specified in the Max Buffer Size setting, incoming messages belonging to the sequence are ignored. | /net30rmp:RmFlowControl | /net30rmp:RmFlowControl |
| Maximum Flow Control Buffer Size | If Flow control is enabled, this value specifies the number of request messages that will be buffered in the RM session. The default setting is 32. For more information, see the description of the Flow Control option. | /net30rmp:RmFlowControl/net30rmp:MaxReceiveBufferSize | /net30rmp:RmFlowControl/net30rmp:MaxReceiveBufferSize |
| Maximum concurrent RM sessions | Specifies how many concurrently active RM sessions (measured based on inbound RM sequences) the SequenceManager dedicated to the WS Endpoint accepts before starting to refuse new requests for sequence creation. | N/A | /metro:MaxConcurrentSessions |
| Reliable Messaging Persistence | Specifies whether the runtime should use persistent sequence and message storage or not. | N/A | /metro:Persistent |
| Sequence manager maintenace task execution period | Specifies the period (in milliseconds) of a sequence maintenance task execution. Sequence maintenance task takes care of terminating inactive sequences and removing the terminated sequences from the sequence repository. | N/A | /metro:MaintenanceTaskPeriod |
This information is now available via official Metro User Guide too.
Intercepting web service calls with a custom Metro Tube
In this blog, I am going to show you how to write a custom tube for intercepting web service messages and how to configure Metro to route all the web service traffic through this custom tube.
As you may know, the upcoming Metro v2.0 release will quietly introduce a new "Declarative tubeline assembler" feature that lets advanced Metro users to write their own Tubes and specify custom tubelines for web services in their applications. The reason why we are not advertising this feature is that we feel there's still some important work to be done in terms of consolidating all existing Metro configuration files and stabilizing some internal APIs enough to expose them publicly. So, before I continue with the blog that is clearly targeted at developers who like to live on the edge of new technologies, I need to clearly state the following "Safe Harbor" Statement:
Metro APIs discussed in this blog post are internal to Metro, evolving and may change in the future Metro releases without a prior warning. Use it at your own risk.
Good, so now that you have been properly warned (and scared), we can finally continue. As I have said, Metro 2.0 has this notion of declarative approach to assembling the web service processing tubeline, which you can imagine as a set of low-lever (and thus powerful) filters that are applied to each SOAP request/response that flows to/from a web service endpoint or its client. The standard set of tubes that comes with Metro takes care for all run-time processing including security, reliability, transactions, message validation, XML-Java and Java-XML transformations, business logic invocation etc.
The concept of tubes has been there in Metro for a quite a long time now. And, as I mentioned before, the concept is VERY powerful - many times more powerful than the standard JAX-WS handlers. Leveraging this concept at a user level was however difficult as there was no easy way how to insert a custom tube into a web service endpoint tubeline and thus effectively override the default Metro tubeline. The declarative tubeline assembler has changed that. It is now possible to write your own tube factories and use them in customizing the processing tubeline on the endpoint level basis. Let's have a look at one of the potential use cases.
In this use case, I am about to develop an interceptor capable of intercepting all calls to all web service endpoints deployed on the application server instance regardless of the application in which they are deployed. I am using GlassFish v2 with latest build of Metro v2.0 installed on it.
I will start with a simple "echo" test service application. By invoking this service I will be able to verify that my interceptor kicks in and really works. The code of the service is pretty simple and straightforward:
@WebService() public class Echo { /** * Web service operation */ @WebMethod(operationName = "echoMessage") public String echoMessage(@WebParam(name = "message") final String message) { return "Echoed: " + message; } }
Once the service is deployed I can create the client application to invoke it. I am using NetBeans web project and for simplicity I embed the service invocation code directly to the index.jsp page. The code looks like this:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <h1>Hello World!</h1> <%-- start web service invocation --%><hr/> <% try { com.sun.metro.samples.tubeinterceptor.testservice.EchoService service = new com.sun.metro.samples.tubeinterceptor.testservice.EchoService(); com.sun.metro.samples.tubeinterceptor.testservice.Echo port = service.getEchoPort(); String result = port.echoMessage("Hello"); out.println("Result = " + result); } catch (Exception ex) { out.println("Exception = " + ex.toString()); } %> <%-- end web service invocation --%><hr/> </body> </html>
When I select "Run" from the client's project context menu, the project gets deployed and the index page gets displayed with the result of invocation:
So far, it is a simple WS sample, nothing new, nothing special. Let's start with the interesting stuff then - I am going to create a new project containing my interceptor.
To intercept the Metro messages, first I need to create a custom implementation of the JAX-WS Tube interface. I will extend my implementation from AbstractFilterTubeImpl class. This class provides some common logic and provides default implementations for methods in the Tube interface. In my implementation I am just adding some logging messages that inform that the interceptor has been invoked. Here's the code:
final class InterceptorTube extends AbstractFilterTubeImpl { private static final Logger logger = Logger.getLogger(InterceptorTube.class.getName()); static enum Side { Client, Endpoint } private final Side side; private InterceptorTube(InterceptorTube original, TubeCloner cloner) { super(original, cloner); this.side = original.side; } @Override public InterceptorTube copy(TubeCloner cloner) { return new InterceptorTube(this, cloner); } InterceptorTube(Tube tube, Side side) { super(tube); this.side = side; } @Override public NextAction processRequest(Packet request) { // TODO: place your request processing code here logger.info(String.format("Message request intercepted on %s side", side)); return super.processRequest(request); } @Override public NextAction processResponse(Packet response) { // TODO: place your response processing code here logger.info(String.format("Message response intercepted on %s side", side)); return super.processResponse(response); } @Override public NextAction processException(Throwable throwable) { // TODO: place your error processing code here logger.info(String.format("Message processing exception intercepted on %s side", side)); return super.processException(throwable); } @Override public void preDestroy() { try { // TODO: place your resource releasing code here } finally { super.preDestroy(); } } }
Of course, your interceptor can do something really fancy with the intercepted messages; it can filter or modify the payload, process message headers, add new message headers, change or fork processing flow, your imagination is the only limit.
The next step is to implement a new TubeFactory class that will be used by Metro run-time code to instantiate my InterceptorTube during the tubeline creation. Again, here's the code:
public class InterceptorTubeFactory implements TubeFactory { private static final Logger logger = Logger.getLogger(InterceptorTubeFactory.class.getName()); public Tube createTube(ClientTubelineAssemblyContext context) throws WebServiceException { logger.info("Creating client-side interceptor tube"); return new InterceptorTube(context.getTubelineHead(), InterceptorTube.Side.Client); } public Tube createTube(ServerTubelineAssemblyContext context) throws WebServiceException { logger.info("Creating server-side interceptor tube"); return new InterceptorTube(context.getTubelineHead(), InterceptorTube.Side.Endpoint); } }
Normally, you would probably want to implement some logic that would decide whether or not the tube should be created, based on a presence of a particular WebServiceFeature or any other information from the tubeline assembly context, but in this scenario it is sufficient to just create a new interceptor tube whenever the factory method is invoked.
Now having both - tube and its factory - in place, I need to wire them with the Metro run-time. First of all, I have to create a custom metro.xmlconfig file that includes my interceptor tube factory and place it under META-INF directory inside my interceptor library jar. The easiest way to do that is to copy and modify the default Metro config file (metro-default.xml) which resides in webservices-rt.jar. Here's the resulting config file:
<metro xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://java.sun.com/xml/ns/metro/config' version="1.0"> <tubelines default="#intercepted-tubeline"> <tubeline name="intercepted-tubeline"> <client-side> <tube-factory className="com.sun.xml.ws.assembler.jaxws.TerminalTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.HandlerTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.ValidationTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.MustUnderstandTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.MonitoringTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.AddressingTubeFactory" /> <tube-factory className="com.sun.xml.ws.tx.runtime.TxTubeFactory" /> <tube-factory className="com.sun.xml.ws.rx.rm.runtime.RmTubeFactory" /> <tube-factory className="com.sun.xml.ws.rx.mc.runtime.McTubeFactory" /> <tube-factory className="com.sun.xml.wss.provider.wsit.SecurityTubeFactory" /> <tube-factory className="com.sun.xml.ws.rx.testing.PacketFilteringTubeFactory" /> <tube-factory className="com.sun.metro.samples.tubeinterceptor.tube.InterceptorTubeFactory" /> <tube-factory className="com.sun.xml.ws.dump.MessageDumpingTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.TransportTubeFactory" /> </client-side> <endpoint-side> <tube-factory className="com.sun.xml.ws.assembler.jaxws.TransportTubeFactory" /> <tube-factory className="com.sun.xml.ws.dump.MessageDumpingTubeFactory" /> <tube-factory className="com.sun.metro.samples.tubeinterceptor.tube.InterceptorTubeFactory" /> <tube-factory className="com.sun.xml.ws.rx.testing.PacketFilteringTubeFactory" /> <tube-factory className="com.sun.xml.wss.provider.wsit.SecurityTubeFactory" /> <tube-factory className="com.sun.xml.ws.rx.mc.runtime.McTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.AddressingTubeFactory" /> <tube-factory className="com.sun.xml.ws.rx.rm.runtime.RmTubeFactory" /> <tube-factory className="com.sun.xml.ws.tx.runtime.TxTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.MonitoringTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.MustUnderstandTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.HandlerTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.ValidationTubeFactory" /> <tube-factory className="com.sun.xml.ws.assembler.jaxws.TerminalTubeFactory" /> </endpoint-side> </tubeline> </tubelines> </metro>
As you can see, I have inserted a reference to my custom tube factory into the chain of default Metro tube factories. The place of insertion depends on what you want to achieve and at what stage of processing you want to intercept. My factory is inserted very close to transport which lets my interceptor tube to see message in the same form as they are transferred over the wire. And now to the last piece. I want to make sure that Metro loads my config file for all web services in all applications deployed in the application server. Since Metro uses a classloader to load config files, I need to find a proper location where to put my library and perhaps modify the application server's classpath. As one may expect, the proper location for my library in GlassFish v2 is in ${AS_HOME}/lib, where the run-time Metro jar resides. Also I need to make sure that my jar gets loaded before Metro jars, so I need to update GlassFish classpath settings and add my library to the classpath prefix:
And now I just properly configure the logging levels to be able to see the relevant logging messages in the server log...
com.sun.metro.samples.tubeinterceptor.tube -> INFO javax.enterprise.resource.webservices.assembler -> FINER javax.enterprise.resource.webservices -> INFO
...and restart the server. When I run the client again, in the server log I can see the following log messages which verify that my interceptor has been invoked for both, client and endpoint side in both request and response directions:
... deployed with moduleid = TestServiceApp Metro monitoring rootname successfully set to: com.sun.metro:pp=/,type=WSEndpoint,name=/TestServiceApp-EchoService-EchoPort Default metro-default.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/webservices-rt.jar!/META-INF/metro-default.xml' Application metro.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/InterceptorLib.jar!/META-INF/metro.xml' Creating server-side interceptor tube deployed with moduleid = TestClientApp Trying to load 'metro-default.xml' via parent resouce loader 'com.sun.xml.ws.client.ClientContainer$1@13ffd111' Default metro-default.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/webservices-rt.jar!/META-INF/metro-default.xml' Trying to load 'metro.xml' via parent resouce loader 'com.sun.xml.ws.client.ClientContainer$1@13ffd111' Application metro.xml configuration file located at: 'jar:file:/Users/m_potociar/dev/glassfish/glassfishv2-ur2/lib/InterceptorLib.jar!/META-INF/metro.xml' Creating client-side interceptor tube Message request intercepted on Client side Message request intercepted on Endpoint side Message response intercepted on Endpoint side Message response intercepted on Client side ...
And that's it. I have successfully created and configured a custom interceptor tube that intercepts all web service message processing on the server. The sample ZIP containing all three NetBeans projects is available here.
WS-Policy the main force behind WSIT
These days we are focusing on fine-tuning quality of WSIT 1.0 that will be released soon. If you want do play with it, feel free to download the latest build. As there are no open issues in policy engine, I found some time to create a screencast (actually this is my first screencast ever!) about the WS-Policy and how it is used in WSIT. Enjoy!
Technorati:Interoperability,
Java,
J2EE,
Open Source,
Web Services,
WSIT,
WS-Policy
Got a question about WSIT? Ask the experts!
Hi there! It has been some time since my last entry on this blog. In my blog entries I usually describe topics I want to. Now I would like to let you know, that if you have a question about WSIT that you would like to ask, please send it to us during the week of April 30 at the next Ask the experts session dedicated to WSIT and project Tango. Your questions will be answered by Harold Carr, Arun Gupta and me.
Technorati:Interoperability,
Java,
J2EE,
Open Source,
Web Services,
WSIT,
WS-Policy
A role of WS-Policy in WSIT
In my last blog I discussed the rise of a new era in SOA development with the use of Java Web Services technology powered by the implementation of WS-* specifications. Today, I would like to write few lines about policy metadata - the real core of these new technologies.
Policy language and its usage is described in two separate specifications (which have already been submitted for standardization by W3 Consortium):
- Web Services Policy Framework (WS-Policy) specification and
- Web Services Policy Attachment (WS-PolicyAttachment) specification
These two specifications bind all other WS-* specifications together into one complex solution.
The main goal of the WS-Policy specification is to introduce a flexible and extensible way of expressing and understanding the capabilities, requirements and general web service properties. In other words, WS-Policy provides a web services policy language together with domain-neutral rules as to how to process and interpret these policies. To complement the WS-Policy specification - which is strictly focused on defining policy language - WS-PolicyAttachment completes the whole picture by defining the means of attaching actual policy expresions to policy subjects to which these expressions apply. This second specification also describes the way how to use policies in the context of web services description mechanisms – WSDL and UDDI.
WSIT introduces its own policy engine and I am a part of a team working on its design and implementation. We do our best to make sure the implementation is interoperable and - of course - fully compliant with WS-Policy and WS-PolicyAttachment specifications. Currently, we are still in a development phase and there's much to be tested and polished yet. But we have already achieved success in supporting all main use case scenarios as well as having demonstrated our ability to interoperate with policy engines from other vendors at several Interoperability workshops.
...and if you want to know even more about policy, don't forget to check the blogs of Jakub and Fabian - my project Tango team-mates.
Technorati:
Interoperability,
Java,
J2EE,
open source,
web services,
wsit,
ws-policy

