Using Web Services for Interoperability


Web services are the recommended interoperability technique, unless performance or other considerations require a binary solution. This section looks at how you can use Web services to implement interoperability. It covers the data format choices, how to build the service interface by using .NET Framework Web services, and how to build the J2EE interoperability adapters.

Deciding on a Data Format

As Chapter 7 observed, it is a good idea for Web services to use primitive XSD data types such as strings and integers instead of using complex XSD data types. All Web service stacks support the primitive XSD data types, so using these data types promotes flexibility and simplifies client access.

If you use primitive data types and need to return something more complex, such as an order, you can package the data as a string. The best solution for this is to populate a string with an XML representation of the data. This XML encoding should follow an agreed schema.

If your communicating applications use compatible Web services stacks, then you can use complex data types to link components rather than serializing into an XML string. While there is currently no guarantee of interoperability between all manufacturers’ Web stacks, this situation should change as more Web stack implementations start to follow industry guidelines.

Note

There is little performance difference between passing .NET Framework data types directly and serializing them and passing them as strings, because the Web service has to serialize the .NET Framework data into XML anyway.

In the XBikes application, the J2EE and .NET Framework developer teams decided on a common data format for the Web services. They created an XML Schema from this design, which both teams then used to create the appropriate classes and mapping files.

Building the Service Interface in .NET Framework

You perform the following two tasks to build the service interface in .NET Framework:

  • Define .NET Framework data types based on an XML Schema.

  • Create .NET Framework Web services.

The following sections describe how to perform these tasks.

Defining .NET Framework Data Types based on an XML Schema

To enable .NET Framework applications to present data for consumption by Java applications, it is a good idea to define .NET Framework classes that you can serialize as XML strings. You can use the Xsd.exe tool in the .NET Framework SDK to generate .NET Framework classes from existing XML Schemas. When instances of these classes are serialized to XML, they generate the correct XML structure as defined by the XML Schemas.

You can use the Xsd.exe tool in one of two ways, depending on how you want to represent data within your .NET Framework application:

  • Use the /dataset switch to generate .NET Framework typed dataset classes.

  • Use the /class switch to generate normal .NET Framework classes.

The XBikes developers used the Xsd.exe tool with the /dataset switch to generate .NET Framework typed dataset classes. The following code sample shows how the XBikes developers generated a typed dataset class for the CustomerData type. To do this, they opened a Visual Studio .NET 2003 command prompt and ran the following command (note that the /namespace switch specifies the .NET Framework namespace of the generated classes).

 xsd CustomerData.xsd /dataset /namespace:xbikes.common.schemas 

Typed dataset classes inherit from the System.Data.DataSet class and have named inner types and type-safe properties that map directly to the XML structure that the XML Schema defines. Typed dataset classes are serializable by default. However, if you serialize a typed dataset using the XMLSerializer class in .NET Framework, the serialized XML output does not conform to the XML Schema that you used to generate the typed dataset. Instead, it contains an XML representation of the typed dataset object itself, including .NET Framework-specific information such as the inner types and type-safe properties. You cannot use this method to exchange data in an interoperability scenario as the Java client does not know how to deserialize the .NET Framework-specific XML format of the serialized typed dataset.

To get around this issue, you can use the typed dataset’s GetXml method. Typed datasets have a GetXml method, which returns the data contained in a dataset object in an XML formatted string. The format of the XML string the GetXml method returns matches the one specified by the XML Schema that defines the typed dataset. You can then exchange this XML string with Java clients that can then deserialize it into a corresponding Java class.

If you are not using datasets in your .NET Framework application, you can use the /class switch with Xsd.exe to generate normal .NET Framework classes as shown in the following example, which again illustrates the CustomerData custom data class.

 xsd CustomerData.xsd /class /namespace:MyNamespace 

