Monday Feb 01, 2010

Screencast #29: Web service using NetBeans 6.8 and GlassFish v3 (Accessing database in business method)

GlassFish v3 comes bundled with Metro - a secure, reliable, transactional, and .NET interoperable Web services stack. Metro is compliant with JAX-WS and provides additional quality of service attributes that can be easily enabled using NetBeans IDE.

This blog contains a screencast that shows how to create a simple Web service using NetBeans 6.8, implement the business logic by accessing a database table using Java Persistence API, and deploy on GlassFish v3.

Please post your questions to users@glassfish.dev.java.net.

Several other screencasts on GlassFish v3and related topics are available here.

Technorati: netbeans jax-ws metro webservice glassfish jpa database

Tuesday Jan 12, 2010

TOTD #117: Invoke a JAX-WS Web service from a Rails app deployed in GlassFish

A user on GlassFish Forum tried invoking a JAX-WS Web service from a Rails application and faced some issues. This Tip Of The Day (TTOD) will discuss the different approaches and shows their current status.

A Rails app can be deployed on GlassFish in 3 different ways:

  1. Directory Deployment in GlassFish v3 Server - TOTD #72 explains how to deploy a trivial Rails application (with just a scaffold) on GlassFish v3 server. Even though the blog uses a Rails application, any Rack-based application can be deployed on the server. This server is also the Reference Implementation for Java EE 6 and can also run Grails and Django applications.
  2. Directory Deployment using light-weight GlassFish Gem - GlassFish Gem is a light-weight version of the full-blown server and is stripped to run, just like the server, any Rack-based application such as Merb, Rails, and Sinatra. TOTD #70 shows how to deploy the same application using GlassFish Gem.
  3. WAR file in GlassFish v2.x or v3 - TOTD #73 explains how to deploy a Rails application as WAR file on GlassFish v2. The JNDI connection pooling part of the blog may be skipped to simplify the steps but the concepts are still valid. TOTD #44 shows how to do JNDI connection pooling for GlassFish v3. As GlassFish v2 has in-built support for session replication, TOTD #92 demonstrate how Rails application can leverage that functionality.

Now lets get to the issue reported by the user using these 3 deployment models.

First, lets deploy a simple Web service endpoint and generate a JAR file of the client-side artifacts:

  1. This blog will use a simple Web service as defined in screencast #ws7. The Web service endpoint looks like:
    package server;
    
    import javax.jws.WebService;
    
    /\*\*
     \* @author arungupta
     \*/
    @WebService()
    public class HelloService {
     public String sayHello(String name) {
     return "Hello " + name;
     }
    }
    

  2. Generate Web service client-side artifacts as:
    ~/samples/v3/rails/webservice/tmp >wsimport -keep http://localhost:8080/HelloWebService/HelloServiceService?wsdl
    parsing WSDL...
    
    
    generating code...
    
    
    compiling code...
    
  3. Create a Web service client jar file as:
    jar cvf wsclient.jar ./server
    

Now lets write a Rails application and invoke this Web service:

  1. Create a simple Rails application as:
    jruby -S rails webservice
    

    Optionally you may specify "-d mysql" to use MySQL database. Or better un-comment the following line:
    # config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
    

    in "config/environment.rb" as no database interaction is required.
  2. Create a controller and view as:
    jruby script/generate controller home index
    
  3. Update the Controller in "app/controllers/home_controller.rb" as:
    include Java
    
    class HomeController < ApplicationController
     def index
     service = Java::server.HelloServiceService.new
     port = service.getHelloServicePort
    
     @result = port.sayHello("Duke")
     end
    
    end
    
  4. Change the View in "app/views/home/index.html.erb" as:
    <h1>Home#index</h1%gt;
    <p>Find me in app/views/home/index.html.erb</p>
    
    <%= @result %>
    

Now lets deploy this Web service using the 3 different deployment models mentioned above.

