Data Access Proxy Strategy


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:

  • Processes the incoming XML request that contains Shipment order information

  • Processes the Shipment order to generate SQL queries

  • Updates the database with the corresponding Shipment statement

  • Sends ShippingNotice back to the .NET application

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

<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="Shipment">   <xsd:complexType>     <xsd:sequence>       <xsd:element name="id" type="xsd:string" minOccurs="1" maxOccurs="1"/>       <xsd:element name="contact" type="xsd:string"/>       <xsd:element name="address" type="Address" minOccurs="1" />       <xsd:element name="items" type="Item" minOccurs="1" maxOccurs="100"/>     </xsd:sequence>   </xsd:complexType> </xsd:element> <xsd:complexType name="Address">   <xsd:sequence>     <xsd:element name="name" type="xsd:string"/>     <xsd:element name="street" type="xsd:string"/>     <xsd:element name="city" type="xsd:string"/>     <xsd:element name="state" type="xsd:string"/>     <xsd:element name="zip" type="xsd:string"/>     <xsd:element name="country" type="xsd:string"/>   </xsd:sequence> </xsd:complexType> <xsd:complexType name="Item">   <xsd:sequence>     <xsd:element name="id" type="xsd:string" />     <xsd:element name="name" type="xsd:string"/>     <xsd:element name="quantity" type="xsd:int"/>   </xsd:sequence> </xsd:complexType> </xsd: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

namespace Java EEDotNet.Chapter10.Retailer { public class WarehouseCallback : System.Web.Services.WebService { [WebMethod(Description = "WarehouseCallback WebService")]      public string SubmitSN(String sn) {          string ack="ShippingNotice sh_id:" +sn+" is Received";          return ack;      }  } } 

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

public String executeCommand(int id, int queryType,    String valueObjectStr) throws RemoteException {   String status ="Received command";   try{     ThreadPerTaskExecutor executor = new ThreadPerTaskExecutor();     DAOExecutor executeCommand =       new DAOExecutor(id, queryType, valueObjectStr);     executor.execute(executeCommand);     status = "Request has been submitted for processing";   }catch(Exception e){   }   return status; } 

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

class ThreadPerTaskExecutor implements Executor {     public void execute(Runnable r) {         new Thread(r).start();     } } 

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

