Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / containers / virtual-machine

.NET/Java Interoperability using Service Interface and DTO Architecture Patterns

4.73/5 (8 votes)
7 Feb 2011CPOL5 min read 50.8K   234  
This article shows you how to build a simple cross-platform interoperation solution between .NET and Java using the Service Interface and Data Transfer Object (DTO) architecture patterns

Introduction

Interoperability between custom-developed distributed systems running on Java/Linux platform and .NET/Windows remains a challenge even to this day. RPC based solutions like the “COM-CORBA bridge” address it but introduce a major liability in terms of brittle coupling. Several propriety and open source solutions based on web services, database integration and remoting bridges also attempt to address this problem. However such solutions often comprise deep and (in some cases expensive) software stacks introducing accidental complexity, large resource footprint, steep learning curve and performance degradation.

In this article, I present an architectural solution for cross-platform interoperation. The architecturally significant aspect of this solution is to surface, define and govern the structure and exchange of data across the two platforms. The architecture is based on ServiceInterface and Data Transfer Object (DTO) patterns.

The reference implementation demonstrates using this architecture to integrate two business systems, “Fulfillment Center” running on Java/Linux tier and “CustomerOrderProcessing” running on .NET/Windows.

Background

Service Interface

The service interface implements the contract between the consumer and provider. This contract allows them to exchange information even if they are on different systems. The service interface is responsible for all of the implementation details needed to perform this communication.

Image 1

Data Transfer Object

Data transfer object is a distribution pattern where you create a serializable object to transfer state between two systems. DTO has no behavior and its serialization/deserialization is handled by an assembler object.

Image 2

XSD

XML Schema or XSD provide constructs to express data types in a platform/language agnostic format. For example, a data type to define Customer Address information in XSD will look like:

XML
<xs:complexType name="Address"> 
 <xs:sequence> 
  <xs:element name="name" type="xs:string" /> 
  <xs:element name="zip" type="xs:integer" /> 
  <xs:element name="city" type="xs:string" /> 
  <xs:element name="country" type="xs:string" /> 
 </xs:sequence> 
</xs:complexType> 

XXsd2Code

XXsd2Code is an open source code generator to generate serializable Java, C#, C++ and C++/CLI classes from XSD.

Problem

“Fulfillment Center” application is part of a warehouse management system that is written in Java and runs on RHEL. “CustomerOrderProcessing “ is part of a POS system written in .NET and running on Microsoft Windows 2008 server.

Use Case

Any new order processed by the CustomerOrderProcessing system needs to be sent to the "Fulfillment Center". Once the fulfillment system ships the order, it in turn notifies “CustomerOrderProcessing”.

SerInterfaceXXsd2Code/system-overall.gif

Solution Outline

  • Based on the DTO pattern, create a schema that represents the structure of the data interchange between the two systems. This is the interface contract.
  • Model this contract as an XSD.
  • Use XXsd2Code to code generate platform specific contracts in C# and Java.
  • Create a service interface for the “Fulfillment Center” application using TCP/IP as transport.
  • Create a service gateway for the “CustomerOrderProcessing“ that connects to this service interface.

Structure (Class Diagram) design.pdf

SerInterfaceXXsd2Code/design-class-diagram-1.gif

Behaviour (Sequence Diagram) design.pdf

SerInterfaceXXsd2Code/design-sequence-diagram-1.gif

Solution Implementation

