Export (0) Print
Expand All
Expand Minimize

Web Services Interoperability Guidance (WSIG): BEA WebLogic 8.1 SP3 (8.1.3)

 

Simon Guest
Microsoft Corporation

September 2004

Applies to:
   Microsoft .NET Framework 1.1
   Microsoft Visual Studio .NET 2003
   BEA WebLogic 8.1 SP3 (8.1.3)

Summary: Based on a series of unit tests between Microsoft .NET and BEA WebLogic 8.1.3, this article shows a series of scenarios and recommendations for achieving Web services Interoperability between the two. (13 printed pages)

Contents
Web Services Interoperability: "Is It Going to Work?"
IDE Recommendations
"On the Wire" Recommendations
Conclusion

Web Services Interoperability: "Is It Going to Work?"

It was just after a presentation I'd given at TechEd 2003 in Dallas. The topic was Interoperability with Web services and I was standing at the podium, taking questions from the attendees.

"I have a question," remarked one of the attendees. "One of our major applications is written in J2EE." I nodded in acceptance. "I'm looking to build an application in .NET and interoperate using Web services." There was a pause. "Is it going to work?"

A great question, I thought. I've found that there are two ways to answer these types of questions. If I've had experience with the said platform, then chances are I can talk through some of the recommendations and best practices that I've come across. If I've not had such experience, then I tend to allude to more general guidelines, and start talking about vendor commitment and the WS-I.

This conversation with the attendee got me thinking. How can we really test Web services interoperability between two platforms? Something that may work for one may not work for another. This is also going to change based on the types of data being passed across the wire.

To try and answer the "is it going to work" question, after the presentation I created a set of unit tests—both in NUnit (for .NET) and JUnit (for Java). These tests were designed to simulate common operations between .NET and leading J2EE platforms that support Web services. For example: Passing a message with a Boolean, then a String, then a Long, then a Float. As the tests moved on, they got increasingly complex. Create an array. Nest types within types. Include some null values. Based on this, I then extended the tests to other platforms.

The interesting outcome of the tests is not how well each of the platforms does; rather, it raises the question: What guidance can be extracted based upon the findings? This article is designed to do exactly that. Based on the experience of running the tests, the goal of this document is to share recommendations for Web services interoperability today between the two platforms. The platforms that this article covers are Microsoft .NET Framework 1.1 and BEA WebLogic 8.1 SP3 (8.1.3).

About the Recommendations

I've divided the recommendations up in to two sections: IDE Recommendations and "On the Wire" Recommendations.

IDE Recommendations covers recommendations for using either Visual Studio .NET 2003 or BEA WebLogic Workshop 8.1.3 in order to create interoperable Web services. Many of the recommendations will cover the generation of client proxies for both platforms and show examples where this applies. This section is not designed as an exhaustive run through of each of the IDEs, but instead focuses on specific issues that were raised during the tests.

The "On the Wire" recommendations section looks at situations where one platform was expecting a certain value, and the other platform may have sent something else—implying some type of wire compatibility issue between the two. For this section, the purpose of this article is to highlight the issue and offer any recommendation for workarounds.

Finally, through customer and partner feedback, I hope that this becomes a living set of recommendations that can be updated with new advice that I may have missed or alternatives to the guidance proposed.

IDE Recommendations

The recommendations listed in this section cover IDE (or design time) recommendations. They are in no particular order of priority, but some do follow sequentially. Each of the recommendations covers the platform it applies to, an example scenario and the actual recommendation itself.

Recommendation: Use "typePackageName" Parameter for Arrays

Applies To: Client written using BEA WebLogic 8.1.3 calling Web service in .NET

Scenario: Imagine two Web services, both deployed using Microsoft .NET. The first Web service is used to create new customers. It has a method that looks like the following:

public Response CreateCustomer(Customer customer)

The second Web service is used to display current orders based on a current customer. It has a method signature that looks as follows:

public Orders GetOrders(Customer customer)

As these two Web services both share the same data (the customer), it is possible—and even recommended in the principles of service design—that the schema for the customer is shared between the two services.

In this example, the schema for the customer data type is represented in XSD as follows:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="Customer" targetNamespace="http://myorg.org/types/customer.xsd" 
elementFormDefault="qualified"
xmlns="http://myorg.org/types/customer.xsd" 
xmlns:mstns="http://myorg.org/types/customer.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="Id" type="xs:string" />
<xs:element name="Created" type="xs:dateTime" />
<xs:element name="Name" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="Customer" type="Customer"></xs:element>
</xs:schema>