GlassFish v3 allows a directory-based deployment of Rails applications. This application needs to locate the Web service client classes. The "wsclient.jar" can be copied to the "lib" directory of Rails application ("webservice/lib" in our case), "domains/domain1/lib/ext" or "JRUBY_HOME/lib". The library can also be passed during deployment using "--libraries" switch. None of this approach seem to work correctly as explained in issue# 11408. So for now, invoking a JAX-WS Web service from a Rails application deployed directly on GlassFish v3 is not possible, at least until the bug is fixed.

In order to deploy the same application using GlassFish Gem, you can copy "wsclient.jar" to the "lib" directory of your Rails application. And also add the following line to "app/controllers/home_controller.rb":

require 'lib/wsclient.jar'

Alternatively you can copy it to "JRUBY_HOME/lib" directory if this Web service client is accessed my multiple applications. In this case there is no need to add any "require" statement to your Controller. Anyway, running the application as:

jruby -S glassfish

and accessing "http://localhost:3000/home/index" shows the following output:

And finally as explained in TOTD #73, bundle up your original Rails application as WAR and then deploy on GlassFish v3 as:

asadmin deploy webservice.war

Make sure to copy "wsclient.jar" to the "lib" directory of your Rails application and then Warbler will copy it to "WEB-INF/lib" of the generated WAR file. The output is shown as below:

So if you want to invoke a Metro/JAX-WS Web service from a Rails application, then run your Rails application using GlassFish Gem or deploying as a WAR file. It'll work on GlassFish v3 server when issue# 11408 is fixed.

Here are some additional links:

  • TOTD #104 also shows how popular Rails applications such as Redmine, Typo, and Substruct can be easily deployed on GlassFish.
  • Rails applications can be easily clustered using Apache + mod_proxy orĀ  nginx.

A complete archive of all the TOTDs is available here.

Technorati: totd glassfish v3 jruby rails webservice jax-ws metro

Monday Aug 24, 2009

TOTD #98: Create a Metro JAX-WS Web service using GlassFish Tools Bundle for Eclipse


Now that you've installed GlassFish Tools Bundle for Eclipse 1.1, lets use this bundle to create a simple Metro/JAX-WS compliant Web service and deploy on GlassFish. These steps will work with either Eclipse 3.4.2 or 3.5 with WTP Java EE support.

  1. Lets create a simple "Dynamic Web Project" as shown below:


  2. Name the project "HelloMetro" and take all other defaults:



    Click on "Finish" to complete the project creation.
  3. Metro allows to create a Web service from a POJO class. So let's add a POJO to the project by right-clicking on the project and selecting "New", "Class" as shown below:

      

    Specify the package name as "server", class name as "HelloService" and click on "Finish".
  4. Add a simple method to the newly generated class as:

    public String sayHello(String name) {
          return "Hello " + name + "!!";
    }
  5. Expand the project, go to "HelloService.java" in "server" package, right-click, select "Web Services", "Create Web service".
  6. Click on "Web service runtime: Apache Axis" and select "Metro (JAX-WS) Runtime" as the Web service runtime as shown below:

  7. Move the slider on the left to top. This will enable testing of the deployed Web service. The completed configuration looks like:



    and click on "Next >".
  8. Select the checkbox "Copy Metro library jars to the project" to resolve the references correctly as shown below:



    and click on "Next >". This bundles the application and deploys to GlassFish and provides an option to test the deployed Web service as shown below:



    Clicking on the "Launch" button shows the following output in the browser:



    The WSDL is hosted at "http://localhost:8083/HelloMetro/HelloServiceService?wsdl".
  9. Click on "sayHello" method, click on "Add" and enter the value as "Duke" as shown below:



    Click on "Go" and the response is shown as:



    Clicking on "Source" in the response window shows the SOAP request/response messages as shown below:

  10. Alternatively, you can click on "Finish" to complete the dialog. Then click on "Run" menu item, "Launch the Web Services Explorer" to see a screen as:



    Enter the URL of the WSDL in "WSDL URL" box as "http://localhost:8083/HelloMetro/HelloServiceService?wsdl" and click on "Go". Now you are seeing the similar screen to test the Web service within the integrated browser as shown below:


A future blog will cover how to write a database-enabled application using the bundled Dali JPA Tools and MySQL pre-registered JDBC driver.

