Service LocatorContextService lookup and creation involves complex interfaces and network operations. ProblemJ2EE clients interact with service components such as EJBTM and JMS components, which provide business services and persistence capabilities. To interact with these components, clients must either locate the service component (referred to as a lookup operation) or create a new component. For instance, an EJB client must locate the enterprise bean's home object, which the client then uses to find object, create, or remove one or more enterprise beans. Similarly, a JMS client must first locate the JMS Connection Factory to obtain a JMS Connection or a JMS Session. All J2EE application clients use the JNDITM (Java Naming and Directory Interface) common facility to lookup and create EJB and JMS components. The JNDI API enables clients to obtain an initial context object that holds the component name to object bindings. The client begins by obtaining the initial context for a bean's home object. The initial context remains valid while the client session is valid. The client provides the JNDI registered name for the required object to obtain a reference to an administered object. In the context of an EJB application, typical administered object is an enterprise bean's home object. For JMS applications, the administered object can be a JMS Connection Factory (for a Topic or a Queue) or a JMS Destination (a Topic or a Queue). So, locating a JNDI administered service object is common to all clients that need to access that service object. That being the case, it is easy to see that many types of clients repeatedly use the JNDI service, and the JNDI code appears multiple times across these clients. This results in an unnecessary duplication of code in the clients that need to look up services. Also, creating a JNDI initial context object and performing a lookup on an EJB home object utilizes significant resources. If multiple clients repeatedly require the same bean home object, such duplicate effort can negatively impact application performance. Let us examine the lookup and creation process for various J2EE components. The lookup and creation of enterprise bean components relies upon the following:
The lookup and creation of JMS components (Topic, Queue, QueueConnection, QueueSession, TopicConnection, TopicSession, and so forth) involves the following. In the following steps, Topic refers to Publish/Subscribe messaging model and Queue refers to point-to-point messaging model:
The process to look up and create components described above involves a vendor-supplied context factory implementation. This introduces vendor dependency in the application clients that need to use the JNDI lookup facility to locate the enterprise beans and JMS components such as topics, queues, and connection factory objects. Forces
SolutionUse a Service Locator object to abstract all JNDI usage and to hide the complexities of initial context creation, EJB home object lookup, and EJB object re-creation. Multiple clients can reuse the Service Locator object to reduce code complexity, provide a single point of control, and improve performance by providing a caching facility. This pattern reduces the client complexity that results from the client's dependency on and need to perform lookup and creation processes, which are resource intensive. To eliminate these problems, this pattern provides a mechanism to abstract all dependencies and network details into the Service Locator. StructureThe following class diagram represents the relationships for the Service Locator pattern.
Participants & ResponsibilitiesThe following sequence diagram shows the interaction between the various participants of the Service Locator pattern.
Client Service Locator InitialContext ServiceFactory BusinessService StrategiesEJB Service Locator StrategyThe Service Locator for enterprise bean components uses the EJBHome object shown as BusinessHome in the role of the ServiceFactory. Once the EJBHome object is obtained, it can be cached in the ServiceLocator for future use to avoid another JNDI lookup when the client needs the home again. Depending on the implementation, the home object can be returned to the client, which can then use it to lookup, create and remove enterprise beans. Otherwise, the ServiceLocator can retain (cache) the home object and gain the additional responsibility of proxying all client calls to the home object. The class diagram for the EJB Service Locator strategy is shown below.
The interaction between the participants in a Service Locator for an enterprise bean is shown in the following sequence diagram. click to enlargeJMS Queue Service Locator StrategyThis strategy is applicable to point-to-point messaging requirements. The Service Locator for JMS components uses QueueConnectionFactory objects in the role of the ServiceFactory. The QueueConnectionFactory is looked up using its JNDI name. The QueueConnectionFactory can be cached by the ServiceLocator for future use to avoid repeated JNDI calls to look it up when the client needs it again. The ServiceLocator may otherwise hand over the QueueConnectionFactory to the client. The Client can then use it to create a QueueConnection. A QueueConnection is necessary in order to obtain a QueueSession or to create a Message, QueueSender (to send messages to the queue), or a QueueReceiver (to receive messages from a queue). The class diagram for the JMS Queue Service Locator strategy is shown below. In this diagram, the Queue is a JMS Destination object registered as a JNDI administered object representing the queue. The Queue object can be directly obtained from the context by looking up using its JNDI name.
The interaction between the participants in a Service Locator for point-to-point messaging using JMS Queues is shown in the following sequence diagram. click to enlargeJMS Topic Service Locator StrategyThis strategy is applicable to publish/subscribe messaging requirements. The Service Locator for JMS components uses TopicConnectionFactory objects in the role of the ServiceFactory. The TopicConnectionFactory is looked up using its JNDI name. The TopicConnectionFactory can be cached by the ServiceLocator for future use to avoid repeated JNDI calls to look it up when the client needs it again. The ServiceLocator may otherwise hand over the TopicConnectionFactory to the client. The Client can then use it to create a TopicConnection. A TopicConnection is necessary in order to obtain a TopicSession or to create a Message, TopicPublisher (to publish messages to a topic), or a TopicSubscriber (to subscribe to a topic). The class diagram for the JMS Topic Service Locator strategy is shown below. In this diagram, the Topic is a JMS Destination object registered as a JNDI-administered object representing the topic. The Topic object can be directly obtained from the context by looking up using its JNDI name.
The interaction between the participants in a Service Locator for publish/subscribe messaging using JMS Topics is shown in the following sequence diagram. click to enlargeCombined EJB and JMS Service Locator StrategyThis strategies for EJB and JMS can be used to provide separate Service Locator implementations since the clients for EJB and JMS may more likely be mutually exclusive. However, if there is a need to combine these strategies, it is possible to do so to provide the Service Locator for all objects--enterprise beans and JMS components. Type Checked Service Locator StrategyThe above diagrams provide lookup facilities by passing in the service lookup name. For an enterprise bean lookup, the Service Locator needs a Class as a parameter to PortableRemoteObject.narrow() method. The Service Locator can provide a getHome() method, which accepts as arguments, the JNDI service name and the EJBHome Class object for the enterprise bean. Using this method of passing in JNDI service names and EJBHome class objects can lead to client errors. Another approach is to statically define the services in the ServiceLocator and instead of passing in string names, the client passes in a constant. Below is an example of such a strategy: public class ServiceLocator { /** singleton's private instance */ static { me = new ServiceLocator(); } private ServiceLocator() {} /** returns the Service Locator instance */ return me; } /* final public static int PROJECT = 0; } /* final static Class PROJECT_CLASS = ProjectHome.class; final static String PROJECT_NAME = "Project"; /* final static Class RESOURCE_CLASS = ResourceHome.class; /* static private Class getServiceClass( int service ) { switch( service ) { return null; } /* static private String getServiceName( int service ) { switch( service ) { } /* public EJBHome getHome( int s ) throws ServiceException { EJBHome home = null; try {
Context initial = new InitialContext(); Object objref = initial.lookup( getServiceName(s)); // Narrow using the EJBHome Class from Object obj = PortableRemoteObject.narrow( home = (EJBHome)obj; } } return home; } } The client code to use the Service Locator for this strategy may look like the following example: public class ServiceLocatorTester { public static void main( String[] args ) { ServiceLocator serviceLocator = try { ProjectHome projectHome = (ProjectHome) } catch( ServiceException ex ) { // client handles exception } } } This strategy is about applying type checking to client lookup. It encapsulates the static service values inside the ServiceLocator and creates an inner class Services, which declares the service constants (PROJECT and RESOURCE). The Tester client gets an instance to the ServiceLocator singleton and calls getHome() passing in the PROJECT. ServiceLocator in turn, gets the JNDI entry name and the Home class and returns the EJBHome. This strategy has tradeoffs. It reduces the flexibility of lookup which is in the Services Property Locator strategy, but add the type checking of passing in a constant to the ServiceLocator.getHome() method. Service Locator Properties StrategyThis strategy helps to address the tradeoffs of the type checking strategy. This strategy suggests the use of property files and / or deployment descriptors to specify the JNDI names and the EJBHome class name. For presentation tier clients, such properties can be specified in the presentation-tier deployment descriptors or property files. When the presentation tier accesses the business tier, it typically uses the Business Delegate pattern. The Business Delegate interacts with the Service Locator to locate business components. If the presentation tier loads the properties on initialization and can provide a service to hand out the JNDI names and the EJB class names for the required enterprise bean, then the Business Delegate could request this service to obtain them. Once the Business Delegate has the JNDI name and the EJBHome Class name, it could request the Service Locator for the EJBHome by passing these properties as arguments. The Service Locator could in turn use Class.forName(EJBHomeClassName) to obtain the EJBHome Class object and go about its business of looking up the EJBHome and using PortableRemoteObject.narrow() to cast the object as shown in the code sample above. The only thing that changes is where the JNDI name and the Class objects are coming from. Thus, this strategy avoids hard-coded JNDI names in the code and provides for flexibility of deployment. However, due to the lack of type checking, there is a scope for avoid errors and mismatches in specifying the JNDI names in different deployment descriptors. Consequences
Because clients of enterprise beans are not aware of the EJBHome objects, it's possible to add new EJBHome objects for enterprise beans developed and deployed at a later time without impacting the clients. JMS clients are not directly aware of the JMS connection factories, so new connection factories can be added without impacting the clients. The clients are not involved in JNDI lookup and factory/home object creation. Because the Service Locator performs this work, it can aggregate the network calls required to lookup and create business objects. The Service Locator can cache the initial context objects and references to the factory objects (EJBHome, JMS connection factories) to eliminate unnecessary JNDI activity that occurs when obtaining the initial context and the other objects. This improves the application performance. Related Patterns
The Business Delegate pattern uses Service Locator to gain access to the service objects like EJB objects, JMS topics and JMS queues. This separates the complexity of service location from the Business Delegate leading to loose coupling and increased manageability. The Session Façade pattern uses Service Locator to gain access to the enterprise beans that are involved in a workflow. The Session Façade could directly use the Service Locator or delegate the work to a Business Delegate (see BusinessDelegate pattern). The Value Object Assembler pattern uses Service Locator to gain access to the various enterprise beans it needs to access to build its composite value object. The Value Object Assembler could directly use the Service Locator or delegate the work to a Business Delegate (see BusinessDelegate pattern). (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved. Version 1.0 Beta | |||
|
| ||||||||||||