 |
Welcome to the Interoperability Blog, Part the Fourth, wherein we talk about that most obvious of interopability choices, the ubiquitous “Web service”. The basics of the Web service are well known by this point: data sent by a client is transformed into an XML message in the SOAP format over HTTP to a listener, which processes the incoming data, generates an XML response again in SOAP format, and returns that to the sender for consumption. Frequently the service is described using another XML-based format, WSDL, which can be used to generate proxies using a code-generation tool of the client’s choice. |
A full description of how to use Web services is well beyond the scope of this blog entry; what we’ll do instead is focus on the practical interaction between Java’s latest standard for defining Web services, the Java API for XML Web Services (JAX-WS) and Microsoft’s Windows Communication Framework (WCF). In this particular case, we’re going to use BEA’s SimpleService example (which ships as part of the WebLogic Server 9.2 installation available for free download from BEA) as the service to talk to, and WCF as the client.
We begin by defining a simple service using JAX-WS metadata annotations, taking a standard Java class and using those annotations to define how the Web service is built. We’ll need the JAX-WS annotations:
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
Plus WebLogic provides an annotation type used to help describe the HTTP binding within the WebLogic Server:
import weblogic.jws.WLHttpTransport;
We define the class itself, using three class-level annotations to describe the way in which it should be exposed as a service. The class will be a simple one, with a single method that takes a String and returns it slightly modified (the ubiquitous “echo” service):
public class SimpleImpl
{
public String echoMessage(String echoMsg)
{
System.out.println("sayHello: " + echoMsg);
return "Here is the message: '" + echoMsg + "'");
}
}
In this case, I’ve removed the annotations so we can examine them individually. We’ll start with the simplest, the WebMethod annotation that we apply to the echoMessage method:
@WebMethod()
public String echoMessage(String echoMsg)
{
System.out.println("sayHello: " + echoMsg);
return "Here is the message: '" + echoMsg + "'");
}
This simply tells WebLogic (or any other JAX-WS-compliant container) that this method should be exposed as an operation in the WSDL generated by the container. The method is simple enough to not require any customization, so we’re done here.
Next, we need to annotate the class with a couple of annotations to describe how the class itself should be made available; first, we use the WebService annotation to indicate the name and namespace (which should always be specified) of the service:
@WebService(name="Simple",
targetNamespace="http://example.org")
public class SimpleImpl
{
@WebMethod
public String echoMessage(String echoMsg)
{
System.out.println("sayHello: " + echoMsg);
return "Here is the message: '" + echoMsg + "'");
}
}
After that, we use the SOAPBinding annotation to describe how the service should be described in WSDL using SOAP; in this particular case (as should be the case for all new Web services), the binding specified uses document/literal encoding, as opposed to the deprecated rpc/encoded style that was popular several years ago. This is good, because WCF also defaults to doc/literal, so WCF isn’t going to have to go through weird gyrations to support our service when it generates proxies:
@WebService(name="Simple",
targetNamespace="http://example.org")
@SOAPBinding(style=SOAPBinding.style.DOCUMENT,
use=SOAPBinding.Use.LITERAL,
parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
public class SimpleImpl
{
@WebMethod
public String echoMessage(String echoMsg)
{
System.out.println("sayHello: " + echoMsg);
return "Here is the message: '" + echoMsg + "'");
}
}
Finally, as an optional benefit, WebLogic provides the WLHttpBinding annotation to describe how this class should be exposed in the WL9.2 container, in this case in a servlet context named “jws_basic_simple”, and with a service name of “SimpleService”. These names can both be found in the URL by which we retrieve the service, later:
@WebService(name="Simple",
targetNamespace="http://example.org")
@SOAPBinding(style=SOAPBinding.style.DOCUMENT,
use=SOAPBinding.Use.LITERAL,
parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
@WLHttpBinding(contextPath="jws_basic_simple",
serviceUri="SimpleService")
public class SimpleImpl
{
@WebMethod
public String echoMessage(String echoMsg)
{
System.out.println("sayHello: " + echoMsg);
return "Here is the message: '" + echoMsg + "'");
}
}
At this point, compile the code, deploy to the server, and the service is ready to go; for a test, point a browser at the URL by which WebLogic (as many other Web service containers do) exposes the generated WSDL by tacking “WSDL” as a query parameter at the end of the URl, as in: “http://localhost/jws_basic_simple/SimpleService?WSDL”.
What should be returned is a rather incomprehensible collection of XML that most of the time can be safely ignored. (WSDL is a complicated specification that, in theory, you never need to read. Unfortunately, there’s theory, and then there’s practice, but we’ll leave that aspect of practice alone for another day.)
At this point, now that we know that we can retrieve WSDL, we can point the WCF svcutil at the URL, and let it build us a proxy to use from a client App. (Just for grins, and to make a few heads spin, we’re going to write the client in Visual Basic.) Run svcutil like so:
svcutil http://localhost:7001/jws_basic_simple/ServiceSimple?WSDL
It creates two files in the current directory, output.config, a .NET configuration file that WCF will examine for details on how to talk to the service, and SimpleImplServiceDefinitions.cs, the actual proxy itself. Write some simple client code to :
Option Strict Off
Option Explicit On
Namespace WCFExample
Public Class Client
Public Shared Sub Main()
Dim cl As New SimpleClient
Console.WriteLine("echo returns: {0}", _
cl.sayHello("Greetings from WCF"))
End Sub
End Class
End Namespace
Compile this with vbc, making sure to reference the System.ServiceModel and System.Runtime.Serialization assemblies (the easiest way being to copy them out of the WCF installation directory into the current directory and reference them using “/r” from the command-line). Make sure the WebLogic Server is running (start it by start it by selecting Start : All Programs : BEA Products : Examples : WebLogic Server : Start Examples Server if it’s not). Rename the output.config file to be named “Client.exe.config” (assuming the Client class is in Client.vb), and run:
echo returns: Here is the message: 'Greetings from WCF'
Voila—you have a dirt-simple Web service. Note that we’re ignoring some significant issues, such as how to pass data types more sophisticated than a String, but for now, we have a working Web service.