Web Services Interoperability between J2EE and .NET - Part 3

Namespaces and sharing XSD schemas

In both J2EE technology and .NET, it is quite common to share XSD schemas among multiple Web services. In fact, it is one of the best practices to share XML schemas for the sake of modular design and reusability. The XML tag: import and include, are used just for this purpose. For example, you can design a schema for a Product type for a merchandise warehouse, as shown in Listing 5:

Listing 5. A Product type

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace=
"http://catalog.warehouse.com" xmlns:Product="http://catalog.warehouse.com">
<complexType name="Product">
<sequence>
<element name="_name" type="string"></element>

<element name="_int" type="int"></element>
</sequence>
</complexType>
</schema>

The Product type is qualified under the namespace http://catalog.warehouse.com. It can be imported into the WSDLs for other Web services managing the inventory. Imagine that the order department has an Order Web service implemented in C#, as shown in Listing 6:

Listing 6. An order Service in .NET

	[WebService(Namespace="http://order.warehouse.com/service")]
public class OrderProductService: System.Web.Services.WebService
{
[WebMethod]
[XmlInclude(typeof(Product))]
public string OrderProducts(Product[] products)
{
int len = products.Length;
//do the order
return "Total number of orders processed: " + len;
}
}

The inventory department has an inventory Web service to restock products to its inventory; it must reuse the same Product type, as shown in Listing 7:

Listing 7. An inventory Service in .NET

	[WebService(Namespace="http://inventory.warehouse.com/service")]
public class InventoryProductService: System.Web.Services.WebService
{
[WebMethod]
[XmlInclude(typeof(Product))]
public string RestockProducts(Product[] products)
{
int len = products.Length;
//add to the inventory
return "Total number of products added: " + len;
}
}

Now that there are three namespaces: http://catalog.warehouse.com for the Product type, the http://order.warehouse.com/service for the Order Web service and the http://inventory.warehouse.com/service for the Inventory Web service. At first look, there doesn't seem to be any potential name conflicts. According to the previous two sections: The three namespace URIs are fully qualified; the domain name for each Web service is unique; even the Web services class names are different.

But still, the problem occurs. The problem occurs when arrays are passed, as in the two Web methods in Listing 6 and Listing 7.

If you build a J2EE project to integrate both the order service and the inventory service, and order a list of products or restock a list of products, only one Web service will be correctly executed; the other one will silently fail because of an empty array of products received, even though the J2EE client indeed sends both with a filled array of products. Why?

You have to investigate the SOAP traffic between the J2EE client and the .NET Web services to look for the answer.

Listing 8. The SOAP request for the .NET order service

<soapenv:Body>

<OrderProduct xmlns="http://order.warehouse.com/service">
<products>
<Product>
<_name xmlns="http://catalog.warehouse.com">Computer</_name>
<_qty xmlns="http://catalog.warehouse.com">10</_qty>

</Product>
<Product>
<_name xmlns="http://catalog.warehouse.com">Monitor</_name>
<_qty xmlns="http://catalog.warehouse.com">20</_qty>
</Product>

</products>
</OrderProduct>
</soapenv:Body>

Listing 9. The SOAP request for the .NET inventory service

<soapenv:Body>

<RestockProduct xmlns="http://inventory.warehouse.com/service">
<products>
<Product xmlns="http://order.warehouse.com/service">
<_name xmlns="http://catalog.warehouse.com">Computer</_name>
<_qty xmlns="http://catalog.warehouse.com">10</_qty>

</Product>
<Product xmlns="http://order.warehouse.com/service">
<_name xmlns="http://catalog.warehouse.com">Monitor</_name>
<_qty xmlns="http://catalog.warehouse.com">20</_qty>
</Product>

</products>
</RestockProduct>
</soapenv:Body>

Compare the XML representations of the orders array and the products array in Listing 8 and Listing 9. The array element in the request to the Inventory service is qualified by the namespace URI of the Order service -- http://order.warehouse.com/service -- so the Inventory Web service does not see any product being sent to it.

So, where is the root of the problem? The problem is in the .NET WSDLs and the helper classes that the Application Developer JAX-RPC tools generates. Listing 10 shows the WSDL for the order service in relation to the ArrayOfProduct[]:

Listing 10. Portion of the WSDL for the .NET Order service in relation to the ArrayOfProduct type

xmlns:s1="http://catalog.warehouse.com" 
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:s0="http://order.warehouse.com/service"
targetNamespace="http://order.warehouse.com/service"
.......
<types>
<s:schema elementFormDefault="qualified" targetNamespace=
"http://order.warehouse.com/service">

<s:import namespace="http://catalog.warehouse.com" />
.......
<s:element name="Product" type="s1:Product" />
</s:schema>
<s:schema elementFormDefault="qualified" targetNamespace=
"http://catalog.warehouse.com">
<s:import namespace=
"http://order.warehouse.com/service" />
<s:complexType name="ArrayOfProduct">
<s:sequence>

<s:element minOccurs="0" maxOccurs="unbounded" ref="s0:Product" />
</s:sequence>
</s:complexType>

The schema ArrayOfProduct is under the targetNamespace http://catalog.warehouse.com, but its element references s0:Product, where s0 is http://order.warehouse.com/service.

Recall how the namespace URI is mapped to the Java package name when the JAX-RPC client stub classes are created; an ArrayOfProduct class is created under the package com.warehouse.catalog, and a helper class ArrayOfProduct_Helper binds its Product field to the namespace s0, which is http://order.warehouse.com/service (see Listing 11).

Listing 11. The helper class binds the Product field to the Order Web service namespace

public class ArrayOfProduct_Helper {
// Type metadata
private static com.ibm.ws.webservices.engine.description.TypeDesc typeDesc =
new com.ibm.ws.webservices.engine.description.TypeDesc(ArrayOfProduct.class);

static {
com.ibm.ws.webservices.engine.description.FieldDesc field = new com.ibm.ws.webservices.engine.description.ElementDesc();
field.setFieldName("product");
field.setXmlName(com.ibm.ws.webservices.engine.utils.QNameTable.createQName("http://order.warehouse.com/service", "Product"));
field.setXmlType(com.ibm.ws.webservices.engine.utils.QNameTable.createQName("http://product.warehouse.com", "Product"));
field.setMinOccursIs0(true);
typeDesc.addFieldDesc(field);
};

This is not good news. Both of the clients for the Inventory and the Order Web services share the same ArrayOfProduct class under the package com.warehouse.catalog, but its class field binds to a particular Web service; in this case it is the Order service. That's precisely what you can see in the SOAP request message in Listing 7, and this is what is causing the problem.

In this scenario, there is no violation of specifications from either side. There are no ideal solutions. You must first identify the problem, and there are two things you can do to solve this namespace binding conflict:

  • When the first client proxy files are created, refactor the com.warehouse.catalog to another package name; when the second client proxy files are generated, both of them will have the correct namespace binding.
  • Use the same techique as shown in Figure 1 when generating the client proxy files; map the http://catalog.warehouse.com to a distinct namespace for each Web service.

It is a good practice to share XSD schemas, but Web service programmers must be aware of the potential namespace problems like this one and know how to fix it when it occurs.

You might also like...

Comments

About the author

Wangming Ye United States

Wangming Ye is an IBM Certified Enterprise Developer and a Sun Certified Enterprise Architect for J2EE Technology. He began as a developer in the DCE/DFS department at Transarc Corporation (late...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“An idiot with a computer is a faster, better idiot” - Rich Julius