The How-To series demo Silverlight accessing Web Services in various configuration. In this post, we will demonstrate a SOAP service consumed by a Silverlight client application. This scenario can be categorized as a D2D scenario with a cross domain policy.

Both the Silverlight and SOAP service are available as an attachment to this post. Since Metro libraries are too large, you will need to copy all jar files from metro\lib\ folder into WebContent\WEB-INF\lib project folder. Steps are described below.

Technical environment

  • SOAP services are built with JAX-WS and JAX-B APIs and hosted in Tomcat.
  • The Silverlight application is hosted under the Visual Studio embedded Web Server code name “Catalina” (which delivers a subset of the IIS capabilities)

image

Requirement  

For this How-To, we use the Metro framework which contains both JAX-WS and JAX-B APIs which will be used to develop SOAP Services in JAVA.

Configuring Tomcat

Download the jar file from https://metro.dev.java.net/1.2/ and launch it. A “metro” folder has been created at the jar location. metro\lib folder contains all the needed libraries for the Java SOAP service development and Tomcat configuration. Here is a good link that describes how to configure your Tomcat server http://blogs.sun.com/arungupta/entry/metro_on_tomcat_6_x

Implementing the SOAP / RPC service

  • In Eclipse, create a new Dynamic Web Project called “SOAP-SumService
  • copy all jar files from metro\lib\ folder into WebContent\WEB-INF\lib project folder.
  • Create a package called “service” and add a new Java class called “ServiceSumImpl” to this package.
  • Add a xml file to WebContent\WEB-INF called “sun-jaxws.xml”.

Your project structure now looks like this :

image

Implementation of the Web Service endpoint

In ServiceSumImpl.java, import the following libraries :

image

Declare the class as a Web Service endpoint with the following annotations, for more informations about annotations refer to this site.

image

The only method that our service exposes is getSum. It returns the sum of two integers. But we want this method to be known over the service as “SumTwoInt”. Within the annotations we can configure all these parameters.

image

  • Now build the project to generate the bytecode as the WSGen tool need it.

Generation of the required artifacts for the Web Service deployment and invocation

For this step, Metro provides a generation tool called “wsgen”.

  • Open a Windows Command Prompt : Start->Run and type “cmd”
  • set a METRO as a path variable to metro\bin folder (in our case it is C:\metro\bin path)

image

  • Go to your build project folder (by default in Eclipse: ProjectName\build\classes). In our example it is the SOAP-SumService\build\classes folder

image 

Type the following command line to generates the artifacts in the project “src” folder.

%METRO%\wsgen –cp . service.ServiceSumImpl –s ../../src

  • Refresh the Project Explorer and observes that two java files have been generated

image

They represent the message that will be shared in the Web Service process

Configuring the Web Service Deployment

To configure the deployment, we need to instantiate a Servlet that will handles JAX-WS Web Services requests.

In web.xml, add a Servlet element. We use a pre-existing Web Service Servlet :

<servlet>
    <servlet-name>jaxservlet</servlet-name>
    <servlet-class>
        com.sun.xml.ws.transport.http.servlet.WSServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

This servlet will handle every requests on /services/* URI, so let’s map to it with its name:

<servlet-mapping>
    <servlet-name>jaxservlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

To provide the right context, we need a listener (which also already exists):

<listener>
    <listener-class>
        com.sun.xml.ws.transport.http.servlet.WSServletContextListener
    </listener-class>
</listener>

Finally, we need to expose the endpoint ServiceSumImpl within an URI, for example /services/sum.

Edit the sun-jaxws.xml file and add the following elements :

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
    <endpoint 
        name="sum" 
        implementation="service.ServiceSumImpl" 
        url-pattern="/services/sum"/>
</endpoints>
  • Save all files, then Run the project on the Tomcat Server
    Right click on the project node : Run As –> Run on Server

The service is now hosted on Tomcat at http://localhost:8080/SOAP-SumService/services/sum?wsdl

image

Enabling Cross Domain Access 

Since the SOAP service and the Silverlight application reside on different hosts, we need to overpass the Cross Domain Issue. Create at the root of the SOAP service host, a clientaccesspolicy.xml file with the following content :

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers="*">
                <domain uri="*"/>
            </allow-from>
            <grant-to>
                <resource include-subpaths="true" path="/"/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

 

Consuming a SOAP service with Silverlight

Now that the Web Service is exposed, we are going to generate the client proxy to consume the Web Service with Silverlight.

Open Visual Studio 2008 and create a new Silverlight Project called SOAP-Sum and paste the following XAML code in Page.xaml to obtain a simple interface that will manipulate the web service.

<UserControl x:Class="SOAP_Sum.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Canvas x:Name="LayoutRoot" Background="White">
        <TextBox Text="0" x:Name="_int1" Width="50" Height="20" 
                          Canvas.Top="10" Canvas.Left="30" TextChanged="_TextChanged"/>
        <TextBlock Canvas.Top="10" Canvas.Left="95" Text="+"/>
        <TextBox Text="0" x:Name="_int2" Width="50" Height="20" 
                          Canvas.Top="10" Canvas.Left="120" TextChanged="_TextChanged"/>
        <TextBlock Canvas.Top="10" Canvas.Left="175" Text="="/>
        <TextBlock x:Name="_result" Canvas.Top="10" Canvas.Left="190" Text=""/>
    </Canvas>
</UserControl>
  • Add a Service Reference
    Right click on References then chose Add a Service Reference

image

  • In Address field, type the Sum service address then click Go (it should be http://localhost:8080/SOAP-SumService/services/sum?wsdl).

    We actually have the Sum service named ServiceSum and one operation exposed as SumTwoInt, this is exactly what we did parameter in “Implementation of the Web Service endpoint” (see above)

image

  • In Namespace field, type ServiceSum : this is how the service will be accessible through the Silverlight project. Click OK. In the Solution Explorer you see the ServiceSum reference in Service References.

image

Implementation of the client proxy

In Page.xaml.cs, add an import to SOAP-Sum.ServiceSum reference library.

   1:  using SOAP_Sum.ServiceSum;

Now we need to handle the TextChanged event in _TextChanged method :

  • Instantiate a client proxy
    ServiceSumClient client = new ServiceSumClient();
  • Add an event handler corresponding to the operation we are going to use so we can get the result
    client.SumTwoIntCompleted += 
       new EventHandler<SumTwoIntCompletedEventArgs>
          (
             client_SumTwoIntCompleted
          );
  • Use Async operation to send our two integers.
    Operations are made asynchronous so Silverlight UI won’t freeze
    client.SumTwoIntAsync(a,b);

The whole method looks like this :

   1:  private void _TextChanged(object sender, TextChangedEventArgs e)
   2:  {
   3:      // check if fields contain some text
   4:      if (_int1.Text == "" || _int2.Text == "") return;
   5:   
   6:      int a = int.Parse(_int1.Text);
   7:      int b = int.Parse(_int2.Text);
   8:   
   9:      // generate a client proxy to the Web Service
  10:      ServiceSumClient client = new ServiceSumClient();
  11:      client.SumTwoIntCompleted += new EventHandler<SumTwoIntCompletedEventArgs>(client_SumTwoIntCompleted);
  12:      client.SumTwoIntAsync(a,b);
  13:  }

Then we can process the result in the event handler

void client_SumTwoIntCompleted(object sender, SumTwoIntCompletedEventArgs e)
{
    _result.Text = e.Result.ToString();
}

Run the project and type some integers in the text boxes : Silverlight is consuming a JAX-WS SOAP service.

- Ronny Kwon