As shown above, the namespace of the customer is http://myorg.org/types/customer.xsd. Based on this, the xsd.exe tool (a utility in the .NET Framework SDK) has been used to generate the class from this XSD document (xsd.exe /c customer.xsd). This is used for the Web services described above.

To generate the client proxies in BEA WebLogic 8.1.3, the clientgen tool is used. Within an ANT script, clientgen is invoked using the following targets.

<target name="Service1">
<clientgen wsdl="http://${host}:${port}/crm/Service1.asmx?WSDL"
 clientJar="${libdir}/${libjar}"
 packageName="org.myorg.service1" />
</target>

<target name="Service2">
<clientgen wsdl="http://${host}:${port}/purchasing/Service1.asmx?WSDL"
 clientJar="${libdir}/${libjar}"
 packageName="org.myorg.service2" />
</target>

Everything works well and the client can call the two Web services with no problems. BEA WebLogic Workshop shows the content of the generated JAR file containing the proxy files for both services as shown in Figure 1.

ms998265.wsi-interop-bea-01(en-us,MSDN.10).gif

Figure 1. JAR file outline contain both services and shared types

The shared Customer type is located in the org.myorg.types.customer.xsd package and is used by both services.

Let's however imagine that the signature for the two .NET Web services changes from:

public Response CreateCustomer(Customer customer)
public Orders GetOrders(Customer customer)

To this:

public Response CreateCustomer(Customer[] customer)
public Orders GetOrders(Customer[] customer)

In the interest of efficiency, it has been decided that the Web services are going to accept multiple customers for each method.

The .NET Web services are modified and the client proxy is regenerated using the clientgen ANT task. Investigating the resulting JAR file shows a newly generated class called ArrayOfCustomerSequenceCodec.class.

ms998265.wsi-interop-bea-02(en-us,MSDN.10).gif

Figure 2. The generated ArrayOfCustomer class

When the BEA client is re-run however, both the calls to the Web services fail. The .NET Web services believe that no value (or a NULL value) was passed to the service.

Recommendation:

If we take a look at the SOAP envelope for one of the failing calls to the .NET Web services we can see why things are not working:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<env:Body>
<n1:CreateCustomer xmlns:n1="http://myorg.org/services/crm">
<n1:customer xmlns:n1="http://myorg.org/services/crm" 
xmlns:n2="http://myorg.org/types/customer.xsd" 
xsi:type="n2:ArrayOfCustomer">
<n3:Customer xmlns:n3="http://myorg.org/services/purchasing">
<n2:Id xmlns:n2="http://myorg.org/types/customer.xsd">12345</n2:Id>
<n2:Created xmlns:n2="http://myorg.org/types/customer.xsd">
2005-01-20T00:00:00-08:00</n2:Created>
<n2:Name xmlns:n2="http://myorg.org/types/customer.xsd">Test customer</n2:Name>
</n3:Customer>
</n1:customer>
</n1:CreateCustomer>
</env:Body>
</env:Envelope>

As can been seen in the trace, the client proxy generation has become confused with the multiple XML namespaces. The array (customer) defines namespaces for both the CRM Web service and the shared schema type, but the element within the array (Customer) defines a namespace that maps to the Purchasing service.

As a result, neither .NET Web service are able to deserialize the request correctly (the CRM Web service believes that the array is empty, the Purchasing Web service believes that the array is not intended for the service).

To correct this, we can explicitly set the generated package for types when the clientgen tool is run. This is done by adding a typePackageName parameter to the ANT task. In our scenario, this looks like the following:

<target name="Service1">
<clientgen wsdl="http://${host}:${port}/crm/Service1.asmx?WSDL"
 clientJar="${libdir}/${libjar}"
 packageName="org.myorg.service1"
 typePackageName="org.myorg.service1" />
</target>

<target name="Service2">
<clientgen wsdl="http://${host}:${port}/purchasing/Service1.asmx?WSDL"
 clientJar="${libdir}/${libjar}"
 packageName="org.myorg.service2"
 typePackageName="org.myorg.service2" />
</target>

When the clientgen task is run, the array class for the customer is created specific to the service in question, as shown in Figure 3.

ms998265.wsi-interop-bea-03(en-us,MSDN.10).gif

Figure 3. Each service now contains specific array classes

After adjusting the BEA client-side code to use the newly generated type (which involves using org.myorg.service1.customer instead of org.myorg.types.customer.xsd), things work as expected.

