Friday Oct 26, 2007
Friday Oct 26, 2007
By Harold Carr
The March 2007 Tech Tip, Securing Web Services Using WSIT introduced Web Services Interoperability Technology (WSIT), an implementation of open web services technologies that enables interoperability between Java EE and .NET. WSIT along with Java API for XML-Based Services (JAX-WS) comprise the stack of web services technologies in GlassFish v2. The web services stack in GlassFish v2 is called Project Metro or Metro for short. Metro is built into Sun Java System Application Server 9.1. Metro also runs in other containers such as Tomcat.
WSIT, also called Project Tango, includes features that enable advanced web service interoperability with the .NET Windows Communication Foundation (WCF), a set of technologies for building and running connected systems. WSIT also addresses key aspects of web services interoperability such as reliable messaging, transaction handling, and security. What this means is that any combination of Metro-based and WCF-based clients and services can interoperate with support for reliable messaging, transaction handling, and security.
To ensure that the stack of WCF and Metro technologies interoperate properly and provide the proper support, Microsoft and Sun engineers run comprehensive interoperability tests called "plug-fests". You can find out more about the plug-fests, including the scenarios that are tested, by viewing the Web Services Interoperability Plug-Fest Home Page. All tests pass in the current levels of .NET and Metro: .NET 3.0 and Metro 1.0.
In this tip, you'll build and run an interoperability test in which a Metro-based client communicates with a WCF-based service. A package that contains the code for the interoperability test accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package).
A Reliable One-Way Interoperability Test
The interoperability test you'll build and run in this tip is a "reliable one-way test." This type of test is described in "Scenario #1.1. One-Way Anonymous Client" in the document WCF (Indigo) Introperability Lab: Reliable Messaging. This is a typical enterprise use case where a client sends data to a service for processing but does not wait for a response -- that's why it's called "One-Way". However, the client wants assurances that the messages are received by the service.
The message flow in this test is as follows:
Reliable Messaging Operation
|
CreateSequence protocol
message.
CreateSequenceResponse protocol message.
SequenceAcknowledgement identifying what messages have
been received. The service does this without having to wait
for the business logic to complete processing.
LastMessage protocol messages to be sent.
SequenceAcknowledgement protocol
message indicating which messages have been received.
LastMessage. The LastMessage-resend
sequence repeats until all messages have been received by the
service. (That is not exercised in this tip.)
TerminateSequence protocol message.
Metro-Based Client
Let's start by creating the Metro-based client. You can view the
source code for the client in the file ReliableOneWay.java. Here
is a snippet of the source code in that file:
package reliableoneway.client; import org.tempuri.IPing; import org.tempuri.PingService; public class ReliableOneWay { public ReliableOneWay() {} public static void main(final String[] args) { try { PingService service = new PingService(); IPing port = service.getCustomBindingIPing(); msg(null, "BEFORE FIRST MESSAGE"); msg(port, "FIRST MESSAGE"); msg(port, "SECOND MESSAGE"); msg(port, "THIRD MESSAGE"); msg(null, "TERMINATE"); ((com.sun.xml.ws.Closeable)port).close();
Using a reliable messaging (RM)-based web service is no
different than using any SOAP-based web service. First you
create a proxy to the service (see the section Running
wsimport). Here, the service is PingService, which is
defined in the WSDL (see the section WSDL). Then you
obtain a port defined in that service, in this case,
CustomBindingIPing. Then you can send application messages to
that service by calling the ping method on the port. Here the
method msg calls port.ping(String) when port is not null. In
this example, three messages are sent.
These steps are the same steps you would take to use any
SOAP-based web service. The one additional step is calling close
on the port. This signals Metro to terminate the RM channel if
the service is using RM. In general, it's a good idea to close
ports when they are no longer needed. This enables Metro to
reclaim any resources used by that port.
WSDL
You generate the PingService proxy from the Web Service
Definition Language (WSDL) file for the service. You can find
the WSDL for the service by going to the Web Services
Interoperability Plug-Fest Home Page. Click on the "RM
Endpoints (WS-Addressing 1.0)" link. Then click on the "OneWay.svc"
link. The address of the WSDL is http://131.107.72.15/ReliableMessaging_Service_WSAddressing10_Indigo/OneWay.svc?wsdl.
The WSDL file is quite large because it is used for other test
scenarios besides scenario 1.1, that is, scenarios that involve
security. The port that this tip uses is named
CustomBinding_IPing. It references the following policy binding:
<wsp:Policy wsu:Id="CustomBinding_IPing_policy"> <wsp:ExactlyOne> <wsp:All> <wsrm:RMAssertion> <wsrm:InactivityTimeout Milliseconds="600000"/> <wsrm:AcknowledgementInterval Milliseconds="200"/> </wsrm:RMAssertion> <wsaw:UsingAddressing/> </wsp:All> </wsp:ExactlyOne> </wsp:Policy>
The RMASssertion in the policy binding specifies an RM policy
assertion for the service. Within the assertion, the
InactivityTimeout parameter sets then interval of time that the
service remains inactive before closing. The
AcknowledgementInterval parameter sets the interval of time that
a destination waits before sending an acknowledgment to the
message source on reliable channels. You can find out more about
RM policy assertions in the WS-ReliableMessaging Policy specification. The
main point here is that including the RMAssertion in the WSDL
and then referencing it in the CustomBinding_IPing binding that
is used in the CustomBinding_IPing port causes operations on
that port to use reliable messaging.
The Structure of the Test
In this tip, you'll use ant to create the proxy, compile the code, and run the client test. You can also use NetBeans -- for more details, see the WSIT Tutorial.
If you haven't already done so, download and install GlassFish v2.
Then download the sample application for the tip and extract its contents. You should now see the newly extracted
directory as <sample_install_dir>/interop, where
<sample_install_dir> is the directory where you installed the
sample application. For example, if you extracted the contents
to C:\ on a Windows machine, then your newly created directory
should be at C:\interop.
Although this tip demonstrates only one scenario, the directory structure below the interop directory is set up such that common artifacts are factored out to be used with other tests. The directory structure is as follows:
common.xml rm build.xml netbeans ReliableOneWayService ... src reliableoneway build.props build.xml client ReliableOneWay.java ... server etc EchoServiceRMCustomOnly.wsdl ReliableOneWay.xsd
At the top level of the directory structure, the build.xml file
imports common.xml, where the real action takes place, and
defines one target: run-reliableoneway. The run-reliableoneway
file cleans the build directory. It then:
wsimportReliableOneWay.java codeRunning wsimport
Here is the snippet of code in the build.xml file that runs wsimport:
<target name="run-reliableoneway"> <ant target="run-wsimport" antfile="src/reliableoneway/build.xml" inheritall="false"/>
Notice that it uses the build.xml file in the
rm/src/reliableoneway directory. That file, in turn, uses the
build.props file in the rm/src/reliableoneway directory. Here is
the contents of build.props:
test.wsdl= http://131.107.72.15/ReliableMessaging_Service_ ... client.dir=client className=reliableoneway.client.ReliableOneWay
The build.props file is a property file that defines test.wsdl
to be the location of the WSDL file of the service. It also sets
two other parameters used by common.xml so it can be used for
other tests that are not covered in this tip.
The build.xml file in the rm/src/reliableoneway directory then
uses the run-wsimport target from the common.xml file.
The wsimport task fetches the WSDL file specified in parameter
test.wsdl. It then generates the proxy code into the
rm/build/classes/org/tempuri directory, and the data schema
class into the
rm/build/classes/com/microsoft/schemas/_2003/_10/serialization
directory. This class is large because it is a superset of all
data used by all the interoperability test scenarios.
Compiling the Code and Running the Test
The final two steps in the run-reliableoneway target are:
<ant target="compile" antfile="src/reliableoneway/build.xml" inheritall="false"/> <ant target="run-tests" antfile="src/reliableoneway/build.xml" inheritall="false"/>
The compile step runs the Java Programming Language compiler,
javac, on the developer-written code, ReliableOneWay.java. The
run-tests step executes the main test class as specified in the
className parameter set in build.props file. In this case,
className is set to reliableoneway.client.ReliableOneWay.
Note that sysproperty is set in the run-tests target of the common.xml file.
<sysproperty key="com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump" value="true"/>This enables the client to send and receive messages.
Setting Environment Variables
Four ant parameters are set from environment variables (here shown in bash syntax):
export PROXY_HOST=-DproxyHost=my.proxy.example.com export PROXY_PORT=-DproxyPort=8765 export METRO_HOME=/glassfish/b58g export TEST_HOME=~/metro-wcf-interop-tests/rmIf you are not behind a firewall then explicitly set PROXY_* to nothing:
export PROXY_HOST= export PROXY_PORT=
Set METRO_HOME to the top-level installation direction of
GlassFish. That setting is used to locate Metro JAR files such
as webservices-rt.jar and webservices-tools.jar in
$METRO_HOME/lib. (If you are using Tomcat or another web
container you need to update common.xml to correctly reference the
location of the Metro JAR files.)
Executing the Test
If you haven't already done so, start GlassFish by entering the following command:
$METRO_HOME/bin/asadmin start-domain domain1Then change to the $TEST_HOME directory and enter the following
command on the command line:
ant Note: This test is designed to run with JDK 5. If you want to run the test with JDK6, you need to use the Java Endorsed Standards Override Mechanism.
Test Results
The output of the test should look similar to the content in the example-output-wcf-endpoint.txt file in the
rm/src/reliableoneway/client directory.
You can ignore all the WARNING messages at the beginning of the
test run, such as the following:
[java] WARNING: WSP0075: Policy assertion "{http://docs.oasis-open.org/ws-rx/wsrmp/200702}RMAssertion" was evaluated as "UNKNOWN".
These warnings are from the numerous policies for bindings in
the WSDL that are not used in this test. Similarly, you can
ignore the non-standard SOAP 1.2 binding warning messages in
the run-wsimport step.
In the output, search for BEFORE FIRST MESSAGE. This is printed
to the console just before the first invocation of port.ping.
When port.ping is called, the RM infrastructure holds the
application message back -- it first establishes a reliable
channel by sending a CreateSequence protocol message.
Content-Type: application/soap+xml;charset="utf-8"; action="http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence"
The CreateSequenceResponse protocol message returned from the
service contains an Identifier that will be used to identify the
reliable channel.
After the reliable channel is established, the application
message is sent containing <Text>FIRST MESSAGE</Text> in the
Body element. The header contains a Sequence element that
contains the reliable channel Identifier along with
a MessageNumber, in this case the number 1.
<S:Envelope xmlns:..."><S:Header>... <ns2:MessageNumber>1</ns2:MessageNumber>... </S:Header>...<Text>FIRST MESSAGE</Text>... </S:Envelope>
Because this is a oneway message, the response to this message
contains an empty Body. The header contains
a SequenceAcknowledgement element that contains the Identifier
and an AcknowledgementRange with attributes Lower and Upper,
in this case the numbers 1 and 1 respectively.
<s:Envelope xmlns:...><s:Header>... <r:SequenceAcknowledgement> <r:Identifier>urn:...</r:Identifier> <r:AcknowledgementRange Lower="1" Upper="1"/>... </r:SequenceAcknowledgement>... </r:SequenceAcknowledgement>This means the lowest message number received by the service was 1 and the highest message number was 1. When there are gaps in the acknowledgement range the client RM infrastructure resends lost messages. It releases message copies (for garbage collection) after those messages have been acknowledged.
Two more application messages are sent with MessageNumbers 2 and
3 respectively.
<S:Envelope xmlns:...><S:Header>... <ns2:MessageNumber>2</ns2:MessageNumber>... </S:Header>...><Text>SECOND MESSAGE</Text>... </S:Envelope> <S:Envelope xmlns:...><S:Header>... <ns2:MessageNumber>3</ns2:MessageNumber>... </S:Header>...<Text>THIRD MESSAGE</Text>... </S:Envelope>
The responses contain AcknowledgementRanges with Lower/Upper
attributes 1/2 and 1/3 respectively.
TERMINATE is printed to the console then the port.close() is
called. This causes the RM infrastructure to send a message with
an empty Body. The header of this message contains a Sequence
element with the Identifier and a MessageNumber of 4. The header
also contains an Action element specifying LastMessage to
let the service know the reliable channel is going to be closed.
<S:Envelope xmlns:...><S:Header>... <ns2:MessageNumber>4</ns2:MessageNumber> <ns2:LastMessage/>... </ns2:Sequence>... <Action xmlns=..."> http://schemas.xmlsoap.org/ws/2005/02/rm/LastMessage </Action>...
The service responds with a SequenceAcknowledgement containing
AcknowledgementRange 1/4 indicating all messages have been
received.
<s:Envelope xmlns:...><s:Header> <r:SequenceAcknowledgement>...> <r:AcknowledgementRange Lower="1" Upper="4"/>... </r:SequenceAcknowledgement>... </s:Envelope>If messages are missing the client resends them.
After the client RM infrastructure sends LastMessage and
receives a SequenceAcknowledgement response indicating that all
messages have been received, it sends a message with an Action
header element containing TerminateSequence.
Content-Type: application/soap+xml;charset="utf-8"; action= "http://schemas.xmlsoap.org/ws/2005/02/rm/TerminateSequence"
This lets the service know that it can release all resources
associated with the reliable channel identified by the Identifier
element. The service responds with an HTTP 202 response.
null: HTTP/1.1 202 AcceptedSummary
This tip showed you how to build and run a reliable messaging test in which a Metro-based client communicates with a WCF-based service, in other words, a public WCF endpoint.
Of course, you can also run the same interoperability test or other interoperability tests using a WCF-based client or a Metro-based service. Running the tests with different combinations of Metro-based and WSF-based clients and servers should enable you to verify interoperability between Metro and .NET. It will also enable you to become a contributor to the open source Metro project and test the interoperability of the code you contribute.
About the Author
Harold Carr is the engineering lead for enterprise web services interoperability at Sun Microsystems. Previous to this role, Harold was responsible for RMI-IIOP load-balancing and fail-over in the Sun Java System Application Server. He designed the core architecture used in Sun's CORBA ORB and in the JAX-RPC 2.0 reference implementation and the scalable socket communications architecture used in SJSAS HTTP and IIOP remoting. Harold helped write the OMG Portable Object Adapter specification and was chairperson of the OMG Portable Interceptor specification. Previous to Sun, he did distributed computing research at Hewlett-Packard Research Laboratories and Schlumberger Research Laboratories, was Chief Architect of Visual Lisp technology at Autodesk, and was a logic simulation consultant for Cirrus Logic. He holds a Ph.D., in Computer Science from the University of Utah.