This guidance is a part of a Java-Silverlight interoperability guidance series to help developers create a Silverlight application in ESL, which enables the utilization of interoperability scenarios using the following protocols:
This guidance walks through how to build a Silverlight application in the Eclipse environment on the Apache CXF SOAP stack accessing JAX-WS Web Services over HTTP transport. Web Services development on JAX-WS can be used in two ways:
In Web services terminology, Bottom Up (Code First) is used when developers start with business logic in a programming language, and then develop and deploy is as a Web service.
Bottom-up
The Top Down (Contract First) approach starts from Web service descriptions in WSDL, and then goes on to expose Web services.
Top-down
This guidance focuses only on the bottom-up approach. It covers preparation, development, deployment, and testing of a Data access application via Web services:
This guidance is intended for software architects and software developers who are building interoperable Web services across platforms and applications.
For your convenience, the JAX-WS/CXF service
Eclipse project and the Silverlight Eclipse projects are available as an
attachment here.
The implementation of this guidance extends the Silverlight DataGrid sample by accessing the application data directly from REST Web service developed in Java. The following knsowledge is required:
If you are new in Silverlight development in eclipse with ESL, we recommend you go through the following tutorial first:
Some softwares need be installed on your machine to work with this guidance:
Please read this page to install ESL in "Pure Eclipse configuration" mode. If you have already installed the Eclipse IDE for Java EE Developers distribution, all necessary tools are already installed and you can skip to next paragraph.
Otherwise, it is really straightforward to install Java EE development tools via Eclipse Update Manager. In Eclipse, call the menu Help → "Software updates ...". In the next dialog you will need to perform the following steps:
- Accept the standard options in the remaining pages of this wizard to complete the installation.
The installation of Tomcat consists of three steps:
Click on the button "Add" to add a new runtime environment. In the next dialog, select the entry "Apache Tomcat v6.0" of the "Apache" category.
Click on the "Browse..." button to set up the Tomcat installation location. And then click on the "Finish" button to complete the setting.
Apache CXF is an open source services framework, designed for building and developing services using frontend programming APIs, like JAX-WS. This guidance relies on the JAX-WS over the SOAP protocol and HTTP transport.
The installation is simple: you need to download the archive zip apache-cxf-2.1.3.zip and unzip it somewhere in your hard disk: e.g. d:\products\apache-cxf-2.1.3.
There was a CFX plug-in for Eclipse provided by Eclipse STP for Eclipse Europa (version 3.3). But this development is moving to Eclipse WTP and Eclipse Ganymede doesn't provide such support yet.
In this guidance, we use a WTP plug-in for Eclipse that is still an incubation project, which is not yet available from the Eclipse Update Site. It is possible to get it from Eclipse Bugzilla here. For your convenience, we have bundled it along with our contribution plug-ins for JAX-WS SOAP development. You can install "CXF Web Services" and "eclipse4SL Web service utilities" via Eclipse Update manager using following URL: http://www.eclipse4sl.org/update.
The plug-ins provided under the
"Contribution" category are not part of the ESL product. It
contains user-contributed software. Since we cannot guarantee its
quality, you use them at your own risk.
Now, to integrate the Apache CXF runtime in Eclipse, we need to set up the Preferences of Eclipse:
Our application is a simple Customer contact management tool. Suppose we have three customers:
Contact Name | Company | Country |
---|---|---|
Luc Forrest | EDF | France |
Scott Bizien | Sun | United States |
Jean Pierret | SAGEM | France |
The image below describes the UI and interaction scenario of our Web application to develop:
In the Bottom Up Approach, we start with the service implementation in programming language and then build the deployable Web service component. The development process will be:
First of all, we need to create a Web project: New → Project, select "Dynamic Web project" under the Web category in the new wizard:
Put the "DataGridCXFService" in the "Project name:" field, and then select "CXF Web Services Project v2.5" in the Configuration group and click on the "Finish" button.
We will be using the CustomerService class as the service implementation class for developing, deploying, and testing the Web service.
This class keeps a list of customers as a property, and provides one public method findCustomers(String country).
package dataservice; import java.util.Collection; import java.util.ArrayList;
/** * Resource that manages customers. */ public class CustomersService { private Customer[] customers = new Customer[]{ new Customer("Luc Forrest", "EDF", "France"), new Customer("Scott Bizien", "Sun", "United States"), new Customer("Jean Pierret", "SAGEM", "France")}; /** * Returns a listing of customers. */ public Customer[] findCustomers(String country) { if (country != null && country.length() > 0) { Collection<Customer> collector = new ArrayList<Customer>(); for (Customer customer : customers) { if (country.equals(customer.getCountry())) { collector.add(customer); } } return collector.toArray(new Customer[collector.size()]); } return customers; } }
We need to call a wizard tool to perform this tranformation: File → New → Other...
Select the "Web Service" under the "Web Services" category, and go to next page.
Take the option "Bottom-up POJO Web service" of Web service type, and select the class dataservice.CustomersService for Service implementation. The configuration will be updated automatically as:
- Server: Tomcat v6.0 server
- Web service runtime: Apache CXF 2.x
- Service project: DataGridCXFService
If your configuration is not same as above, you will need to change them by clicking on the hyperlinks.
And then click on the "Next >" button to go to the next step.
Click on the "Next >"button to accept the default options.
Click on the "Next >" button to accept the default options. In the next page, check the option "Generate server" to create a Tomcat server configuration. The option "Default SOAP Binding" is very critical. since Silverlight 3 only supports SOAP 1.1.
Click on the "Start server" button to launch the Tomcat server with DataGridCXFService Web service.
Click the "Finish" button to close the wizard.
Now, we can open a Web brower to check the Web service using URL http://localhost:8080/DataGridCXFService/services/CustomersServicePort?wsdl.
Now that the Web Service is implemented and exposed via the Web, it is time to go to the next step: developing the Web service consumer in Silverlight.
The development of Silverlight Web client consists of three steps:
Before going forward, we need to create two projects in Eclipse: a Silverlight project and a Silverlight Web project named as DataGridCXFClient and DataGridCXFClient.Web respectively. If you are not yet familiar with Eclipse development environment for Silverlight, this Data Binding example will be useful to get started.
In DataGridCXFClient project, open the Page.xaml and modify it as
follows:
<UserControl x:Class="DataGridCXFClient.Page" xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="300"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Grid.Column="0" Name="LayoutRoot" Orientation="Vertical"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <TextBlock Margin="10" Text="Filter :" VerticalAlignment="Center"/> <TextBox Margin="10" Text="France" Name="country" Width="200"/> <Button Margin="10" Name="load" Content="Load" Click="Button_Click" Width="50" RenderTransformOrigin="0.5,0.5"/> </StackPanel> </StackPanel> <data:DataGrid Margin="5" Name="grid" Grid.Row="1" Grid.Column="0" AutoGenerateColumns="True" RowBackground="Aquamarine" AlternatingRowBackground="White" RenderTransformOrigin="0.5,0.5"> </data:DataGrid> </Grid> </UserControl>
The first creation of a DataGrid element in XAML editor, by either DnD from palette tool or code completion, automatically updates the project structure by adding the "System.Windows.Control.Data" Reference if this Reference is not already used. However, if you copy and paste this code in the XAML editor, you need to add the reference manually in the following way:
Now through the exposed Web Service description in WSDL, we can use our contribution tool to generate the client proxy.
This proxy generation tool is missing in the
Silverlight 3 SDK. We have developed this contribution tool specifically
for this guidance. Its implementation is not complete. Any contributions
to improve this tool are welcome. If you need this tool in a production
project it is possible to use Visual Studio 2008.
In our application, when we click on the button "Load", the code-behind class will handle this event, invoke the Web service to access the application data and then display the data in the DataGrid.
When we add or change a property event of XAML element in the ESL editor, the event handler is generated automatically in the code behind class. In our case, the generated event handler is Button_Click() in Page.xaml.cs, which will access Web services to get application data. It uses the generated proxy classes from WSDL.
using System; using System.Collections.Generic; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace DataGridRESTClient { /** * UI view */ public partial class Page : UserControl { public Page() { InitializeComponent(); }
private void client_FindCustomersCompleted(object sender, DataService.findCustomersCompletedEventArgs e) { // Put the list of customers as DataGrid's Data grid.ItemsSource = e.Result; // Enable the button load.IsEnabled = true; } /** * Load */ private void Button_Click(object sender, RoutedEventArgs e) { // Disable the button load.IsEnabled = false; var client = new DataService.CustomersServiceClient(); client.findCustomersCompleted += new EventHandler<DataService.findCustomersCompletedEventArgs>(client_FindCustomersCompleted); client.findCustomersAsync(country.Text); } } }
Now we need to handle the Click event in Button_Click method :
var client = new DataService.CustomersServiceClient();
client.findCustomersCompleted += new EventHandler<DataService.findCustomersCompletedEventArgs> (client_FindCustomersCompleted);
client.findCustomersAsync(country.Text);
When this operation is completed, the event listener client_FindCustomersCompleted() is executed to process the result of the Web service request.
private void client_FindCustomersCompleted(object sender, DataService.findCustomersCompletedEventArgs e) { // Put the list of customers as DataGrid's Data grid.ItemsSource = e.Result; // Enable the button load.IsEnabled = true; }
If the Silverlight application and the Web services are hosted in the same Web server, this deployment mode is called "single domain deployment".
Single domain
In single domain mode, Java Web services and Silverlight applications share the same domain name, port number and transport protocol.
Cross domain
Otherwise, it is a so called "cross domain deployment".
In this mode, we need first to deploy our Silverlight application on the Web server where the Web services are hosted. It can be done by copying following files from DataGridCXFClient. Web to the WebContent folder of the DataGridRESTService.
To make the DataGridCXFClient.html the default page, you can rename it to index.html.
Now, our server is correctly configured and ready to start. You
can start it by clicking on the Run button on the tool bar of the Server
view. Then you can open a web browser to test our Silverlight
application via the URL
http://localhost:8080/DataGridCXFService/
This Silverlight application, launched via
the Run configuration of Eclipse environment (see screenshot below),
will fail to access the Web services in single domain deployment mode
since ESL starts automatically a new Web server to host it. It
falls automatically into the cross domain mode.
In an enterprise with a heterogeneous environment, Silverlight application may be hosted in a different Web server from Web services. Using Silverlight for cross-domain communication requires guarding against several types of security vulnerability that can be used to exploit Web applications.
To enable a Silverlight control to access a service in another domain, the service must explicitly opt-in to allow cross-domain access. By opting-in, a service states that the operations it exposes can safely be invoked by a Silverlight control, without potentially damaging consequences to the data the service stores.
Silverlight 3 supports two different mechanisms for services to opt-in to cross-domain access:
More information about the cross-domain control can be found here.
Cross domain control in a Web server depends on its configuration. Eclipse WTP provides three options:
There are two solutions to change the configuration:
1. Right click "Tomcat v6.0 server at localhost" in "Server" View and select "Open" menu
2. Right click "Tomcat v6.0 server at localhost" in "Server" View and select "Properties" menu:
The location of clientaccesspolicy.xml must be in Web server root folder, which depends on server configuration. You can find it by the concatenation of <Server path> and <Deploy path>/ROOT. Taking an example of option N° 1, the Web root should be <workspace>/.metadata/.plugin/org.eclipse.wst.server.core/tmp0/wtpwebapps/ROOT.
To enable cross domain access in this configuration, we need to put a clientaccesspolicy.xml file in the Web root folder.
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy>
This
configuration allows access from any other domain to all resources on
the current domain. It is highly recommended to restrict access in a
production environment.