The previous several chapters have implied that there are shortcomings in the event patterns; however, the chapters have not been explicit about the reasons or about the techniques to mitigate the shortcomings. In fact, the environment was sufficient to illustrate the structure of patterns (Event Monitor, Observer, and Publish/Subscribe) and led to simple deployment and programming examples.
Two scenarios illustrate the boundary you run into by allowing yourself to have logical tiers without physical tier separation. The first scenario, described in "Using Web Service Events" illustrates how you trap yourself within the Apache Axis process when you build event subscribers with Web Services. The second scenario, described in "Avoiding Service Implementation Bloat," shows how you drag your persistence mechanisms into the Web tier along with the entire logic tier at deployment time. This second scenario creates bloat in your Web tier in terms of the deployment packages and the footprint of your Web tier and runs the danger that the persistence mechanism does not perform as expected. Finally, you will look at how you solve both problems with the Remote Method Invocation (RMI) capability of Java 2 Standard Edition (J2SE) or similar interprocess protocol by using the Connector pattern.
The subscriber from the previous chapter illustrates the most obvious problem with your use of Web Services ”the inability of your Web Services to interact with an application or component running in a separate process. Consider the scenario where you have a stand-alone Java-based client that wants to receive notifications from one of the event mechanisms constructed in the previous two chapters. The Java-based client runs in one process, and the Web Service subscriber runs in a separate process. For this reason, you need a mechanism that connects the Java application to the Web Service.
Figure 13-1 illustrates a containment scenario using Apache Tomcat and the Apache Axis Web Service engine. The diagram also illustrates the concept of a separate Java client process. As you can see, the event subscriber used in the previous two chapters is in a physically separate process from the application classes that need to be notified of the event. The subscriber that receives messages from the event service exists within the Apache Tomcat process, and the application is in its own Java client process.
In Figure 13-1, the Subscriber is a part of the Web Tier. You could consider that the ApplicationClasses are part of the logic tier, which must react to changes in the external Web Service. Thus, you have created a tiered scenario, without a connector to move data between the tiers.
The challenge in the current paradigm is letting one or more application classes know that the subscriber running in the Apache Tomcat container received an event; this communication requirement between tiers is the essence of the Connector pattern. There are many mechanisms for communicating across process boundaries and connecting the physically separate tiers. In Java, RMI is a relatively lightweight mechanism for creating the communication path . Other mechanisms for interprocess communication include message buses based on the Java Message Service (JMS) standard and heavier ORB-based mechanisms, such as a CORBA implementation.
The second scenario to illustrate the necessity of the Physical Tiers pattern, and the subsequent usage of the Connector pattern, revolves around your deployment scenario. Your business objects use Java Data Objects (JDO) for data persistence. Because Apache Axis invokes the service implementation within the Apache Axis container, the JDO and JDBC classes load into the container with the service implementation. Depending on the JDO implementation you are using and the deployment options you use for the service implementation, the environment and performance can become unpredictable. Figure 13-2 illustrates the JDO and JDBC classes being instantiated within the Apache Tomcat container.
Apache Axis instantiates your service implementation, BusinessObject , based on options in the deployment descriptor for the class. There are three options for the lifecycle of the service implementation:
Application scope: Creates a single service implementation that services all requests
Request scope: Creates a new service implementation for each request processed by the Apache Axis engine
Session scope: Creates a new service implementation for each user session
No matter which lifecycle option you choose for the lifecycle of the service implementation, you end up with a deployment architecture in which the service implementation and the related services run within a thread in the Apache Tomcat process. On the other hand, the lifecycle options can affect the behavior of the service and dependent services.
Consider a deployment using the Axis Request scope lifecycle option for the service implementation. On each request, Apache Axis creates a business object. The business object must access data so JDO gets a connection to the database through JDBC. If you process 100 or 1,000 requests at a time, you will quickly overwhelm a database's ability to service requests. Depending on your Web Service environment, JDO may not be able to optimize access to the database through built-in mechanisms. For example, if a Web Service implementation spawns a process for each request, then JDO could never fully utilize built-in thread and connection pooling.
Apache Axis does not spawn a process for each request, but it does separate each request into different threads and different class loaders for different services. This complicates the environment for JDO substantially, and you will have to be careful to test your JDO implementation in the Apache Axis environment.
In the end, the service deployment must contain all of the class libraries and Java extensions required to run your Web Service. In the case study, you have 10 or more JAR files in the Apache Tomcat or Apache Axis class path that would not be in the path if you physically separated the logic tier from the Web tier.
As discussed, as you move your classes and implementation into the Web tier deployment, you lose control of their lifecycle and how multiple instances are handled. Further, you lose the ability to manage the logic tier services separately from the Web tier services. This loss in flexibility impacts everything from the optimization of the path to the database to the versatility of the connections between the logic components themselves , as discussed in the previous section. You also lose flexibility in system deployment itself; by physically coupling the logic tier and Web tier, you force your application to be deployed on a single host. This deployment scenario limits the scalability of your solution in addition to limiting your ability to leverage the logic tier instances to facilitate reuse in stand-alone applications. You essentially require a running instance of each object for each application. With the size of enterprise applications, this creates a massive redundancy in deployed code, as well as a maintenance nightmare.
In both of the previous scenarios ”the subscriber-to-application scenario and the separation of the tiers to mitigate deployment issues ”the solution to your problem is to do the following:
Recognize that there are multiple physical tiers in the solution.
Use the Connector pattern to link the two tiers together.
In both scenarios, there are multiple physical tiers. In the event-to-application scenario, the Web tier houses the logic to interact with the Web Service world, and the application contains logic that interacts with a user. These tiers must be physically separate because of the containment of the Web Service environment within Apache Tomcat and the containment of the application logic within the Java process facilitating the stand-alone application. In the second scenario, you will want to separate logic from the Web tier to facilitate greater control over the components in the logic tier, to facilitate more diverse deployment scenarios, and to reduce the amount of code running within the Apache Tomcat container. To reach this goal, you physically separate the logic components into a stand- alone process. In both cases, you now follow a Physical Tiers pattern, and you use a Connector pattern to facilitate communication between the tiers.