Please send your questions and comments to users@glassfishplugins.dev.java.net.
Please leave suggestions on other TOTD that you'd like to see. A complete archive of all the tips is available here.

Technorati: totd glassfish eclipse galileo webservices metro jax-ws

Friday Oct 17, 2008

SOAP and REST - both equally important to Sun


"Sun moving away from SOAP to embrace REST" is the misleading title of an article recently published in SD Times. The article provides a good introduction to JAX-RS and Jersey. But I really wonder what motivated the author of this article to use this title. This blog, hopefully, provides a better context.

Jersey is the Reference Implementation of Java API for RESTful Web Services (JAX-RS, JSR 311) and was released earlier this week. The headline indicates that Sun is leaving SOAP and will support REST. The debate between REST and SOAP is not new and there are religious camps on both sides (even within Sun). And that's completely understandable because each technology has its own merits and demerits. But just because a new JSR aimed to make RESTful Web services easy in the Java platform is released, it does not mean Sun Microsystems is leaving existing technology in trenches.

The addition of Jersey to Sun's software portfolio makes the Web services stack from GlassFish community a more compelling and comprehensive offering. This is in contrast  to "moving away" from SOAP as indicated by the title. As a matter of fact, Jersey will be included as part of Metro soon, the Web Services stack of GlassFish. And then you can use JAX-WS (or Metro) if you like to use SOAP or JAX-RS (or Jersey) if you prefer RESTful Web services. It's all about a offering choice to the community instead of showing a direction.

Here are some data points for JAX-WS:
  • The JAX-WS 2.0 specification was released on May 11, 2006. There have been couple of maintenance releases since then and another one brewing.
  • Parts of Metro, the implementation of JAX-WS, are currently baked into GlassFish, embeddable in JBoss WS Stack, and also part of Oracle Weblogic and IBM Websphere.
  • The implementation stack is mature and used in several key customer deployments. 
  • JAX-WS is already included in Java SE 6 and hence available to a much wider audience.
  • As opposed to "moving away", JAX-WS 2.2 (currently being worked upon) will be included in Java EE 6 platform, as will Jersey be.
So I believe both SOAP and REST are here to stay, at least in the near future. And Sun Microsystems is committed to support them!

You still think Sun is moving away from SOAP ?

It seems a personal preference is interpreted as Sun's disinvestment in SOAP. It's good to have increased readership but not at the cost of misleading headlines :)

Technorati: jax-ws rest webservices metro sdtimes glassfish

Monday Aug 11, 2008

LOTD #1: Using Silverlight to access GlassFish Metro and JAX-WS Web service endpoints

Following TOTD (Tip Of The Day) pattern, I'm starting LOTD (Link Of The Day) series today. These are light-weight entries with generally a single line description and links to other blogs/articles/tips/whitepapers/screencasts/etc.

Let's start with three recent entries on MSDN that describe how to invoke Metro and JAX-WS Web service endpoints from Microsoft Silverlight and .NET:
All entries will be archived at LOTD.

Technorati: lotd webservices metro jax-ws glassfish msdn

Friday Jan 11, 2008

Java SE 6 Update 4 is released - "Good Riddance" with JAX-WS Endorsed

Java SE 6 Update 4 is now released. Download it here.

If you are a Metro user (either JAX-WS or WSIT) then this is a milestone release for you because it includes JAX-WS 2.1 API in the rt.jar. This means that, as a user, you no longer you need to copy JAX-WS or JAXB API jars in JAVA_HOME/jre/lib/endorsed as described here, here and here. Hurrah!

After you have downloaded and installed JDK 1.6 U4, java -version shows:

java version "1.6.0_04"
Java(TM) SE Runtime Environment (build 1.6.0_04-b12)
Java HotSpot(TM) Client VM (build 10.0-b19, mixed mode, sharing)

wsgen -version shows:

JAX-WS RI 2.1.1 in JDK 6

wsimport -version shows:

JAX-WS RI 2.1.1 in JDK 6

Additionally, you can also verify by greping for javax.xml.ws.Endpoint class in JAVA_HOME/jre/lib/rt.jar. This is a new class introduced in JAX-WS 2.1.