The generated classes contain public fields corresponding to the structure defined by the XML Schema. It is a good idea to make the fields private and define public properties to get and set the fields. The following sample code shows how the generated classes appear after making these changes.

 using System; using System.Xml.Serialization; namespace MyNamespace {   [System.Xml.Serialization.XmlTypeAttribute(            Namespace="http://xbikes.com/CustomerData.xsd")]   [System.Xml.Serialization.XmlRootAttribute(            Namespace="http://xbikes.com/CustomerData.xsd", IsNullable=false)]   public class CustomerData   {     private CustomerDataCustomers[] Items;     [System.Xml.Serialization.XmlElementAttribute("Customers")]     public CustomerDataCustomers[] customers     {       set { Items = value; }       get { return Items;  }     }   }   [System.Xml.Serialization.XmlTypeAttribute(            Namespace="http://xbikes.com/CustomerData.xsd")]   public class CustomerDataCustomers   {     private int    _CustomerID;     private string _FullName;     private string _EmailAddress;     private string _Password;     private string _ZipCode;     public int CustomerID     {       set { _CustomerID = value; }       get { return _CustomerID;  }     }     public string FullName     {       set { _FullName = value; }       get { return _FullName;  }     }     public string EmailAddress     {       set { _EmailAddress = value; }       get { return _EmailAddress;  }     }     public string Password     {       set { _Password = value; }       get { return _Password;  }     }     public string ZipCode     {       set { _ZipCode = value; }       get { return _ZipCode;  }     }   } } 

The next set of sample code shows how to create and serialize an instance of the CustomerData class. XmlSerializer is a standard .NET Framework class in the System.Xml.Serialization namespace, and it allows you to serialize and deserialize objects to and from XML format.

 using System;                     // For the Console class using System.Xml.Serialization;   // For the XmlSerializer class using System.IO;                  // For the StringReader and StringWriter classes using System.Text;                // For the StringBuilder class using MyNamespace;                // For CustomerData and CustomerDataCustomers class SerializationExample {   static void Main(string[] args)   {     // Create and initialise a customer     CustomerDataCustomers customer = new CustomerDataCustomers();     customer.FullName = "someone";     customer.EmailAddress = "someone@microsoft.com";     customer.CustomerID = 1008;     customer.Password = "secret";     customer.ZipCode = "91210";     // Create a customer collection, containing a single customer     CustomerData cd = new CustomerData();     cd.customers = new CustomerDataCustomers[] { customer };     // Serialize the customer collection to an XML string     XmlSerializer ser = new XmlSerializer(typeof(CustomerData));     StringBuilder sb = new StringBuilder();     StringWriter writer = new StringWriter(sb);     ser.Serialize(writer, cd);     writer.Close();     // Output the XML string that represents the customer collection     Console.Write(sb.ToString());   } } 

The result is that when the CustomerData object is serialized to XML, the XML data has exactly the correct format as specified by the XML Schema.

Creating the .NET Framework Web Service

The next task is to create a .NET Framework Web service to provide a service interface that encapsulates the .NET Framework business service fa ade.

Visual Studio .NET and the .NET Framework make it extremely easy to create Web services. When you create an ASP.NET Web service project in Visual Studio .NET, the project contains a single Web service. The Web service comprises an .asmx file (for example, MyWebService.asmx) and a code-behind file (for example, MyWebService.asmx.cs). The code-behind file contains a class that inherits from System.Web.Services.WebService, and it can contain a series of methods annotated with the [WebMethod] attribute.

You must define a separate Web service method for each business method that you want to expose from the BusinessServiceFacade. To simplify interoperability, the Web service methods should convert the return values from the BusinessServiceFacade methods into XML-formatted strings. Use one of the following techniques to perform this conversion:

  • If you are using .NET Framework datasets to represent data, you can call GetXml to convert the dataset into an XML string.

  • If you are using normal .NET Framework classes to represent data, you must use the XmlSerializer to obtain the XML string representation for your objects.

The flow of logic in any of the Web service methods is as follows:

  1. The application calls the Web service method.

  2. The Web service method creates the BusinessServiceFacade object.

  3. The Web service method calls the appropriate method on the BusinessServiceFacade, passing the converted data.

  4. If the BusinessServiceFacade method returns data, the Web service method converts this data to a string by using the helper class.

  5. The Web service method returns the string representation of the data back to the calling application.

Figure 8.1 shows this in action.

click to expand
Figure 8.1: Implementing a .NET Framework service interface for the .NET Framework business service fa ade