Service Contract

  1. Defining the Contract

    Using any XSD editor, create the following data contract. I created this XSD using Microsoft VS 2005.

    SerInterfaceXXsd2Code/xsd.GIF

  2. Code generates C# classes

    Assuming the XXsd2Code.exe is in the same folder as the .xsd file, execute the following command:

    XXsd2Code.exe .\/.\/C# 

    This will code generate C# bindings for the xsd contract:

    C#
    namespace CustomerOrderAndFulfillment
    {
    { 
    namespace Contract
    [Serializable]
    public class FulfillmentRecord : ICloneable
    {
    public string FulfillmentRecordID;
    public bool IsBackOrder; 
    public FuzzyCondition StorageTemperature;
    ...
    }
    [Serializable]
    public class CustomerOrder : ICloneable
    {
    public string OrderNumber;
    public CreditRating Rating;
    public Address AddressInfo;
    public List<OrderItem> Orders;
    public CreditCardDetails CcInfo;
    ... 
    }
    }
  3. Code generate Java classes

    Execute the following command:

    XXsd2Code.exe .\/.\/Java 

    This will code generate Java bindings for the xsd contract:

    Java
    package CustomerOrderAndFulfillment.Contract; 
    public class FulfillmentRecord implements Cloneable
    {
    public String FulfillmentRecordID;
    public boolean IsBackOrder;
    public FuzzyCondition StorageTemperature; 
    ...
    }
    package CustomerOrderAndFulfillment.Contract; 
    public class CustomerOrder implements Cloneable
    {
    public String OrderNumber;
    public CreditRating Rating;
    public Address AddressInfo;
    public java.util.List<OrderItem> Orders;
    public CreditCardDetails CcInfo;
    ...
    }

Transport

  1. .NET TCP/IP Client

    Use the TcpClient and NetworkStream to write a simple TCP/IP client

    C#
    TcpClient client = new TcpClient(server,port);
    NetworkStream stream = client.GetStream(); 
    //Send Message
    Byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
    stream.Write(data, 0, data.Length)
    //Receive response – blocking call
    byte[] buffer = new byte[1024];
    stream.Read(buffer, 0, buffer.Length);
    return buffer;
  2. Java TCP/IP Server

    Using ServerSocket, BufferedReader and DataOutputStream write a simple TCP/IP deamon on port 1111.

    Java
    ServerSocket welcomeSocket = new ServerSocket(1111);
    Socket connectionSocket = welcomeSocket.accept();
    BufferedReader inFromClient = new BufferedReader(new 
    InputStreamReader(connectionSocket.getInputStream()));
    DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
    //get request message
    String request = inFromClient.readLine();
    //Send response
    outToClient.writeBytes(response);

Data Transfer Object

  1. Write an assembler class to Serialize and DeSerialize .NET service interface contract classes using XMLSerializer.

    Serialize method transforms the CustomerOrder object into a stream of bytes so that it is sent to the FulfillmentCenter server.

    C#
    static string SerializeCustomerOrder(CustomerOrder order)
    {
    XmlSerializer serializer = new XmlSerializer(typeof(CustomerOrder));
    using (MemoryStream memoryStream = new MemoryStream())
    {
    serializer.Serialize(memoryStream, order);
    return Encoding.UTF8.GetString(memoryStream.ToArray());
    }
    }

    DeSerialize converts the response from the FulfillmentCenter server into a strongly typed FulfillmentRecord object:

    C#
    static FulfillmentRecord DeSerializeFulfillmentRecord(byte[] stream)
    {
    XmlSerializer serializer = new XmlSerializer(typeof(FulfillmentRecord));
    using (MemoryStream memoryStream = new MemoryStream(stream))
    {
    return serializer.Deserialize(memoryStream) as FulfillmentRecord;
    }
    }
  2. Write an assembler class to Serialize and DeSerialize Java service interface contract classes using xstream.

    DeSerialize converts the request from the CustomerOrderProcessing into a strongly typed CustomerOrder object.

    Java
    static CustomerOrder DeSerializeCustomerOrder(String request)
    {
    XStream serializer = new XStream();
    RegisterXstreamConverters(serializer);
    return (CustomerOrder) serializer.fromXML(request);
    }

    Serialize transforms the FulfillmentRecord object into a stream of bytes so that it can be sent back to the CustomerOrderProcessing system.

    Java
    static String SerializeFulfillmentRecord(FulfillmentRecord 
    fulfillmentRecord)
    {
    XStream serializer = new XStream();
    RegisterXstreamConverters(serializer);
    return serializer.toXML(fulfillmentRecord); 
    }

    XMLSerializer and xstream XML compatibility.

    To make sure xstream generates XML that is compatible with XMLSerializer, use the following code:

    Java
    xs.aliasType("CustomerOrder", CustomerOrder.class);
    xs.aliasType("OrderItem", OrderItem.class);
    xs.aliasType("FulfillmentRecord", FulfillmentRecord.class); 
    xs.aliasType("Address", Address.class); 

XStream Emun Serialization

To force xstream serialize enums correctly write an Enum Converter:

Java
public class EnumSingleValueConverter< T extends Enum<T> > extends 
EnumConverter implements SingleValueConverter 
{
    private Class<T> enumType;
    public static <V extends Enum<V>> SingleValueConverter create(Class<V> 
    enumClass) { return new EnumSingleValueConverter<V>(enumClass); }
    private EnumSingleValueConverter(Class<T> newEnumType) 
    { this.enumType = newEnumType; }
    public boolean canConvert(Class type) { return type == enumType; }
    public Object fromString(String str) {
    Object r = null;
    Method m = enumType.getMethod("fromValue", String.class);
    r = m.invoke(null, str); 
    }
    public String toString(Object obj) { return obj.toString(); } 
} 

Using the Reference Implementation

Download and unzip the reference implementation, then follow these steps:

  1. Run the GenerateCrossPlatformDataBindings.bat.
  2. Open .\CustomerOrderProcessing-DotNetClient\CustomerOrderProcessing.sln and compile it.
  3. Open .\FulfillmentCenterSimple-JavaTcpIpServer using Eclipse or Netbeans.
  4. Export a runnable jar and name it FulfillmentCenterApplication.jar.
  5. Run the jar file using this command:
    java -jar FulfillmentCenterApplication.jar 
  6. Now run CustomerOrderProcessing using this command:
    CustomerOrderProcessing.exe 127.0.0.1 1111 

Note: If you modify the xsd (interface contract), then make sure to run the GenerateCrossPlatformDataBindings.bat again.

Integration Test

I used Eclipse Ganymede to create a runnable jar. To test my “Fulfillment Center” server application, I used Ubuntu 10.10 running as a VirtualBox guest on windows 7 host. Make sure to add a bridge network adaptor for the VM so that the Windows .NET app can reach the Java server running on the Ubuntu guest. Now run Java/Linux “Fulfillment Center” server application.

SerInterfaceXXsd2Code/server-app.gif

Now run the “CustomerOrderProcessing” .NET application. Make sure you point to the IPAddress of the Linux Virtual machine.

SerInterfaceXXsd2Code/client-dot-net.gif

Conclusion

The architectural solution described in the article enable you to:

  • Decouple interoperation/communication plumbing from the business applications using the Service Interface pattern.
  • Minimize number of calls between the two systems using the Data Transfer Object pattern.
  • Surface, define, govern and ensure compile time enforcement of the service contract by
    • creating platform agnostic data types using XSD
    • then code generating platform specific data types using XXS2Code

History

  • 7th February, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)