Now after you've installed Java SE U4, you can download Metro 1.1, set JAVA_HOME to point to this new Java SE installation and you can easily import a WSDL as:

wsimport -d temp http://localhost:8080/MetroWithJavaSE6/HelloService?WSDL
parsing WSDL...


generating code...


compiling code...

If you try to import the same WSDL with an earlier release of Java SE 6, then you'll see the error message:

You are running on JDK6 which comes with JAX-WS 2.0 API, but this tool requires JAX-WS 2.1 API. Use the endorsed standards override mechanism (http://java.sun.com/javase/6/docs/technotes/guides/standards/), or use -Xendorsed option.

We hope this will make your life simpler :)

Metro 1.0.1 is anyway baked in GlassFish v2 UR1. You can override it with Metro 1.1 as described in TOTD #21.

Technorati: webservices metro jax-ws glassfish endorsed javase6 jdk

Friday Dec 21, 2007

Metro 1.0.1 and 1.1 are now available

Metro 1.0.1 (integrated in GlassFish v2 UR1) ad Metro 1.1 are now released. Metro contain stable releases of JAX-WS RI and WSIT. Read Vivek's blog for more details.

Even though Metro 1.1 is a stand-alone release, it can be easily installed on an existing GlassFish instance (for example override on v2ur1). A later release of Metro 1.1 will be integrated in GlassFish v2.1. Metro Roadmap provides all the details.

Please send us your feedback on users@metro or Forum. A pleasant change that happened earlier today was that cross-posting was enabled between user's list and forum. So all the questions posted on user's list are cross-posted to Forum and vice versa. This enables wider audience for your questions and more engineers to respond back :)

Technorati: webservices metro jax-ws wsit glassfish v2ur1

Sunday Sep 09, 2007

TOTD #8: Generating JSON using JAXB annotations in Jersey

Jersey provides a pluggable type system for the encoding/decoding of a Java type to/from an entity of an HTTP response/request. JSON support was added in Jersey using the BadgerFish convention for encoding/decoding JAXB beans to/from JSON. A new sample was also added in the recently released 0.2.1 that demonstrates this concept.

This TOTD provides provides a trivial sample (using the snippets shown on BadgerFish) that will allow you to experiment with this feature in Jersey. This sample consists of a Resource, Server and "build.xml" to build, deploy, run the server and invoke the endpoint.

Here is the code for the REST resource:

import javax.ws.rs.HttpMethod;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.UriTemplate;

@UriTemplate("/helloworld")
public class HelloWorldResource {

  @HttpMethod
  @ProduceMime("application/xml")
  @UriTemplate("/xml")
  public States getXML() {
    return new alice();
  }

  @HttpMethod
  @ProduceMime("application/json")
  @UriTemplate("/json")
  public States getJSON() {
    return new alice();
  }
}

This resource is published on two URIs - one using XML (/xml) and the other using JSON (/json) representation. Here is the code to start the server:

import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.ws.rest.api.container.ContainerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;

public class HelloWorld {
  public static void main(String[] args) throws IOException {
    HttpHandler handler = ContainerFactory.createContainer(
      HttpHandler.class,
      HelloWorldResource.class);

    HttpServer server = HttpServer.create(new InetSocketAddress(9998), 0);
    server.createContext("/", handler);
    server.setExecutor(null);
    server.start();

    System.out.println("Server running");
    System.out.println("Visit: http://localhost:9998/helloworld");
    System.out.println("Hit return to stop...");
    System.in.read();
    System.out.println("Stopping server");
    server.stop(0);
    System.out.println("Server stopped");
  }
}

This is a very standard code that is available in all the bundled samples. And here is the "build.xml":

