Christian Weyer- thinktecture
Buddhike de Silva- Readify
Kent Brown - Microsoft
Kirill Gavrylyuk - Microsoft
Douglas R. Bunting - Microsoft
Michael Green - Microsoft
Abu Obeida Bakhach - Microsoft
ON THIS PAGE
- Primitive Types
- .NET Server to Java Clients Mapping
- Java Server to .NET Client Mapping
- Arrays and Abstract Data Types
- Date & Time
- Binary Data
- Programming Language Constructs
- Wrapping up
As a developer it's almost inevitable running into XML-based Web Services these days. Literally, they are everywhere. Web Services are often available as either REST style services or SOAP messaging style services built using various tools and running in different operating environments. Therefore, a number of standards have been defined over the years in order to ensure seamless interoperability between Web Services running in different operating environments. However, it is still somewhat challenging to develop interoperable Web Services because of how different platform vendors support industry standards. This whitepaper is written based on an interoperability analysis carried out between .NET 4 and three major Java based platforms - IBM Web Sphere, Oracle WebLogic and Oracle Metro (referred as Java client/server systems hereinafter).
Interoperable Web Services use universal type system - XML Schema Definition (XSD) constructs to define the data and message structures used for communication. However, the advanced tools and runtimes used for Web Service development usually abstract the details of XSD (and other protocols) from the developer. As a result developers can elaborate their Web Services using the programming language artifacts they are familiar with and leave the heavy lifting to the tooling/runtimes to translate those into the standard format. Even though some developers favor developing their type system using XSD (also known as schema first development), interoperability issues still arise due to limitations in various platforms for some XSD features. In this whitepaper, our analysis primarily focuses on the interoperability problems that occur while translating XSD artifacts to various platform level artifacts and vice versa.
The interoperability analysis was done through an interoperability test bed we prepared in our lab. It consists of Ping and Echo style verification tests to test communication from Java clients to .NET web services and vice versa. The test bed targets the interoperability of primitive types, arrays and complex types and common programming language constructs such as inheritance and generic types. Ping and Echo style tests helps us to understand how these artifacts used in one platform map to another and ensure they can be successfully transmitted back and forth without loss of any data.
In order to keep the focus solely on the aforementioned list, we have defined some constraints while preparing the test bed. The test suite uses the standard basic HTTP binding. Therefore the interoperability features related to specialized WS-* family bindings are not going to be among our interests. We have also avoided addressing potential problems that may have been caused by known bugs or limitations in the respective tools (e.g. Proxy generation tools, Extensions to integrated development environments).
.NET Server to Java Clients Mapping
Table 1 illustrates how primitive types used in a .NET server are mapped to Java clients.
Table 1 Primitive type mapping from .NET servers to Java clients
Java Server to .NET Client Mapping
Table 2 illustrates how primitive types used in Java servers are mapped to .NET clients.
Table 2 Primitive type mapping from Java server to .NET client
Since the length of the Java type is larger than the length of the .NET type, a Java client could potentially send a value that does not fit into the allowed range. In that case the .NET server endpoint will respond with a SOAP fault containing the value that caused the problem.
Represents a 16-bit Unicode character.
Decimal type in .NET supports only approximately 29 significant digits. However, the BigDecimal type used in Java platforms allows arbitrary precision. This in turn can make Java servers/clients send values that are not within the supported range in .NET or values with a higher precision. In that case .NET endpoints attempt to round these values to the nearest available significant digit. If rounding fails, the .NET server endpoints respond to the client with a SOAP fault containing the value that caused the problem. .NET clients on the other hand, throw an exception with a message containing the erroneous value.
Arrays and Abstract Data Types
Arrays of primitive types
Arrays of all primitive types are mapped identically to the translation given in the primitive types section.
Metro - Special Case
Web Services hosted in Metro add the nillable="true" attribute to the corresponding XSD element (Listing 1) definition which causes an interesting side effect on .NET client side.
Listing 1 Array element definition
Primitive types in .NET are value types. In order to accept a null value as specified by the above schema element, one has to use Nullable<T>. Consequently, in the above example, WCF proxy generation tools generate a Nullable<int> instead of an int..
If a WCF client uses a Nullable<T>type, each null element is included in the outgoing message. But these null elements are not included in the array deserialized by Metro.
For example, if the following array is sent to a Metro Web Service using a Nullable<int>interface:
Listing 2 .NET array of nullable integers
The deserialized array seen by Metro will only have values 1, 2 and 4. This could potentially cause errors in the Web Services/Clients relying on the indexes of the values or the length of the array.
A possible workaround would be either modifying the WSDL and removing the nillable="true" attribute prior to code generation or modifying the generated code* to use int instead of Nullable<int>.
Date & Time
Date and time compatibility is probably the most common point of interoperability concerns. Subtleties around time zones and daylight savings make handling of date time values non-trivial. Listing 3 and Listing 4 illustrate how date and time types are mapped between Java and .NET Web Services.
Listing 3 Date and time mapping from .NET server to Java clients
Listing 4 Date and time mapping from Java servers to .NET client
The System.DateTime type in .NET supports three date and time formats that can be specified by one of the values in the System.DateTimeKind enumeration:
When DateTimeKind is Local, local time zone information is encoded to the serialized date/time string. For example, Local DateTime value; Sunday, May 23, 2010 07:17:54 PM returned from a Web Service running in Tokyo Standard Time (JST) zone is serialized as 2010-05-23T19:17:54.6951298+9:00. When Java clients deserialize this value the time zone offset is preserved in the XMLGregorianCalendar instance regardless of the client's time zone.
When .NET clients deserialize this value, absolute value of the serialized value is converted to the local time zone of the client. For instance, in the case of above example, the value read by a .NET client running in Australian Eastern Standard time zone is converted to Sunday, May 23, 2010 08:17:54 PM (note the hour difference in the time zones). Although this could be confusing when the time zone data is not present, this conversion is correct and it round trips work fine.
DateTime of kind DateTimeKind.Unspecified and DateTimeKind.Utc are round tripped without any change to the formatted value.
Which DateTime.Kind to use in a .NET Web Service is implementation specific. However, the use of Utc kind is a widely accepted mechanism to increase the interoperability.
The XMLGregorianCalendar type used in Java allows the construction of an XMLGregorianCalendar instance without a time offset. The serialized form of such an instance only contains the date portion and causes an error in the .NET deserializer.
.NET 3.5 introduced the System.DateTimeOffset type for handling date/time values with time zone information more generically. As shown in Listing 3, this type is represented as a complex type that has two fields that contain a date/time value and a time zone offset. Consequently the value serialized into the DateTimeproperty contains only the date/time value and OffsetMinutes property contains the time zone offset in minutes. Although the time zone is now explicitly specified in the structure, Java stacks are not aware of this type and do not deserialize the value into a XMLGregorianCalendar with the correct time zone. Therefore some programming is required to manually perform this calculation before date/time value is used by the application.
One important thing to be aware of is System.DateTimeOffset is only serialized when using System.Runtime.Serialization.DataContractSerializer.This would not be an option if you want to use System.Xml.Serialization.XmlSerializer by specifying System.ServiceModel.XmlSerializerFormatAttribute.
Listing 5 and Listing 6 illustrates how Globally Unique Identifier types are mapped between .NET and Java web services.
Listing 5 Guid mapping from .NET server to Java clients
Listing 6 Guid mapping from WebLogic server to .NET client
Listing 7 Guid mapping from Metro and Web Sphere server to .NET client
List is a widely used abstract data type to represent an array of items. In addition to dynamic resizing behavior, implementations usually contain methods for basic list based operations Add, Remove, Sort, Search etc., etc. Listing 8 and Listing 9 illustrate how List types are mapped between .NET and Java Web Services.
Listing 8 List mapping from .NET server to Java clients
Listing 9 List mapping from Java servers to .NET client
The Dictionary type has the ability to store an array of values keyed by another value. Implementations usually index the values by computing a hash of the key so that lookups can approximate O(1). Listing 10 and Listing 11 illustrate how Dictionary types are mapped between .NET and Java Web Services.
Listing 10 Dictionary mapping from .NET server to Java clients
In both generic and non-generic cases, deserialized key/value pairs in Java client side are not hashed. If hashing is required, client side programming is required to add incoming entries to a hashtable.
Listing 11 Dictionary mapping from Java servers to .NET clients
The Queue structure is used when it is required to process a list of items in First-In-First-Out (FIFO) order. Listing 12 and Listing 13 illustrate how Queue types are mapped between .NET and Java Web Services.
Listing 12 Queue mapping from .NET server to Java clients
Listing 13 Queue mapping from Java servers to .NET client
In both scenarios regular Queue operations are not available on the types generated. If Queue behavior is required, some programming is required to create and initialize an instance of a Queue with the received values. In that case it may be quite natural to assume that the first value in the items array is meant to be the oldest value of Queue. But this could be specific to a particular implementation. In Queues serialized by WCF endpoints, this is where _size, _head and _tailelements are useful. The .NET Framework's Queue implementation stores all the elements in the Queue in an array. The index of the oldest item in the array is stored in _head and the index for the next item to be enqueued is stored in _tail. Due to automatic resizing, size of the array could be larger than the actual number of items in it. The _size property indicates how many items are actually in the array. Consequently, the following algorithm can be used to successfully restore the state of the Queue in a Java client or server.
As you could notice in the schema representation in Listing 12, WCF emits schema annotations in the generated schema to notify that Queue<T> is being used on the server side. When extensibility support is available in Java tooling, developers can leverage this to modify the code generation process to use the built-in Queue<T> instead of generating a new type.
Stack is the opposite counterpart of Queue. It is used to process a list of items in Last-In-First-Out (LIFO) order. Listing 14 and Listing 15 illustrate how Stack types are mapped between .NET and Java Web Services.
Listing 14 Stack mapping from .NET server to Java clients
Listing 15 Stack mapping from Java servers to .NET client
In both scenarios regular Stack operations are not available on the types generated for clients. If Stack behavior is required, some programming is required to create and initialize an instance of Stack<T>with the received values.
Similar to the Queue, the order of the items in the array could be specific to the implementation. Stack<T> in the .NET Framework uses an array to store all the elements and the last item in the array represents the latest value in the stack. Therefore, Stack could be easily restored by adding the items in the array to a Java Stack<T> in the reverse order. Due to automatic resizing, the serialized array may contain more items than the actual number of items in the stack. Therefore the _sizeproperty should be used to find out the actual number of items in the stack.
The .NET framework 4 introduces Tuple<T>, Tuple<T1, T2>, Tuple<T1, T2, T3> and so on to represent finite sequences (also known as tuples)*. Listing 16 illustrates how Tuples used in .NET Web Services are interpreted in Java.
Listing 16 Tuple mapping from .NET service to Java clients
In both .NET and Java Web Services, binary data transfers are done using arrays of bytes. Listing 17 and Listing 18 illustrate how these types are compatible on both sides of communication. However, large binary data transfers are usually done by using protocol extensions such as MTOM or even by out of band mechanisms like HTTP chunked transfer or FTP.
Listing 17 Binary data mapping from .NET server to Java clients
Listing 18 Binary data mapping from Java servers to .NET client
Programming Language Constructs
Modern object oriented programming languages such as Java and C# offer various constructs to model hierarchical object models. The ability to preserve the usage of these constructs when crossing service boundaries really depends on language support and availability of a compatible XSD counterpart. In fact, Web Service designers should not rely on these language features when modeling their Web Service contracts in order to reduce the friction between clients and services. However, there is nothing that would prevent developers from using these features and therefore this section shows the interoperability of a few commonly used constructs.
Inheritance is an object oriented programming feature to form new classes (data types) from the classes that have been already defined. This kind of hierarchical typing is meant to increase code reusability. When building Web Services, or exposing existing object models as Web Services, it is a common practice to use types that are in the inheritance hierarchy. The following listings illustrate the interoperability of inherited types.
Listing 19 Inheritance mapping from .NET server to Java clients
Listing 20 Inheritance mapping from Java servers to .NET client
Both .NET and Java support basic inheritance in structures. However, additional object-oriented constructs such as abstract base classes and virtual members are not propagated to the clients.
Generics allow algorithms to be written in a data type agnostic manner. While they are mostly used to implement generalized algorithms (behavior), there is nothing that would prevent developers from using generics when modeling their contracts in code. They could end up in the contracts as a result of using custom generic types or using standard abstract generic implementations such as List<T>, Queue<T>, Stack<T>. Listing 21 and Listing 22 illustrate the generic type interoperability between .NET and Java Web Services.
Listing 21 Generics mapping from .NET server to Java clients
Listing 22 Generics mapping from Java servers to .NET client
When generic types are used in .NET and Java Web Services, client utilities only identify the generic base type. Therefore, data members exposed by generic parameter types are not available for the client programmers. This behavior is primarily due to a lack of first class support for representing generics in XSD. Consequently, schema documents generated by WCF are augmented with XSD annotations to hint the generic usage (as highlighted in Listing 20). These annotations can be used to customize Java client utilities when appropriate extensibility options are available.
Interfaces are a formal way of expressing what a class can do as opposed to expressing what it is and how it works. This gives the programmer the ability to implement a polymorphic behavior in a set of classes that confirms to a uniform interface. Although the intended usage of interface is expressing what a classcan do, it's also possible to use it to declare what it has because interfaces can contain properties (which are translated to pairs of getXxx and setXxx methods by the compiler).
When creating Web Services with WCF it is possible to return an interface from an operation. However, the way it works makes it impossible to interoperate with a Java client. For example, assume there's an interface, data contract and a service contract with an operation that expects an instance of a type implementing interface as follows.
Listing 23 .NET service operation returning an interface
Although this does not cause any compile time or runtime errors, the runtime generated schema to represent this service does not include the Personstructure in it. As shown in Listing 24 all it has is an element with its type set to xs:anyType.
Listing 24 Schema representation of an interface
Consequently a Java client would not be able to figure out that this service is expecting an object that implements the IPerson interface.
One way to work around this issue is by using the ServiceKnownTypeAttribute attribute to indicate that the Person class implements the IPerson interface as shown in Listing 25.
Listing 25 Using ServiceKnownTypeAttribute attribute
While that includes Person in the schema, it does not change the request type shown in Listing 24. Therefore some form of out-of-band communication between the developer teams would be required to elaborate the expectations making the use of interfaces unfriendly from an interoperability perspective.
Use of interfaces also works when System.Runtime.Serialization.NetDataContractSerializer is used. However, "System.Runtime.Serialization.NetdataContractSerializer"is not applicable for WCF to JAX-WS scenarios as it serializes the CLR type information.
As Web Services become more predominant in enterprise solutions comprised of distributed systems running on different operating environments, interoperability becomes a critical concern. This whitepaper discussed the data type interoperability between Web Services built with .NET Framework 4 and Web Services built with Oracle Weblogic, IBM Web Sphere and Oracle Metro.
Some illustrations used in paper are considered as bad practices for designing Web Service contracts. This paper acknowledges those shortcomings but provides those samples nevertheless to demonstrate the consequences. For more information on interoperability visit: http://msdn.microsoft.com/en-us/netframework/webservicesinterop.aspx.
 Changing generated code is generally not a good idea and also not practical in large solutions because the changes are lost when the code is regenerated. That adds an extra maintenance cost.