The following procedure describes how the XBikes developers created a .NET Framework Web service service interface to expose the business service fa ade in the Business tier of the .NET Framework version of XBikes:

  1. The developers created a new ASP.NET Web service project named XBikes-BLL-WSServiceInterface.

  2. They removed the default Service1.asmx file and replaced it with a new Web service named BLLWSServiceInterface.asmx. The developers annotated the Web service class with a [WebService] attribute as follows.

     [WebService(Namespace="http://XBikes.com/BLLWSServiceInterace/")] public class BLLWSServiceInterface : System.Web.Services.WebService {   // Members... } 

  3. The team added Web service methods to the Web service class, with the correct method signatures for each of the methods exposed by the existing business service fa ade. However, instead of each method accepting or returning datasets as parameters, the developers changed the data type exchanged to “string.”

     [WebService(Namespace="http://XBikes.com/BLLWSServiceInterace/")] public class BLLWSServiceInterface : System.Web.Services.WebService {   [WebMethod]   public string AuthenticateCustomer(string email, string password)   {}   [WebMethod]   public string GetCategories()   {}   [WebMethod]   public string GetProductsByCategory(int CategoryID)   {}   [WebMethod]   public string GetSearchResults(string keyword)   {}   [WebMethod]   public void PlaceOrder(string order)   {}   [WebMethod]   public string GetCustomerOrders(int customerID)   {} } 

  4. The team added code to each of the Web service methods to call the business service fa ade methods. The following code sample shows how this was done for the GetCategories method in the Web service service interface. The developers called the GetXml method of the CategoriesData object returned from the business service fa ade to convert the data into an XML formatted string, which the WS service interface returns to its caller.

     [WebMethod] public string GetCategories() {   try   {     // Create a business service fa ade (BSF) object     BusinessServiceFacade bsf = new BusinessServiceFacade();     // Call the GetCategories method on the BSF object     CategoriesData cd = bsf.GetCategories();     // Convert the CategoriesData dataset into XML, and return it     return cd.GetXml();   }   catch (XBikesInteropException intExp)   {     //.. Error handling code   } } 

  5. According the WS-I Basic Profile 1.0, Web services should support the SOAP protocol but not the HTTPGet or HTTPPost protocols. To remove support for the protocols from the Web service, the XBikes developers added the following code to the <system.web> section of the Web.config file for the Web service.

     <webServices>   <protocols>     <remove name="HttpGet" />     <remove name="HttpPost" />   </protocols> </webServices> 

To test the Web service, the developers built and ran the Web service project in Visual Studio .NET 2003. When you run an ASP.NET Web service project, a test page appears automatically in the browser. The test page contains hyperlinks that allow you to invoke each of the Web service methods. The test page also has text boxes for you to enter input values if necessary. After you invoke a Web service method, another browser window opens showing the XML response from the Web service method.

Note

Although the XBikes sample application is not fully WS-I Basic Profile 1.0 compliant, the developers used toolkit capability, available at the time of development, to come as close as possible to Basic Profile compliance.

Creating the Interoperability Adapters in J2EE

Earlier sections described how to create the Web service service interface in .NET Framework; after that is created, you can proceed to build the J2EE interoperability adapters. This section describes how. The procedures to do this are as follows:

  • Build the Java data classes based on the XML Schema.

  • Build a Web service proxy using the tools provided by your Web services stack.

  • Create an adapter for either the entire service interface, or one for each use case.

    Note

    In XBikes, the developers created an adapter for each use case.

The interoperability adapter calls the proxy, which then calls the Web service. The adapter also has to convert any Java data to and from the correct string/XML format based on the XML Schema.

The logic flow for an adapter is as follows:

  1. The application calls the adapter method.

  2. The adapter method converts any complex data to an XML string representation.

  3. The adapter method creates an instance of the Web service proxy.

  4. The adapter method calls the appropriate method in the Web service proxy.

  5. If the proxy returns data, the adapter method converts it into the correct Java format if necessary.

  6. The adapter method returns the data back to the calling application.

Figure 8.2 shows this in operation.

click to expand
Figure 8.2: Web service proxy operation with J2EE applications

The tasks to build the Java adapters are the following:

  1. Create the Java data types and XML mapping from the XML Schema.

  2. Create the Java Web service proxies from the WSDL for the .NET Framework Web service.

  3. Create the Java adapters to convert Java data and call the .NET Framework Web service.

The following sections describe how to perform these tasks.

Creating Java Data Types and XML Mapping from the XML Schema

Most Java XML products provide tools that allow you to create Java data types based on an XML Schema. Typically, these tools also create a mapping file that maps each field in the Java class to an element or attribute in the XML Schema.

Note

If your existing Java data types are already consistent with an XML Schema, you do not have to define intermediary Java classes. Simply define a mapping file that maps Java fields directly to elements and attributes in the XML Schema.

After you define the XSD-based Java classes, the next step is to write code that copies data from your original Java objects into objects of the XSD-based classes. You must then write code to serialize the XSD-based objects into an XML formatted string.

It is a good idea to put the serialization code into a helper class, and this is what the XBikes developers did. The helper class requires two methods for each type of data you want to write to a string or read from a string; the methods take a single parameter for the type of data you want to convert and return a single value of the converted type. The following method signatures show how to convert an Order object to and from an XML formatted string.

 public static String orderToString(Order o);
public static Order StringToOrder(String o);

The following steps illustrate how the XBikes developers created XSD-based Java classes from the CustomerData.xsd XML Schema described earlier in this chapter. The XBikes developers used GLUE to generate the XSD-based Java classes, and to create mapping files so that GLUE can serialize and deserialize Java objects to and from XML. The steps were:

  1. The developers created Java classes based on the CustomerData.xsd schema, using the schema2java tool provided by GLUE.

     schema2java CustomerData.xsd -g -p xbikes.common.dataconverters.customers 

  2. The schema2java command generated two Java files, CustomerData_TYPE.java and Customers_TYPE.java. The XBikes developers added these files to the Java project, placing them in the common package for easy access. The package destination was xbikes.common.dataconverters.customers.

  3. The schema2java command also generated a CustomerData.map file, which the GLUE serializer uses to map the fields to the correct XML elements and attributes. The XBikes developers copied this map file into the XBikesWeb\Web Content\WEB-INF\maps folder, which is where GLUE expects to find map files for XML serialization.

  4. Because GLUE is the chosen environment, the team added the Glue.jar file to the build path.

  5. Next, the developers wrote a Helper class to perform the conversion between Java data and strings. They named the new class CustomerConverter, and added it to the xbikes.common.dataconverters package.

  6. The developers added two methods to the CustomerConverter class, one to convert an XML string into a Java object, and the other to convert a Java object into an XML string. These methods use the GLUE serializer to read and write XML data from XSD-based CustomerData_TYPE and Customers_TYPE objects.

    1. The CustomerData_TYPE and Customers_TYPE data types exist solely to allow XML data to be serialized and deserialzed as Java objects; the rest of the J2EE application uses an existing Java class named CustomerData. Therefore the conversion methods in CustomerConverter are expressed entirely in terms of the CustomerData class, and they perform internal conversions between this data type and CustomerData_TYPE and Customers_TYPE.

    2. The following code listing shows the completed CustomerConverter class.

       package xbikes.common.dataconverters; import java.io.StringWriter; import xbikes.common.data.CustomerData; import xbikes.common.dataconverters.customers.CustomerData_TYPE; import xbikes.common.dataconverters.customers.Customers_TYPE; import xbikes.common.exceptions.XBikesInteropException; import electric.xml.Document; import electric.xml.io.IReader; import electric.xml.io.IWriter; import electric.xml.io.literal.LiteralReader; import electric.xml.io.literal.LiteralWriter; public final class CustomerConverter {     private static final String WRITER = "CustomerData";     private static final String NAMESPACE =         "http://tempuri.org/CustomerData.xsd";     public static CustomerData stringToCustomerData(String xml) throws         XBikesInteropException     {         try         {             // Need to convert the string into xml             Document d = new Document(xml);             IReader reader = new LiteralReader(d);             CustomerData_TYPE myCustomer =                 (CustomerData_TYPE)                 reader.readObject(CustomerData_TYPE.class);             Customers_TYPE customer = myCustomer.getCustomers();             CustomerData cd = new CustomerData();             cd.setAddress(customer.getEmailAddress());             cd.setCustomerID(customer.getCustomerID());             cd.setName(customer.getFullName());             cd.setPassword(customer.getPassword());             cd.setZip(customer.getZipCode());             return cd;         }         catch (Exception e)         {             System.out.println(e.getMessage());             throw new XBikesInteropException(e.getMessage());         }     }     public static String customerDataToString(CustomerData cd) throws         XBikesInteropException     {         try         {             // Move the data from the internal java classes into those 
      // generated by the schema tool Customers_TYPE customer = new Customers_TYPE(); customer.setCustomerID(cd.getCustomerID()); customer.setEmailAddress(cd.getAddress()); customer.setFullName(cd.getName()); customer.setPassword(cd.getPassword()); customer.setZipCode(cd.getZip()); CustomerData_TYPE customerData = new CustomerData_TYPE(); customerData.setCustomers(customer); // Write the object to a string, via the LiteralWriter / // Document and StringBuffer IWriter writer = new LiteralWriter(WRITER); writer.writeObject(customerData); writer.writeNamespace("", NAMESPACE); Document d = writer.getDocument(); String sCustomerData = ""; StringWriter sw = new StringWriter(); d.write(sw); sCustomerData = sw.getBuffer().toString(); return sCustomerData; } catch (Exception e) { System.out.println(e.getMessage()); throw new XBikesInteropException(e.getMessage()); } } }

The conversion methods in the CustomerConverter class allow CustomerData objects to be converted to and from the XML format defined by the CustomerData.xsd schema.

Creating Java Web Service Proxies

The next task is to generate the Java proxy classes for the .NET Framework Web service. Most Web service stacks provide a tool named wsdl2java (or similar), to generate Java proxy classes from WSDL.

The following steps describe how the XBikes developers used GLUE to generate Java proxy classes for the .NET Framework Web service:

  1. The developers used the Wsdl2java tool in GLUE to generate Java proxy classes and associated helper classes for the .NET Framework Web service.

     wsdl2java http://localhost/XBikes-BLL-WSServiceInterface/BLLWSServiceInterface.asmx?wsdl  -p xbikes.usecaseintropadapters.net.ws 

    This command generated a BLLWSWebServiceInterfaceHelper Java class and an IBLLWSServiceInterfaceSoap Java interface These Java types were located in xbikes.usecaseinteropadapters.net.ws Java package.

  2. The developers added the BLLWSWebServiceInterfaceHelper Java class and the IBLLWSServiceInterfaceSoap Java interface to the J2EE XBikesWeb project.

The Web service proxies enable the J2EE presentation layer to invoke Web service methods upon the .NET Framework Web service.

Creating the J2EE Interoperability Adapters

Now that you have the Java data classes and Web service proxies, you can define the interoperability adapters for the J2EE application. You can either define a single adapter for each Web service or define a separate adapter for each use case, depending upon the level of fine control you require. The XBikes developers created an adapter for each use case to give maximum flexibility.

The XBikes use cases follow the Command pattern. Each adapter class implements an interface named IUseCaseCommand and provides initialise and execute methods:

  • initialise — Sets up the use case adapter and assigns parameters for the upcoming action.

  • execute — Causes the use case adapter to perform its action.

Adapter classes are named after the command that they adapt. For example, AuthenticateCustomerCommandAdapter is the adapter class for the AuthenticateCustomerCommand command. The following code listing shows how the XBikes developers implemented the AuthenticateCustomerCommandAdapter adapter class.

 package xbikes.usecaseinteropadapters.j2ee.ws; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; import xbikes.bll.facade.BusinessServiceFacadeHome; import xbikes.common.data.CustomerData; import xbikes.common.data.ValueObject; import xbikes.common.dataconverters.CustomerConverter; import xbikes.common.interfaces.architecture.IUseCaseCommand; public class AuthenticateCustomerCommandAdapter implements IUseCaseCommand {     private String email;     private String password;     /**      * Constructor for AuthenticateCustomerCommandAdapter.      */     public AuthenticateCustomerCommandAdapter()     {         super();         this.email = "";         this.password = "";     }     /**      * @see xbikes.common.interfaces.architecture.IUseCaseCommand#execute()      */     public ValueObject execute() throws Exception     {         IBLLWSServiceInterface service;         service = BikesWebServiceHelper.bind();         String xml = service.authenticateCustomer(email, password);         CustomerData cd = CustomerConverter.stringToCustomerData(xml);         return cd;     }     public ValueObject execute(String pEmail, String pPassword) throws Exception     {         this.email = pEmail;         this.password = pPassword;         return this.execute();     }     /**      * @see
xbikes.common.interfaces.architecture.IUseCaseCommand#initialise(Object[]) */ public void initialise(Object[] params) { this.email = params[0].toString(); this.password = params[1].toString(); } }

This section described how to create interoperability adapters in Java and service interfaces in .NET that use Web services to interoperate. The next sections describe how to implement interoperability adapters and service interfaces using Ja.NET to provide higher performance interoperability solutions.




Application Interoperability. Microsoft. NET and J2EE
Application Interoperability: Microsoft .NET and J2EE: Microsoft(r) .Net and J2ee (Patterns & Practices)
ISBN: 073561847X
EAN: 2147483647
Year: 2003
Pages: 104

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net