<project name="json" default="default" basedir=".">
  <property name="jersey.home" value="c:\\jersey-0.2.1-ea"/>
  <path id="jersey.classpath">
    <fileset dir="${jersey.home}\\lib" includes="\*.jar"/>
  </path>
  <property name="build.dir" value="build"/>
  <property name="src.dir" value="src"/>

  <target name="init">
    <mkdir dir="${build.dir}"/>
  </target>

  <target name="build" depends="init">
    <javac srcdir="${src.dir}" destdir="${build.dir}" includes="\*\*/\*.java">
      <classpath refid="jersey.classpath"/>
    </javac>
  </target>

  <target name="run" depends="build">
    <java classname="samples.HelloWorld" fork="true">
      <classpath>
        <path refid="jersey.classpath"/>
        <pathelement location="${build.dir}"/>
      </classpath>
    </java>
  </target>

  <target name="get-json">
    <get src="http://localhost:9998/helloworld/json" dest="out.json"/>
  </target>

  <target name="get-xml">
    <get src="http://localhost:9998/helloworld/xml" dest="out.xml"/>
  </target>

  <target name="clean">
    <delete quiet="true" dir="${build.dir}"/>
  </target>
</project>

Make sure to set the value of "jersey.home" property correctly in this file. And now the JAXB bean and corresponding XML and JSON representation:

JAXB Bean XML representation JSON representation
@javax.xml.bind.annotation.XmlRootElement
public class alice {

  @javax.xml.bind.annotation.XmlValue
  protected String value;

  public alice() { this.value = "bob" }
  public String getValue() { return value; }
}

<alice>bob</alice>

