Java EE and .NET Interoperability: Integration Strategies, Patterns, and Best Practices
A simple alternative to the Indirect Data Access strategy entails the use of a shared Data Access Layer, discussed through the rest of the chapter. Scope
This strategy attempts to solve the same problem as the previous strategy, Distributed Mediator. Multiple applications share a database, while only one of them has primary ownership over the data. The secondary application receives only intermittent access to submit changes. Solution
The Data Access Proxy component asynchronously invokes a remote DAO to perform underlying CRUD operations. An application can continue its execution without waiting for the underlying database operation to complete. For the Proxy object and the remote DAO component to stay loosely coupled, it is valuable to introduce a Command pattern that the DAO implements. The Command pattern helps to expose a coarse-grained DAO API to the Proxy object and provides a consistent integration point. Figure 10-6 depicts the Data Access Proxy strategy, which can be easily applied to either Java or .NET components. Figure 10-6. Data Access Proxy strategy
The Command object can be passed, encapsulating necessary operations, and return back success, failure, or a result set. For the integration technology, the developer can select the most suitable one for the overall architecture of the distributed environment. Technologies such as .NET Remoting or Web services are among a few to choose from, and this pattern can be used for both synchronous and asynchronous communication. In other words, this pattern needs to be layered on top of a previous Java EE-.NET integration pattern. Is there any reason this is an asynchronous pattern? Benefits and Limitations
The simplicity of this design is one of its advantages. If Web services or a custom Bridging solution is already being used, implementing the Data Access Proxy strategy should be fairly straightforward. This strategy allows Java and .NET applications to remain loosely coupled as the Proxy component asynchronously connects to the remote Data Access Layer. The limitation of this strategy is that a custom notification or a callback mechanism must be built. For that, it might be beneficial to assign a unique identifier to the command submitted to DAO. This design also involves custom time out and an error handling mechanism. The Data Access Layer design has to be scalable to start with to accommodate requests external to its application. One way to manage the scalability aspect of this design is by creating a separate database connection pool that is dedicated to the external requests. A long-running query submitted by the Proxy component won't affect the internal application's access to the database, as the underlying connections are not shared. Related Patterns
Data Access Object, [CJ2EEP], and a couple of GoF patterns such as Proxy and Command are used by this strategy. Example
This example implements the Java Data Access Proxy strategy. A.NET application accesses the Java DAOFacadeService Web service that creates a separate thread to asynchronously process the request. This thread checks on the type of command submitted, and given that in this case the command corresponds to inserted Shipment order, the thread executes the following tasks:
For the first two tasks the thread invokes the ShipmentHandler method, whose responsibility is to unmarshal the XML Shipment order into a Shipment object. ShipmentHandler also knows how to parse the Shipment object into the SQL statements. For the third task, the worker thread invokes the DAO object to connect to either MySQL or an SQL Server database and execute the SQL query. Figure 10-7 shows the UML sequence diagram of the ShipGoods use case. Figure 10-7. Data Access Proxy strategy, ShipGoods sequence diagram
For scalability reasons, it is best to delegate the actual command processing to a separate thread, DAOExecutor, that invokes DAO component. With this design a .NET client application does not have to wait until a connection to a database gets obtained or until a lengthy query gets executed. The .NET client application receives notification after the asynchronous processing is complete. To run this example, it is necessary to place corresponding MySQL and SQL Server JDBC libraries under Tomcat's common/lib directory such as C:\tomcat50-jwsdp\common\lib. In addition, the autodeploy property of the Tomcat server.xml file, located under tomcat50-jwsdp/conf directory, has to be set to false: autoDeploy="false". The JNDI Context.xml is defined and included as part of the final WAR file. Check the Context.xml file, located under chapter10/etc/server directory to ensure that Resource parameters including JDBC package name, document root, and context path correspond to your configuration. The Ship Goods scenario is implemented using a top-down approach, starting with a common XSD schema. The application is composed out of four distinct steps outlined as follows: Step 1: Defining XSD Shipment
The top-down Web services design allows both Java and .NET applications to adhere to the same XML schema. Listing 10-1 lists the Shipment schema, from the Shipment.xsd file: Listing 10-1. The XSD Shipment Schema
Step 2: Building .NET Callback Application
Listing 10-2 is the sample code for the .NET WarehouseCallback.asmx: Listing 10-2. .NET WarehouseCallback Web Service
Because this callback service is not invoked at the end of the processing loop, for testing purposes it is worth creating a standalone Java client to ensure that the service is deployed correctly. Step 3: Implementing Java DAOFacade Application
The core business logic resides in the DAOFacade application. The DAOFacadeService Web service is the frontier to the service request processing. The Web service implementation class, DAOFacadeServiceImpl, delegates the actual request processing to a separate thread, DAOExecutor tHRead. The service returns the status that the Shipment order has been received and sent for further processing. Listing 10-3 shows the executeCommand() method: Listing 10-3. Java DAOFacadeService Web Service
The ThreadPerTaskExecutor class is located in the DAOExecutor.java file. The ThreadPerTaskExecutor class simply extends the java.util.concurrent.Executor, as shown in Listing 10-4: Listing 10-4. Java ThreadPerTaskExecutor Class
The DAOExecutor is responsible for processing the actual command. Listing 10-5 lists its run() method that is invoked when the thread starts. Listing 10-5. Java DAOExecutor run( ) Method
As is shown, there are two methods that need to be explored: convertXmlToSql() and executeSql(). The first method is responsible for checking on the type of query and because in our case the query corresponds to Shipment order, it creates the ShipmentHandler object to convert XML into the Shipment value object and then generate corresponding SLQ statements based on the content of the Shipment order. The convertXmlToSql is shown in Listing 10-6: Listing 10-6. Java DAOExecutor convertXmlToSql( ) Method
The ShipmentHandler object creates the Shipment object. The Shipment class is auto-generated from the original Shipment XSD. Before delving into the ShipmentHandler implementation, here is a brief look at the executeSql() method invoked by DAOExecutor. All it does is access the Data Access Object component and iterate over the list of SQL statements to execute them. This is shown in Listing 10-7: Listing 10-7. Java DAOExecutor executeSql() Method
In putting this all together, the DAOFacadeService Web service implementation class, DAOFacadeServiceImpl, invokes the DAOExecutor to actually process the incoming command. Listing 10-8 includes a complete listing of DAOExecutor.java: Listing 10-8. Java DAOExecutor.java
There are two main references within the DAOExecutor class, the ShipmentHandler and the DAO class. We'll start with the ShipmentHandler class that takes a Shipment XML request file and using JAXB APIs, unmarshals that file into the Shipment object. This is shown in Listing 10-9: Listing 10-9. Java ShipmentHandler getShipment( ) method
After the object has been restored from the XML file, it is time to parse it to create SQL statements, defined in the createSQL() method of ShipmentHandler. This method is given in Listing 10-10: Listing 10-10. Java ShipmentHandler createSQL( ) method
The createSQL() method retrieves items and creates insert SQL statements with the corresponding values. Similarly, SQL statements can be extended with Address and Contact information encapsulated within the Shipment object. This is it for the ShipmentHandler class. After the Shipment has been processed, the next step is to execute SQL statements. The DAO object is responsible for establishing the actual database connection. In this example, the JNDI Context is used to lookup the DataSource and with the help of the DataSource create the database connection. Listing 10-11 shows the core logic of the DAO connect() method: Listing 10-11. Creating JNDI Context, Performing a DataSource Lookup, and Creating Connection
Once the database connection is established, the update query can be executed: stmt.executeUpdate(query);
It is now possible to check with the database that the Shipment order submitted by the .NET application has been correctly processed. In this case, the .NET client application sends two order items that are reflected in the database, as demonstrated in Listing 10-12: Listing 10-12. Checking Database Records
Step 4: Building the .NET Client Application
The schema is transformed into the corresponding C# class. Listing 10-13 provides the ant target, "createC#ClassesFromSchema", that generates the corresponding classes by using the xsd.exe .NET tool. Listing 10-13. Creating C# Classes From XSD Schema
The auto-generated Shipment class, shown in Listing 10-14, is composed of identifier, content, address, and item array: Listing 10-14. Auto-generated C# Shipment class
The DotNetClient populates the Shipment object and passes it to the .NET DAOProxy. The proxy invokes the Web service executeCommand() method and sends the XML Shipment order as a parameter to the Web service. Listing 10-15 shows the submitShipment() method of the DAOProxy class: Listing 10-15. C# DAOProxy Class Populating Shipment and Invoking the DAOFacadeService Endpoint
After the Java Web service is deployed, you can consume it with a .NET client application. Listing 10-16 details the output of the .NET client application execution that includes the XML Shipment file: Listing 10-16. Result of Executing .NET Client Application
It is also important to check the log file, generated as the result of the callback operation, to ensure that the ShippingNotice acknowledgement successfully arrived. As can be seen in these examples, the client application is quite simple, and most of the business logic resides in Java. What this example has demonstrated is a simple mechanism to perform asynchronous processing of the database request. Integrating databases with business logic and the Web service may be quite involved; therefore, it is quite helpful to do unit testing to ensure that individual operations are performed correctly. Additionally for ease of troubleshooting, it is important to enable logging at the Web service hosting server, application level, and database. In addition, building the .NET Data Access Adapter is simpler than doing so in Java, given that one can directly leverage the .NET asynchronous programming model with begin/end method calls. Refer to the Web services and asynchronous programming model under the .NET platform in previous chapters as well as the online examples and walkthroughs that come with Visual Studio.NET for examples on how to do this. |
Категории