public void run(){  // Processing received XML file to create SQL queries  String[] queries = convertXmlToSql(id, queryType, valueObject);  // Executing SQL queries  executeSql(queries);  // Submitting Shipment Notifications  String sn = "Shipment Order is Submitted Succesfully";  submitShipmentNotification(sn); } 

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

private String[] convertXmlToSql(int id, int queryType,        String valueObject){  String[] sqlStmt=null;  // If query type == 1, the query corresponds to insert shipment  if (queryType==1) {      ShipmentHandler handler = new ShipmentHandler();      // Creating queries based on the received command      Shipment shipment = handler.getShipment(valueObject);      sqlStmt = handler.createSQL(shipment);  } else {    // Process other type of queries...  }  return sqlStmt; } 

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

private void executeSql(String[] queries){  DAO dao = new DAO();  for (int i=0; i<queries.length;i++)    dao.executeQuery(queries[i]); } 

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

package Java EEdotnet.chapter10.retailer; import shipment.Shipment; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; import java.util.concurrent.Executor; import java.io.FileOutputStream; import java.sql.Connection; public class DAOExecutor implements Runnable {  DAO dao;  int id;  String valueObject;  int queryType;  public DAOExecutor(int id, int queryType, String valueObject) {    this.id = id;    this.queryType = queryType;    this.valueObject = valueObject;  }  public void run(){    // Processing received XML file to create SQL queries    String[] queries = convertXmlToSql(id, queryType, valueObject);    // Executing SQL queries    executeSql(queries);    // Submitting Shipment Notifications    String sn = "Shipment Order is Submitted Succesfully";    submitShipmentNotification(sn);  }  /*   * Convert XML object into the corresponding SQL queries   * based on the query type.   */  private String[] convertXmlToSql(int id, int queryType,     String valueObject)  {    String[] sqlStmt=null;    // If query type == 1, the query corresponds to insert shipment    if (queryType==1) {      ShipmentHandler handler = new ShipmentHandler();      // Creating queries based on the received command      Shipment shipment = handler.getShipment(valueObject);      sqlStmt = handler.createSQL(shipment);    } else {      // Process other type of queries...    }    return sqlStmt;  }  private void executeSql(String[] queries){    DAO dao = new DAO();    for (int i=0; i<queries.length;i++)      dao.executeQuery(queries[i]);  } } // Defining a Thread per task executor class ThreadPerTaskExecutor implements Executor {     public void execute(Runnable r) {         new Thread(r).start();     } } 

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

public Shipment getShipment(String xmlShipment){  try {    JAXBContext jc = JAXBContext.newInstance("shipment");    Unmarshaller u = jc.createUnmarshaller();    sh = (Shipment)u.unmarshal(        new StreamSource(new StringReader(xmlShipment)));    } catch (Exception exe ) {    // Log exception…    // StackTraceElement[] exeElem= exe.getStackTrace();    }  return sh; } 

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

public String[] createSQL(Shipment sh){  String [] queries = null;  Item item = null;  String id = null;  String name = null;  int qty = -1;  List items = sh.getItems();  int size = items.size();  queries = new String[size];  for (int i=0; i<size; i++ ) {    item = (Item)items.get(i);    id = item.getId();    name = item.getName();    qty = item.getQuantity();    queries[i] =      "insert into ship_goods values ('"+id+"','"+name+"',"+qty+")"; } 

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

Context initCxt = new InitialContext(); Context envCxt = (Context)initCxt.lookup("java:comp/env"); //Looking up DataSource for WarehouseA, MySql DataSource dataSource = (DataSource)envCxt.lookup("jdbc/WarehouseA"); // Establishing connection to the database con = dataSource.getConnection(); 

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

mysql> select * from ship_goods; +------------+----------------+------+ | id        | descr         | qty  | +------------+----------------+------+ | 223       | Sun Fire X4200 |   1 | | 762       | Sun Fire X4100 |   1 | +------------+----------------+------+ 2 rows in set (0.03 sec) 

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

<target name="createC#ClassesFromSchema" depends="prepare">    <echo message="--- creating C# classes of given schema---"/>     <exec executable="xsd.exe">     <arg value="${etc.schema}/Shipment.xsd"/>     <arg value="/classes"/>     <arg value="/namespace:Java EEDotNet.Chapter10.Retailer"/>     <arg value="/out:${build.src}"/>     </exec>  </target> 

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

[View full width]

 // // This source code was auto-generated by xsd, Version=1.1.4322.2032. // namespace Java EEDotNet.Chapter10.Retailer {    using System.Xml.Serialization;    /// <remarks/>    [System.Xml.Serialization.XmlRootAttribute(Namespace="",     IsNullable=false)]    public class Shipment {        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string id;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string contact;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public Address address;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute("items", Form=System.Xml.Schema .XmlSchemaForm.Unqualified)]        public Item[] items;    }    /// <remarks/>    public class Address {        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string name;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string street;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string city;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string state;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string zip;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string country;    }    /// <remarks/>    public class Item {        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string id;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public string name;        /// <remarks/>        [System.Xml.Serialization.XmlElementAttribute        (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]        public int quantity;    } } 

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

public string submitShipment (Shipment sh) {    XmlSerializer serializer = new XmlSerializer(typeof(Shipment));    StringWriter writer = new StringWriter();    serializer.Serialize(writer,sh);    string shXML = writer.ToString();    Console.WriteLine("Shipment XML: " + shXML);    // Assuming 1 indicates submit shipment    int queryType = 1;    int queryId = 99;    DAOFacadeService srv = new DAOFacadeService();    string result = srv.executeCommand(queryId, queryType, shXML);    return result; } 

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

app/build/bin>DotNetClient DotNetClient.Main(): populating Shipment... Shipment XML: <?xml version="1.0" encoding="utf-16"?> <Shipment xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  <id>932850</id>  <contact>joe.miller@abc123.com</contact>  <address>    <name>Joe Miller</name>    <street>134 N 1st Street</street>    <city>San Jose</city>    <state>CA</state>    <zip>95111</zip>    <country>US</country>  </address>  <items>    <id>223</id>    <name>Sun Fire X4200</name>    <quantity>1</quantity>  </items>  <items>    <id>762</id>    <name>Sun Fire X4100</name>    <quantity>1</quantity>  </items> </Shipment> Result of executing the Web Service: Request has been submitted for processing 

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.




Java EE and. Net Interoperability(c) Integration Strategies, Patterns, and Best Practices
Java EE and .NET Interoperability: Integration Strategies, Patterns, and Best Practices
ISBN: 0131472232
EAN: 2147483647
Year: N/A
Pages: 170

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net