{"alice":{"$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {

  @javax.xml.bind.annotation.XmlElement
  protected String bob;

  @javax.xml.bind.annotation.XmlElement
  protected String david;

  public alice() {
    bob = "charlie";
    david = "edgar";
  }

  public String getBob() { return bob; }
  public String getDavid() { return david; }
}
<alice>
  <bob>charlie</bob>
  <david>edgar</david>
</alice>
{"alice":{"bob":{"$":"charlie"},"david":{"$":"edgar"}}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {
  @javax.xml.bind.annotation.XmlValue
  protected String value;

  @javax.xml.bind.annotation.XmlAttribute
  protected String charlie;

  public alice() {
    value = "bob";
    charlie = "david";
  }

  public String getValue() { return value; }
  public String getCharlie() { return charlie; }
}
<alice charlie="david">
  bob
</alice>
{"alice":{"@charlie":"david","$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement(namespace="http://some-namespace")
public class alice {
  @javax.xml.bind.annotation.XmlValue
  protected String value;

  public alice() { value = "bob"; }

  public String getValue() { return value; }
}
<alice xmlns="http://some
-namespace">
  bob
</alice>
{"alice":{"@xmlns":{"$":"http:\\/\\/some-namespace"},"$":"bob"}}
@javax.xml.bind.annotation.XmlRootElement
public class alice {
  @javax.xml.bind.annotation.XmlElement
  protected java.util.List<String> bob;

  public alice() {
    bob = new java.util.ArrayList<String>();
    bob.add("charlie");
    bob.add("david");
  }

  public java.util.List<String> getBob() { return bob; }
}
<alice>
  <bob>charlie</bob>
  <bob>david</bob>
</alice>
{"alice":{"bob":[{"$":"charlie"},{"$":"david"}]}}

JSON representation can always be constructed using the JSON APIs as shown below and by adding the method to "HelloWorldResource":

import org.codehaus.jettison.json.\*;

@HttpMethod("GET")
@ProduceMime("application/json")
@UriTemplate("/json2")
public JSONObject getJSONMessage() throws JSONException {
  JSONObject object = new JSONObject();
  JSONObject content = new JSONObject();
  content.put("$", "bob");
  object.put("alice", content);

  return object;
}

JAX-WS also supports JSON as a pluggable encoding.

Please leave suggestions on other TOTD that you'd like to see. A complete archive is available here.

Technorati: totd jersey json jax-ws REST pluggableencoding restful

Thursday Aug 02, 2007

wsHttpDualBinding - a non-interoperable binding

Based upon a user request, I'll explain why wsDualHttpBinding (a system-provided binding in WCF) is not an interoperable binding. This blog entry will explain the service endpoint code, client code, generated WSDL and the SOAP messages exchanged based upon the DualHttp Binding Sample that is bundled with the Windows SDK samples. This is also an update to an earlier attempt to explain wsDualHttpBinding.

In the sample, I replaced the default wsDualHttpBinding with an equivalent custom binding (after removing the security) as:

<bindings>
  <customBinding>
    <binding name="Binding1">
      <reliableSession />
      <compositeDuplex />
      <oneWay />
      <textMessageEncoding
                     messageVersion="Soap12WSAddressing10" 
                     writeEncoding="utf-8" />
      <httpTransport authenticationScheme="Anonymous"
                     bypassProxyOnLocal="false"
                     hostNameComparisonMode="StrongWildcard"
                     proxyAuthenticationScheme="Anonymous"
                     realm=""
                     useDefaultWebProxy="true" />
    </binding>
  </customBinding>
</bindings>

The wsDualHttpBinding, also known as Composite Duplex or Full Duplex Binding, provides a bi-directional communication between Client and Endpoint. In a single direction communication, the client can invoke a set of operations on a service and this interface is referred as primary interface in Duplex binding. The set of operations that a service can invoke on the client is called as callback interface.

The sample in Windows SDK is a trivial calculator service where primitive Math operations are performed in a running session on the "primary" interface and results are returned on the "callback" interface.

Service Endpoint Code

Let's understand the service endpoint code first. The "primary" service endpoint interface looks like:

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", 
                 SessionMode=SessionMode.Required,
                 CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
  [OperationContract(IsOneWay=true)]
  void Clear();
  [OperationContract(IsOneWay = true)]
  void AddTo(double n);

  ...
}

Three points to note in this code:

  • A duplex contract requires a session, because a context must be established to correlate the set of messages being sent between client and service. Even though this is specified using SessionMode=SessionMode.Required attribute but the default value of SessionMode=SessionMode.Allowed (equivalent of not specifying) will be sufficient as well. This is because all Duplex bindings in .NET maintain a transport session.

  • Callback interface is specified using CallbackContract attribute.

  • All the methods are declared as One-way operations otherwise the response can be returned on the transport back channel itself. The documentation on this particular binding is limited.

The "callback" interface is defined as:

public interface ICalculatorDuplexCallback
{
  [OperationContract(IsOneWay = true)]
  void Result(double result);

  ...
}

In order for a service endpoint to establish a connection with the "callback" interface on client, a CallbackChannel is obtained from the OperationContext in the implementation of the "primary" interface as:

public class CalculatorService : ICalculatorDuplex
{
  double result;
  ICalculatorDuplexCallback callback = null;

  public CalculatorService()
  {
    result = 0.0D;
    callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
  }

Another variable is initialized to return the running result. The implementation of each method in the "primary" interface then invokes a method on the "callback" interface to return the running result as:

public void AddTo(double n)
{
  result += n;
  callback.Result(result);
}

Generated WSDL

Now let's look at the generated WSDL fragments. The policy assertion elements are:

<wsp:All>
  <wsrm:RMAssertion xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">
    <wsrm:InactivityTimeout Milliseconds="600000" /> 
    <wsrm:AcknowledgementInterval Milliseconds="200" /> 
  </wsrm:RMAssertion>
  <cdp:CompositeDuplex xmlns:cdp="http://schemas.microsoft.com/net/2006/06/duplex" /> 
  <ow:OneWay xmlns:ow="http://schemas.microsoft.com/ws/2005/05/routing/policy" /> 
  <wsaw:UsingAddressing /> 
</wsp:All>

The wsrm:RMAssertion and wsaw:UsingAddressing elements are bound to a known namespace and their behavior is clearly documented. However the specification of cdp:CompositeDuplex and ow:OneWay elements are unclear at this time. This does not allow any WSDL-based interoperability whenever these elements are included.

All methods from the "primary" and the "callback" interface are generated in one wsdl:portType. The methods from the "primary" interface are generated as One-way operations. But methods from the "callback" interface are generated as Notification operation. For example, one of the methods from "callback" interface looks like:

<wsdl:operation msc:isInitiating="true" msc:isTerminating="false" name="Result">
  <wsdl:output
      wsaw:Action="http://Microsoft.ServiceModel.Samples/ICalculatorDuplex/Result"
      message="tns:ICalculatorDuplex_Result_OutputCallbackMessage"/>
</wsdl:operation>

JAX-WS, the core of Metro, supports only Request-Response and One-way operations. This is the second place where WSDL-based interoperability will not work with any JAX-WS-based WSDL import tool, such as wsimport. Moreover, the WSDL-to-Java mapping defined by the JAX-WS specification requires each wsdl:portType map to a single Java interface. This WSDL design pattern requires two interfaces to be generated from a single wsdl:portType.

There are some other elements in namespace prefix bound to "http://schemas.microsoft.com/ws/2005/12/wsdl/contract" and their purpose is also unclear. Rest of the WSDL is pretty straight-forward.

Client side code

On the client side, svcutil (WSDL importing tool for .NET 3.0) generates the "primary" and "callback" interface from the WSDL. The "callback" is implemented as:

public class CallbackHandler : ICalculatorDuplexCallback
{
  public void Result(double result)
  {
    Console.WriteLine("Result({0})", result);
  }

  public void Equation(string eqn)
  {
    Console.WriteLine("Equation({0})", eqn);
  }
}

This client instance is initialized with the callback implementation as:

class Client
{
  static void Main()
  {
    // Construct InstanceContext to handle messages on callback interface
    InstanceContext instanceContext = new InstanceContext(new CallbackHandler());

    // Create a client with given client endpoint configuration
    CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);

And then the client invokes the service endpoint normally as shown below:

// Call the AddTo service operation.
double value = 100.00D;
client.AddTo(value);

...

SOAP messages

Lets look at the SOAP messages exchanged between client and endpoint now. The first call from the client to an endpoint triggers a protocol handshake for establishing a session. The CreateSequence protocol message looks like:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action>
    <a:ReplyTo>
      <a:Address>http://iamfine.sfbay.sun.com/Temporary_Listen_Addresses/bfd8c103-b0f9-4c65-9cb6-fbebb7d1517b/4e0cdb31-2451-4fb6-84b8-dc286e5f26c8</a:Address>
    </a:ReplyTo>
    <a:MessageID>urn:uuid:51918652-9a78-4ba3-82f5-e68ecd664d42</a:MessageID>
    <a:To s:mustUnderstand="1">http://localhost:8888/</a:To>
  </s:Header>
  <s:Body>
    <CreateSequence xmlns="http://schemas.xmlsoap.org/ws/2005/02/rm">
      <AcksTo>
        <a:Address>http://iamfine.sfbay.sun.com/Temporary_Listen_Addresses/bfd8c103-b0f9-4c65-9cb6-fbebb7d1517b/4e0cdb31-2451-4fb6-84b8-dc286e5f26c8</a:Address>
      </AcksTo>
      <Offer>
        <Identifier>urn:uuid:b1116e69-f1dd-45b0-8495-129645038160</Identifier>
      </Offer>
    </CreateSequence>
  </s:Body>
</s:Envelope>

The WCF runtime uses the Windows HTTP.SYS library to host an endpoint at the address specified in a:ReplyTo. This address is used for all subsequent messages sent on the callback channel. This message is used to create a session for the "primary" interface. The message also carries an offer, in the SOAP Body, to create a "callback" interface session.

The CreateSequenceResponse protocol message returns "primary" interface session identifier and also accepts the offered "callback" session. The message looks like:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action>
    <a:RelatesTo>urn:uuid:51918652-9a78-4ba3-82f5-e68ecd664d42</a:RelatesTo>
    <a:To s:mustUnderstand="1">http://iamfine.sfbay.sun.com/Temporary_Listen_Addresses/bfd8c103-b0f9-4c65-9cb6-fbebb7d1517b/4e0cdb31-2451-4fb6-84b8-dc286e5f26c8</a:To>
  </s:Header>
  <s:Body>
    <CreateSequenceResponse xmlns="http://schemas.xmlsoap.org/ws/2005/02/rm">
      <Identifier>urn:uuid:d483898c-4bd3-4077-ba04-07a9010ab27f</Identifier>
      <Accept>
        <AcksTo>
          <a:Address>http://localhost:8888/</a:Address>
        </AcksTo>
      </Accept>
    </CreateSequenceResponse>
  </s:Body>
</s:Envelope>

Now, because of the way each method is implemented (invoking callback.Result(result) method at the end of each "primary" operation), a response to a request received by an endpoint is returned over the callback channel. This happens under-the-cover even though all messages in the "primary" interface are defined as One-way operations.

The behavior is quite analogous to a Request-Response operation primitive. I wonder what are the usecases of wsDualHttpBinding ?

Summary

Finally, I summarize the reasons that makes wsDualHttpBinding a non-interoperable binding:

  1. The specifications of cdp:CompositeDuplex and ow:OneWay are not available and these elements will thus be ignored by the Metro WSDL importing tool.

  2. The operations from "callback" interface are mapped as Notification operation in the WSDL. This operation primitive is not supported by Metro.

  3. On the service endpoint, all the operations from "primary" and "callback" interface are mapped to a single wsdl:portType. On the client side, wsdl:portType is mapped to separate "primary" and "callback" interfaces. The Java-to-WSDL mapping defined by the JAX-WS specification allows one-to-one mapping between Java interface and wsdl:portType.

Technorati: webservices interoperability wcf metro jax-ws wsit

Sunday Jul 22, 2007

Metro on Tomcat 6.x

Rama described how to run JAX-WS samples with Tomcat 6.x. JAX-WS is part of Metro - the Web services stack in GlassFish. Another key component of Metro is WSIT (aka Project Tango) that provides Secure, Reliable, Transactional and Interoperable Web service. Read more about Project Tango in this 26-page article.

A stable version of Metro is integrated in GlassFish V2 and the latest nightlies of stand-alone bundle are also available. The stand-alone bundle comes with an install scipt (wsit-on-tomcat.xml) that allows it install on Tomcat 5.x. I followed the steps in Rama's blog to install Metro on Tomcat 6.x. But first, a little bit of explanation and then the actual code fragments.

Tomcat's classloading mechanism has changed slightly between 5.x and 6.x. The first change is that Tomcat 5.x used to have shared/lib directory to share classes across all web applications. This directory in turn used to be specified as value of shared.loader property in  conf/catalina.properties. In Tomcat 6.x, the property still exists but it's value is set to nothing and shared/lib directory no longer exists in the default installation. I see the motivation behind this change as it keeps the Tomcat installation directory clean and any shared resources can still be specified in conf/catalina.properties. But this means that wsit-on-tomcat.xml script, that copies the files in shared/lib directory, will work on Tomcat 5.x only. In order for this script to work on Tomcat 6.x, the value of shared.loader property need to be changed to include Metro jars.

Now, the code fragments! The value of shared.loader property in Tomcat 5.x is:

shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/\*.jar

And in Tomcat 6.x is the value of this property is:

shared.loader=

If Metro is installed in c:\\metro then changing its value to:

shared.loader=file:///c:/metro/lib/\*.jar

will enable Tomcat 6.x to host Secure, Reliable, Transactional and .NET 3.0-Interoperable Web services. And this mechanism will work for Tomcat 5.x too, so changing the value of this property in Tomcat 5.x installation to:

shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/\*.jar,file:///c:/metro/lib/\*.jar

instead of copying the files in shared/lib will be sufficient as well.

The second change in Tomcat's classloading mechanism is required if you are using Java SE 6. Tomcat 5.x used to have common/endorsed directory which no longer exists in Tomcat 6.x. Java SE 6 is bundled with JAX-WS 2.0 and Metro needs JAX-WS 2.1. So if you are using Java SE 6 then copy webservices-api.jar in c:/jdk6/jre/lib/endorsed directory. Read Endorsed Directory Mechanism for more details.

Several screencasts are available that show how to develop Secure, Reliable and Transactional and Interoperable Web service. All the screencasts use NetBeans IDE but if you are more of a command-line user then follow this entry that shows how to develop a reliable endpoint and invoke it from WCF and vice versa.

Technorati: metro webservices wsit jax-ws glassfish tomcat

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today