Chapter 1 described three main scenarios for interoperability between .NET Framework and J2EE applications:
Integrating .NET Framework components at the Presentation tier.
Integrating .NET Framework components at the Business tier.
Implementing asynchronous interoperability.
The interoperability architecture and implementation strategies in this guide apply to all three scenarios. However, certain exceptions and restrictions apply, such as when you cannot modify an existing application, regardless of whether the application runs on .NET Framework or J2EE. The XBikes sample application takes a middle path — it does not represent the ideal scenario, with both .NET Framework and J2EE applications under development at the same time by the same programming teams. However, it does not take the rigid approach where you cannot modify the existing application at all.
Chapters 4 and 5 showed that you have several interoperability mechanisms to choose from:
.NET Remoting with runtime bridges.
Asynchronous interoperability through a message queuing system.
Each of these mechanisms applies best in different scenarios. Web services are best when you want to design your applications to use open standards and for compatibility with future applications. Runtime bridges such as Ja.NET and JNBridgePro are best for when you need high performance. Asynchronous interoperability usually involves only Business to Data tier interaction. However, the interoperability requirements for your application are likely to change, although hopefully not as quickly as the Web services specifications.
Your application design should reflect the reality that the interoperability mechanism it uses will probably change over time. Therefore, it is recommended that you implement additional interoperability layers and components to abstract the various connection points between your .NET Framework and Java applications.
These interoperability layers and components correspond to how you implement a service oriented architecture. The next section details these interoperability layers, makes recommendations for implementing them, and refers to how the developers implemented these in the XBikes sample application.
A connection point represents at a basic level, a client/server relationship where a client component consumes the services of provider. The diagrams in Chapter 1 show a matrix of possible connection points. Figure 6.4 demonstrates a basic connection point.
Figure 6.4: Simple connection point example
In two multi-tiered applications, there can be multiple connection points. Figure 6.5 shows a high level diagram of the possible interoperability points between two applications, one on J2EE, the other on .NET.
Figure 6.5: Interoperability points in multi-tiered applications
When designing your application for interoperability, consider each provider’s connection point as a service. For example, in Figure 6.5, path 2 represents an ASP.NET Presentation tier client connecting to a J2EE Business tier EJB. In this example, the ASP.NET client considers the J2EE Business tier EJB to be a service. In service-oriented architectures, applications can communicate with a service through different channels or communication mechanisms. It is recommended that you create an abstraction layer between a client consumer and service provider that isolates the provider and consumer, thus masking the details of how they communicate from each other.
The XBikes sample application design supports the interoperability paths 1, 2, 4, and 5 in Figure 6.5. However, it does not cover the connection point that path 3 represents. This interoperability scenario depicts point-to-point connectivity between a Business tier application in .NET Framework and a Business tier application in J2EE. If you consider each provider end of each connection point as a service, the Business tier component on the alternate platform appears to the calling Business tier component as just another service. Hence the path 3 implementation is similar to that of any of the other paths.
The next section discusses the components that make up the interoperability layer which abstracts the details of the interoperability mechanisms for each connection point.
When implementing interoperability, you should incorporate the ability to cope with changes into your application architecture. The following are examples of the changes your application must cope:
The interoperability requirements may change, requiring a different interoperability mechanism.
Your application’s interoperability requirements may require different interoperability mechanisms for distinct parts of your application at the same time.
The interoperability mechanism itself may change as new technology appears.
To implement change-tolerant interoperability between a .NET Framework and J2EE application, it is recommended that you implement the following programmatic elements:
Interoperability adapter factories
Figure 6.6 shows how these components fit in to a point-to-point interoperability scenario.
Figure 6.6: Programmatic elements for point to point interoperability
The following sections examine these interoperability elements in detail.
This guide has already described that a service interface enables an application to expose functionality to a consumer. It has also described how you can implement a simple service interface that follows the Fa ade design pattern to consolidate all policy-related code into one location. In addition, this service interface can aggregate the methods from multiple business components into one interface. This section explores how you can use service interfaces to enable interoperability between application components on J2EE and .NET Framework.
A service interface handles the details of the communication mechanism between a client and service, including mapping and transformation of data types between the client application and the service itself. This approach allows you to decouple the mechanism needed to communicate with the service from the service’s business logic. It is this functionality that makes implementing service interfaces ideal for interoperability scenarios.
When designing your application for interoperability, implement service interfaces to handle the details of the interoperability mechanism you choose. Using service interfaces in your application provides greater flexibility when modifying the application later.
For example, you may have implemented a business service fa ade that exposes your Business tier functionality as a single interface. You can then create additional service interfaces to expose your business service fa ade through a Web service or through a runtime bridge such as Ja.NET or JNBridge. In this example, the service interface handles the details of the Web service or runtime bridge implementations.
It is important to appreciate that the data types used on one platform are unlikely to be the same as the data types on the alternate platform. The service interface transforms data from the common data type that the interoperability mechanism exchanges to the native data type of the service provider.
Consider the example where your Java Business tier interface works with custom Java collection data types and your .NET Framework Presentation tier works with typed datasets. Using the techniques discussed in Chapter 3, you can develop a common data format for data exchange between the two environments. In this scenario, the Java Business tier service interface accepts and returns data in the common data format. The Java service interface then transforms the data from the collection objects in the Java Business tier to the common data format the service interface exposes. On the .NET Framework side, a similar process occurs, except that the .NET Framework Business tier exchanges typed datasets with the service interface. Chapter 7, “Integrating .NET in the Presentation Tier,” shows how the XBikes developers implemented these service interfaces.
For more information about the service interfaces pattern, see “Enterprise Solution Patterns: Service Interface” on MSDN.
Interoperability adapters provide the corresponding piece of the puzzle on the client side of the interoperability scenario. An interoperability adapter enables a client to communicate with a service provider through a specific service interface.
Interoperability adapters hide the implementation details of the interoperability mechanism from the connected client application. Interoperability adapters are responsible for transforming the data between the format that a client application uses and a common data format for interoperability.
Taking the example in the “Service Interfaces” section, the .NET Framework client uses a CustomerData typed dataset. The interoperability adapter then handles the data mapping from the .NET Framework typed dataset to the common data format for exchange with the Java application through one of the service interfaces.
You can compare an interoperability adapter to three established concepts:
The service gateway pattern.
The service agent pattern.
A business delegate in J2EE.
The closest match is to the service gateway pattern, but for terminology reasons, this book refers to interoperability adapters. The comparison to the service agent pattern is less strict, because interoperability adapters provide only a subset of the functionality in a service agent. Like an interoperability adapter, a J2EE business delegate provides an abstraction of the implementation of the business services, reducing coupling between the Presentation and Business tiers.
You should create interoperability adapters based on the design of the service interface and the level of fine control that you require. Taking a service that implements the logic for one or more use cases, you may choose either to build an interoperability adapter for each use case or a single interoperability adapter for the service interface.
Implementing an interoperability adapter for each use case provides you with the flexibility to use a different interoperability mechanism for each use case. For example, you could set up your ASP.NET application so that one use case implements a Web service interoperability adapter to communicate with the J2EE Business tier, while another use case (requiring higher performance) connects through a runtime bridge.
Implementing an interoperability adapter for each use case also enables a migration scenario where you may have some uses cases running on one platform and others on another. For example, you may have an ASP.NET Presentation tier that calls an interoperability adapter for one use case that a J2EE Business tier implements, and at the same time have a different interoperability adapter for another use case that calls directly into the .NET Framework Business tier.
An interoperability adapter factory enables dynamic selection of the correct interoperability adapter at run time. Although not a requirement for interoperability, the adapter factory provides the flexibility to change interoperability adapters easily. By implementing an adapter factory, you can configure adapter selection through a configuration file rather than by making changes to the code. An interoperability adapter factory also provides the flexibility in your design to incorporate as yet undeveloped interoperability mechanisms.
The interoperability adapter factory implements the Factory design pattern. The Factory design pattern is a well known pattern that allows a client to create an object based on predetermined settings.
You are recommended to implement one or more interoperability adapter factories to enable flexibility in which interoperability mechanisms you can use. For example, you may have multiple interoperability adapters that call into the same service, but through different interoperability mechanisms. A client calls the interoperability adapter factory to determine which interoperability adapter to use to call the service. The interoperability adapter factory looks in the configuration settings to see which interoperability adapter to use, and then it creates and returns an instance of the proper adapter to the client.
The interoperability adapter factory should return objects with only one type of interface. If you implement multiple interoperability adapters to communicate with the same service, but through different service interfaces, each interoperability adapter should implement the same interface.
You should implement an interoperability adapter factory for each service with which you want you communicate. For example, your Business tier application might communicate with both a Data Access Logic tier and a message queuing service. The Data Access Logic tier and message queuing service do not implement the same interfaces, so you need a separate factory to return the appropriate interoperability adapter for each one.
For more information about the Factory design pattern, see Design Patterns: Elements of Reusable Object-Oriented Software.
It is recommended to apply the following best practices when implementing interoperability layers between each of the interoperability connection points linking multi-tiered applications:
Implement service interfaces that expose each tier’s fa ade through the interoperability mechanisms you require.
Create interoperability adapters either for each service interface or for each use case, depending on the level of fine control you require.
Implement an interoperability adapter factory to enable dynamic selection of interoperability adapters for each service (or resource) with which your application needs to communicate.
Provide multiple abstraction layers to ensure maximum flexibility for future developments.
Figure 6.7 shows how these interoperability elements link into the elements of a multi-tiered application from the best practices diagram in Application Architecture for .NET: Designing Applications and Services.
Figure 6.7: Linking interoperability elements into a multi-tiered application
Now that the interoperability elements have been described, it is time to see how the developers implemented these in the XBikes sample application.