If we now re-check the SOAP trace we can see that the XML namespace for the elements within the array match the array construct.

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<env:Body>
<n1:CreateCustomer xmlns:n1="http://myorg.org/services/crm">
<n1:customer xmlns:n1="http://myorg.org/services/crm" 
xmlns:n2="http://myorg.org/types/customer.xsd" 
xsi:type="n2:ArrayOfCustomer">
<n1:Customer xmlns:n1="http://myorg.org/services/crm">
<n2:Id xmlns:n2="http://myorg.org/types/customer.xsd">12345</n2:Id>
<n2:Created xmlns:n2="http://myorg.org/types/customer.xsd">
2005-01-20T00:00:00-08:00</n2:Created>
<n2:Name xmlns:n2="http://myorg.org/types/customer.xsd">
Test customer</n2:Name>
</n1:Customer>
</n1:customer>
</n1:CreateCustomer>
</env:Body>
</env:Envelope>

Recommendation: Use IsNil() to Validate Null Nested Types

Applies To: Client in .NET calling a Web service written using BEA WebLogic 8.1.3

Scenario: Imagine a Web service created using BEA WebLogic 8.1.3 that exposes the following Web Method:

public Orders GetOrders(Company company)

The Orders and Company type have been generated by first creating the types in XSD and then dropping them into the Schemas folder within BEA WebLogic workshop. This has generated corresponding XML beans for each complex type. The Company type is defined in XSD as follows:

<xs:element name="Address" nillable="true" type="Address" />
<xs:complexType name="Address">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Line1" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="Line2" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="City" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="State" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="PostalCode" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="CountryCode" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="Company" nillable="true" type="Company" />
<xs:complexType name="Company">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
<xs:element minOccurs="1" maxOccurs="1" name="Founded" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" name="MailingAddress" type="Address" nillable="true" />
</xs:sequence>
</xs:complexType>

Notice how the Company type contains a field for the mailing address. This is of type Address.

Let's assume that this Web service works as expected, however we want to test whether a mailing address has been set for the company passed. You may imagine that the code used to check this could look similar to the following:

if (company.getMailingAddress != null)
{
   // read the mailing address and do something
}

When the mailing address is set to null however, the WebLogic Web service still believes it contains some data (i.e. company.getMailingAddress is never asserted to null).

Recommendation

Because the generated customer and mailing address are XML beans, they still contain a structure. Therefore, testing company.getMailingAddress to null is testing whether the structure of the XML Bean exists as opposed to the value. If a valid structure exists, this will always be false.

To test whether the actual mailing address is null, it is recommended to use the IsNil() method on the data type. This will test whether the value of the passed type is null. For our scenario, this would look as follows:

if (!customer.getMailingAddress.IsNil())
{
   // read the mailing address and do something
}

Following this recommendation returns the correct result.

Recommendation: Use compareTo() Method When Comparing Dates

Applies To: BEA WebLogic 8.1.3 client calling a .NET Web service

Scenario: Imagine that you have a client created using BEA WebLogic 8.1.3 that is consuming a Web service based in .NET. The Web service returns data which contains a xsd:dateTime value.

On the client, this will be deserialized to a type of java.util.Calendar. To validate a date it may be tempting to use:

if (service.getDate() == myOrder.getDate())
{
   //do something
}

Due to potential inaccuracies to how the xsd:dateTime format is passed across the wire and serialized/deserialized, this expression may fail – although the dates look alike.

Recommendation: For accuracy, it is recommended to use the compareTo method in order to compare with a locally held date. Using this method will help prevent any ambiguity between the passed date/time and the local value.

For example:

if (service.getDate().compareTo(myOrder.getDate() == 0)
{
   // do something
}

Recommendation: Use addNewItems() Construct When Using Empty Arrays

Applies To: .NET Client calling Web service in BEA WebLogic 8.1.3

Scenario: Imagine that you have a Web service hosted in BEA WebLogic 8.1.3. The Web service returns an order. The method for this could look similar to the following:

public Order GetOrders()

The order contains a number of items. By creating an XSD document and using the Schema folder in BEA WebLogic Workshop you have generated a series of XML Beans. The XSD for the Order and OrderItem types are defined as follows:

<xs:element name="OrderItem" nillable="true" type="OrderItem" />
<xs:complexType name="OrderItem">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Id" type="xs:long" />
<xs:element minOccurs="1" maxOccurs="1" name="Date" type="xs:dateTime" />
<xs:element minOccurs="1" maxOccurs="1" name="Fulfilled" type="xs:boolean" />
<xs:element minOccurs="1" maxOccurs="1" name="Type" type="xs:byte" />
<xs:element minOccurs="1" maxOccurs="1" name="UnitAmount" type="xs:double" />
<xs:element minOccurs="1" maxOccurs="1" name="Discount" type="xs:float" />
<xs:element minOccurs="1" maxOccurs="1" name="Qty" type="xs:int" />
<xs:element minOccurs="1" maxOccurs="1" name="Sequence" type="xs:short" />
<xs:element minOccurs="0" maxOccurs="1" name="Comments" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="Order" nillable="true" type="Order" />
<xs:complexType name="Order">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Id" type="xs:long" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="Items" type="OrderItem" nillable="true"/>
</xs:sequence>
</xs:complexType>

You wish to return an order to the client in .NET. You create the order, add new items but fail to populate them before returning. For example:

        // Create a new order from the XML Bean
        Order order = Order.Factory.newInstance();
        
        // Create some empty items
        OrderItem[] items = new OrderItem[3];
        order.setItemsArray(items);
        
        // return the order
        return order;

Although this may look correctly formed, when the .NET client calls this Web service an exception will be generated on the BEA WebLogic 8.1.3 Server.

<Sep 1, 2004 3:39:15 PM PDT> <Warning> <WLW> <000000> 
<Id=top-level; Method=Service1.GetOrders();
Failure=java.lang.IllegalArgumentException: Array element null [ServiceException]>
<Sep 1, 2004 3:39:15 PM PDT> <Error> <WLW> <000000> 
<Failure=java.lang.IllegalArgumentException: Array element null [ServiceException]>

Recommendation

If you plan to return an object that contain multiple items, some of which may be initialized, it is recommended to add them individually. For example:

        Order order = Order.Factory.newInstance();
        OrderItem i1 = order.addNewItems();
        OrderItem i2 = order.addNewItems();
        OrderItem i3 = order.addNewItems();        
        return order;

The above code will correctly return an empty array of three elements to the calling .NET client.

"On the Wire" Recommendations

The recommendations listed in this section cover "On the Wire" (or run time) recommendations. They are in no particular order of priority, but some do follow sequentially. Each of the recommendations covers the platform it applies to, an example scenario and the actual recommendation itself.

Recommendation: Avoid sending Null Date and Time Values over Web Services

Applies To: .NET Client and .NET Web service

Scenario: You have a Web service running on BEA WebLogic 8.1.3 that returns a date and time. Either through a single method:

public java.util.Date Method1()
{
return null;
}
Or, a method that returns an object that contains a date and time value:
public OrderItem Method1()
{
return new OrderItem();
}

public class OrderItem
{
public long id;
public java.util.Date created;
}

When this method is consumed by a client in Microsoft .NET, and the date/time value is null (or not initialized), an exception is raised. The exception type is System.FormatException, and the client believes that there is an error is the returned XML file.

Recommendation

The reason for this error is that in Java, java.util.Date and java.util.Calendar are classed as reference types. In .NET, System.DateTime is considered a value type. As you may know, reference types can be null, whereas value types cannot. In v1.1 of the .NET Framework, there is no understanding of null date and time values.

To overcome this, you can take one of three approaches:

  1. Embed the date/time value in a complex object (as shown above with the OrderItem type). Set the value of complex object to null to indicate a null date/time value. Within the unit tests, this method is proven to work.
  2. Have the BEA WebLogic 8.1.3 Web service and .NET client agree on a default value (e.g. 1/1/1900 12.00am) to represent a null value.
  3. Send the date/time as a string across the Web service. Use either a null or empty string to represent a null date.

Conclusion

In this article you have seen some of the recommendations for achieving interoperability between Web services created using Microsoft .NET Framework 1.1 and BEA WebLogic 8.1.3.

Through creating this article, it is apparent that interoperability using Web services developed on the Microsoft .NET Framework 1.1 and BEA WebLogic 8.1.3 is most definitely achievable today. The results and observations from running the unit tests confirm this, and this is validated by the number of organizations who are developing applications today that span both platforms.

Overall, the results are testament to how well both Microsoft and BEA have implemented the WS-I Basic Profile 1.0 for their implementations of Web services.

I would like to thank Sarvashrestha Paliwal, Avadh Jain, Nilesh Jain, Sachin Joshi, Anurag Katre, Abhilasha Mishra, Ajay Panditrao, Debasis Patil, and Sachin Patil (all from Tata Consulting Services) for their assistance in creating this article.

 

About the author

Simon Guest is a Program Manager in the Architecture Strategy team at Microsoft Corporation, and specializes in interoperability and integration. Simon holds a Masters Degree in IT Security from the University of Westminster, London, and is the author of the Microsoft .NET and J2EE Toolkit (Microsoft Press, Sept. 2003).

Simon can be reached via his blog at http://www.simonguest.com.

Show:
© 2014 Microsoft