Escolar Documentos
Profissional Documentos
Cultura Documentos
DESIGN PATTERNS
4. Delegation pattern
5. Observer Pattern
6. Struts Patterns
Front Controller
Command Pattern
8. Session Façade
9. Service Locator
DesignPatterns 2
The Solution
Factory Method is a creational pattern. This pattern helps to model an interface for creating an
object which at creation time can let its subclasses decide which class to instantiate. We call
this a Factory Pattern since it is responsible for "Manufacturing" an Object. It helps instantiate
the appropriate Subclass by creating the right Object from a group of related classes. The
Factory Pattern promotes loose coupling by eliminating the need to bind application- specific
classes into the code.
Factories have a simple function: Churn out objects.
Obviously, a factory is not needed to make an object. A simple call to new will do it for you.
However, the use of factories gives the programmer the opportunity to abstract the specific
attributes of an Object into specific subclasses which create them.
The Factory Pattern is all about "Define an interface for creating an object, but let the
subclasses decide which class to instantiate. The Factory method lets a class defer
instantiation to subclasses" Thus, as defined by Gamma et al, "The Factory Method lets a class
defer instantiation to subclasses".
Figure 1 below illustrates the roles of the Factory Pattern.
Let's consider a simple case where we could use a Factory class. Suppose we have an entry
form and we want to allow the user to enter his name either as “firstname lastname” or as
“lastname, firstname”. We’ll make the further simplifying assumption that we will always be
able to decide the name order by whether there is a comma between the last and first name.
This is a pretty simple sort of decision to make, and you could make it with a simple if
statement in a single class, but let’s use it here to illustrate how a factory works and what it
can produce. We’ll start by defining a simple base class that takes a String and splits it
(somehow) into two names:
class Namer {
In this base class we don’t actually do anything, but we do provide implementations of the getFirst and getLast
methods. We’ll store the split first and last names in the Strings first and last, and, since the derived classes will need
access to these variables, we’ll make them protected.
Now we can write two very simple derived classes that split the name into two parts in the constructor. In the FirstFirst
class, we assume that everything before the last space is part of the first name:
And, in the LastFirst class, we assume that a comma delimits the last name. In both classes, we also provide error
recovery in case the space or comma does not exist.
class NameFactory {
//returns an instance of LastFirst or FirstFirst
//depending on whether a comma is found
public Namer getNamer(String entry) {
int i = entry.indexOf( ","); //comma determines name order
if (i>0)
return new LastFirst(entry) ; //return one class
else
return new FirstFirst(entry) ; //or the other
}}
DesignPatterns 5
Let’s see how we put this together. We have constructed a simple Java user interface that allows you to enter the names
in either order and see the two names separately displayed. You can see this program below.
You type in a name and then click on the Compute button, and the divided name appears in the text fields below. The
crux of this program is the compute method that fetches the text, obtains an instance of a Namer class and displays the
results.
In our constructor for the program, we initialize an instance of the factory class with
Then, when we process the button action event, we call the computeName method, which calls the getNamer factory
method and then calls the first and last name methods of the class instance it returns:
private void computeName( ) {
//send the text to the factory and get a class back
namer = nfactory.getNamer( entryField. getText() );
//compute the first and last names
//using the returned class
txFirstName. setText(namer. getFirst( ));
txLastName.setText( namer.getLast( ));
}
And that’s the fundamental principle of Factory patterns. You create an abstraction which
decides which of several possible classes to return and returns one. Then you call the methods
of that class instance without ever knowing which derived class you are actually using. This
approach keeps the issues of data dependence separated from the classes’ useful methods
The Abstract Factory pattern is one level of abstraction higher than the factory pattern. You
can use this pattern when you want to return one of several related classes of objects, each of
which can return several different objects on request. In other words, the Abstract Factory is a
factory object that returns one of several factories.
One classic application of the abstract factory is the case where your system needs to support
multiple “look-and-feel” user interfaces, such as Windows-9x, Motif or Macintosh. You tell the
factory that you want your program to look like Windows and it returns a GUI factory which
returns Windows-like objects. Then when you request specific objects such as buttons, check
boxes and windows, the GUI factory returns Windows instances of these visual interface
components.
The Singleton pattern is one of the simpler design .This pattern is effective for limiting the
maximum number of instances of a class to exactly one. In this case, if more than one object
needs to use an instance of the Singleton class, those objects share the same Singleton class
instance.
The singleton pattern is implemented by creating a class with a method that creates a new
instance of the object if one does not exist. If an instance already exists, it simply returns a
reference to that object. To make sure that the object cannot be instantiated any other way,
the constructor is made either private or protected. Note the distinction between a simple
static instance of a class and a singleton. Although a singleton can be implemented as a static
instance, it can also be lazily constructed, requiring no memory or resources until needed.
/**
* This method returns the singleton instance of this class.
* @return PerdiemDAO
*/
return INSTANCE;
}
}
You can call the instance of this class using PerdiemDAO.getInsta nce(), where ever you
want.
The design of this class ensures that only one Singleton object is ever created. The
constructor is declared private and the getInstance( ) method creates only one object. This
implementation is fine for a single-threaded program. However, when multiple threads are
introduced, you must protect the getInstance( ) method through synchronization. If the
getInstance( ) method is not protected, it is possible to return two different instances of the
Singleton object. Consider two threads calling the getInstance( ) method concurrently and
the following sequence of events:
1. Thread 1 calls the getInstance( ) method and determines that instance is null at //1.
2. Thread 1 enters the if block, but is preempted by thread 2 before executing the line at
//2.
3. Thread 2 calls the getInstance( ) method and determines that instance is null at //1.
4. Thread 2 enters the if block and creates a new Singleton object and assigns the
variable instance to this new object at //2.
5. Thread 2 returns the Singleton object reference at //3.
6. Thread 2 is preempted by thread 1.
7. Thread 1 starts where it left off and executes line //2 which results in another Singleton
object being created.
8. Thread 1 returns this object at //3.
The result is that the getInstance( ) method created two Singleton objects when it was
supposed to create only one. This problem is corrected by synchronizing the getInstance( )
method to allow only one thread to execute the code at a time, as shown in Listing 2:
4. Delegation pattern
Delegation can be viewed as a relationship between objects where one object forwards certain
method calls to another object, called its delegate. Delegation can also a powerful
DesignPatterns 7
As a simple example, consider the relationship between a Rectangle class and a Window class.
With inheritance, a Window class would inherit its rectangular properties from class Rectangle.
With delegation, a Window object would maintain a reference or pointer to a Rectangle object,
and calls to rectangle-like methods of the Window object would be delegated to corresponding
methods of the Rectangle object.
Now let’s consider a slightly more complex example. Suppose employees can classified based
on how they are paid; e.g., hourly or salaried. Using inheritance, we might design three
classes: an Employee class which encapsulates the functionality common to all employees, and
two subclasses HourlyEmployee and SalariedEmployee which encapsulates pay-specific details.
While this design might be suitable for some applications, we would encounter problems in a
scenario where a person changes, say from hourly to salaried. The class of an object and the
inheritance relationship are both static, and objects can’t change their class easily (but see the
State pattern for tips on how to fake it).
A more flexible design would involve delegation – an Employee object could delegate pay-
related method calls to an object whose responsibilities focused solely on how the employee is
paid. In fact, we might still use inheritance here in a slightly different manner by creating an
abstract class (or interface) called PayClassification with two subclasses HourlyPayClassifica
tion and SalariedPayClassifi cation which implement classification- specific computations. Using
delegation as shown, it would be much easier to change the pay classification of an existing
Employee object.
This second example illustrates an important point: In implementing delegation, we often want
the capability to replace the delegate with another object, possibly of a different class.
Therefore delegation will often use inheritance and polymorphism, with classes of potential
delegates being subclasses of an abstract class which encapsulates general delegate
responsibilities.
One final point. Sometimes, the choice between delegation and inheritance is driven by
external factors such as programming language support for multiple inheritance or design
constraints requiring polymorphism. Consider threads in Java. You can associate a class with a
thread in one of two ways: either by extending (inheriting) directly from class Thread, or by
implementing the Runnable interface and then delegating to a Thread object. Often the
approach taken is based on the restriction in Java that a class can only extend one class (i.e.,
Java does not support multiple inheritance) . If the class you want to associate with a thread
already extends some other class in the design, then you would have to use delegation;
otherwise, extending class Thread would usually be the simpler approach.
5. Observer Pattern
Simply, the Observer pattern allows one object (the observer) to watch another (the subject).
The Observer pattern allows the subject and observer to form a publish-subscribe relationship.
Through the Observer pattern, observers can register to receive events from the subject.
When the subject needs to inform its observers of an event, it simply sends the event to each
observer.
Use the Observer pattern in any of the following situations:
• When an abstraction has two aspects, one dependent on the other. Encapsulating
these aspects in separate objects lets you vary and reuse them independently.
• When a change to one object requires changing others, and you don't know how many
objects need to be changed.
DesignPatterns 8
• When an object should be able to notify other objects without making assumptions
about who these objects are. In other words, you don't want these objects tightly
coupled.
import java.util.ArrayList ;
import java.util.Iterator;
while( i.hasNext() ) {
Observer o = ( Observer ) i.next();
o.update( this );
}
}
}
IntegerDataBag holds onto Integer instances. The IntegerDataBag also allows Observers to
add and remove themselves.
Consider these two implementations of Observer -- IntegerAdder and IntegerPrinter:
import java.util.Iterator;
import java.util.Iterator;
}
DesignPatterns 10
IntegerAdder and IntegerPrinter add themselves to the integer bag as observers. When an
IntegerAdder receives an update, it sums up the Integer values held in the bag and displays
them. Likewise, when IntegerPrinter receives an update, it prints out the Integers held in
the bag.
Here is a simple main() that exercises these classes:
6. Struts Patterns
The heart of Struts is the Controller. Struts uses the Front Controller Pattern and Command
Pattern. A single servlet takes a request, translates HTTP parameters into a Java ActionForm,
and passes the ActionForm into a Struts Action class, which is a command. The URI denotes
which Action class to go to. The Struts framework has one single event handler for the HTTP
request. Once the request is met, the Action returns the result back to the front controller,
which then uses it to choose where to navigate next
Front Controller
The Front Controller pattern defines a single component that is responsible for processing
application requests. A front controller centralizes functions such as view selection, security,
DesignPatterns 11
and templating, and applies them consistently across all pages or views. Consequently, when
the behavior of these functions need to change, only a small part of the application needs to
be changed: the controller and its helper classes.
Introducing a controller as the initial point of contact for handling a request. The controller
manages the handling of the request, including invoking security services such as
authentication and authorization, delegating business processing, managing the choice of an
appropriate view, handling errors, and managing the selection of content creation strategies.
The controller provides a centralized entry point that controls and manages Web request
handling. By centralizing decision points and controls, the controller also helps reduce the
amount of Java code, called scriptlets, embedded in the JavaServer Pages (JSP) page.
Centralizing control in the controller and reducing business logic in the view promotes code
reuse across requests. It is a preferable approach to the alternative- embedding code in
multiple views-because that approach may lead to a more error-prone, reuse-by-copy- and-
paste environment.
Dispatcher : A dispatcher is responsible for view management and navigation, managing the
choice of the next view to present to the user, and providing the mechanism for vectoring
control to this resource. A dispatcher can be encapsulated within a controller or can be a
separate component working in coordination. The dispatcher provides either a static
dispatching to the view or a more sophisticated dynamic dispatching mechanism. The
dispatcher uses the RequestDispatcher object (supported in the servlet specification) and
encapsulates some additional processing.
Helper : A helper is responsible for helping a view or controller complete its processing. Thus,
helpers have numerous responsibilities, including gathering data required by the view and
storing this intermediate model, in which case the helper is sometimes referred to as a value
bean. Additionally, helpers may adapt this data model for use by the view. Helpers can service
requests for data from the view by simply providing access to the raw data or by formatting
the data as Web content. A view may work with any number of helpers, which are typically
implemented as JavaBeans components (JSP 1.0+) and custom tags (JSP 1.1+). Additionally,
a helper may represent a Command object, a delegate, or an XSL Transformer, which is used
in combination with a stylesheet to adapt and convert the model into the appropriate form.
View
A view represents and displays information to the client. The view retrieves information from a
model. Helpers support views by encapsulating and adapting the underlying data model for
use in the display.
Front Controller centralizes control. A controller provides a central place to handle system
services and business logic across multiple requests. A controller manages business logic
processing and request handling. Centralized access to an application means that requests are
easily tracked and logged. Keep in mind, though, that as control centralizes, it is possible to
introduce a single point of failure. In practice, this rarely is a problem, though, since multiple
controllers typically exist, either within a single server or in a cluster.
DesignPatterns 12
Command Pattern
A simple example of a common problem the Command Design Pattern is well suited to solve is the
evaluation and execution of an incoming HttpServletRequest action parameter command from a submit on a
JSP page.
In Struts terminology, this class is called an action although you'll also see this general pattern referred to as
the command pattern. At runtime, the Struts controller (a Java servlet) maps the incoming request onto an
action class and calls the execute() method, which contains the (business) logic required to service the
request.
Most servlets in the M-V-C Model 2 form (i.e. Struts framework type) redirect both their doGet() and doPost()
methods to a perform() or performTask( ) method after stripping out the relevant data from the
HttpServletRequest. Thus, we can pass an already constructed object graph the system will then
manipulate. To keep things simple, we will just pass the performTask( ) method of our servlet a String action
parameter such as "create", "replace", "update", etc.
// CommandExample. java
import java.util.HashMap;
class ExampleServlet {
private HashMap commandMap = new HashMap();
} //endmethod: doPost
} //endmethod: performTask2
Switchs with the appropriate command. Obviously, the Switch connected to the Light will
have a different command than the Switch connected to the Fan. The Command class has to be
abstract or an interface for this to work.
When the constructor for a Switch is invoked, it is parameterized with the appropriate set of
commands. The commands will be stored as private variables of the Switch.
When the flipUp() and flipDown() operations are called, they will simply make the
appropriate command to execute( ). The Switch will have no idea what happens as a result
of execute( ) being called.
TestCommand. java
class Fan {
public void startRotate( ) {
System.out.println( "Fan is rotating");
}
public void stopRotate() {
System.out.println( "Fan is not rotating");
}
}
class Light {
public void turnOn( ) {
System.out.println( "Light is on ");
}
public void turnOff( ) {
System.out.println( "Light is off");
}
}
class Switch {
private Command UpCommand, DownCommand;
public Switch( Command Up, Command Down) {
UpCommand = Up; // concrete Command registers itself with the
invoker
DownCommand = Down;
}
void flipUp( ) { // invoker calls back concrete Command, which executes
the Command on the receiver
UpCommand . execute ( ) ;
}
void flipDown( ) {
DownCommand . execute ( );
}
}
class LightOnCommand implements Command {
private Light myLight;
public LightOnCommand ( Light L) {
myLight = L;
}
public void execute( ) {
myLight . turnOn( );
}
}
class LightOffCommand implements Command {
private Light myLight;
public LightOffCommand ( Light L) {
myLight = L;
}
public void execute( ) {
myLight . turnOff( );
}
}
class FanOnCommand implements Command {
private Fan myFan;
DesignPatterns 17
testSwitch.flipUp( );
testSwitch.flipDown ( );
Fan testFan = new Fan( );
FanOnCommand foc = new FanOnCommand( testFan);
FanOffCommand ffc = new FanOffCommand( testFan);
Switch ts = new Switch( foc,ffc);
ts.flipUp( );
ts.flipDown( );
}
}
Command.java
public interface Command {
public abstract void execute ( );
}
Notice in the code example above that the Command pattern completely decouples the object
that invokes the operation -- (Switch ) -- from the ones having the knowledge to perform it
-- Light and Fan. This gives us a lot of flexibility: the object issuing a request must know only
how to issue it; it doesn't need to know how the request will be carried out.
7. DAO
The Data Access Object Pattern, also known as the DAO pattern, abstracts the retrieval of data from a data
resource such as a database. The concept is to "separate a data resource's client interface from its data
access mechanism."
DesignPatterns 18
J2EE developers use the Data Access Object (DAO) design pattern to separate low-level data access logic
from high-level business logic. Implementing the DAO pattern involves more than just writing data access
code.
The problem with accessing data directly is that the source of the data can change. Consider, for example,
that your application is deployed in an environment that accesses an Oracle database. Then it is
subsequently deployed to an environment that uses Microsoft SQL Server. If your application uses stored
procedures and database-specific code (such as generating a number sequence), how do you handle that in
your application? You have two options:
Rewrite your application to use SQL Server instead of Oracle (or add conditional code to handle the
differences) , or
Create a layer inbetween your application logic and the data access
The Data Access Object pattern does the latter.
The benefits of the DAO pattern are obvious, but the implementation is a little tricky. To properly implement
the DAO pattern you need to generate the following components:
1. DAO Interface
2. DAO Factory
3. DAO Implementation classes
4. Deployment Descriptor Entry
The DAO pattern, in and of itself, does not necessarily require a factory, but in practice you will see the two
patterns coupled. The reason is that some mechanism needs to be created to obtain the appropriate
implementation class and using a factory is the cleanest implementation.
Example
DAO Interface
The DAO interface defines all the methods that your data access objects will provide. For a
database, these include things like inserting, deleting, updating, and retrieving rows. Because
it is an interface, the methods will not be implemented. Listing 1 shows a sample DAO
interface.
SampleDAO.java
SampleModel. java
The class that eventually uses the implementation of this interface will use it via this interface,
and not through the implementation itself.
DAO Implementation
We need a class that performs the actual data access; this will be the function of the DAO
implementation class. Its job is to translate the general request for information to a request
specific to the data source it represents. For database access, I usually suggest the following:
• JDBC Generic implementation: this is an implementation that uses only JDBC and
standard SQL
• Database vendor specific implementation number one: e.g. Oracle
• Database vendor specific implementation number two: e.g. SQL Server
• Database vendor specific implementation number ...
Below shows a sample implementation of the DAO implementation class.
SampleDAOJDBCImpl. java
import java.sql.*;
DAO Factory
We need a mechanism for determining what class to load at runtime and use in our
application. This is the job of the DAO factory class.
The factory class is used in another pattern, the Factory Pattern. The purpose of the factory
pattern is to defer the choice of an implementation class until runtime. You ask the factory to
create a class that implements a specific interface and (given some piece of additional
environmental information) , the factory creates the appropriate class.
In the case of an EJB deployment, the additional environmental information is usually specified
through the EJB's deployment descriptor. In a stand-alone environment, you can specify it with
Java system properties or a configuration file (such as an XML file or properties file).
SampleDAOFactory. java
try {
DesignPatterns 21
The SampleDAOFactory class maintains a static reference to the SampleDAO class. The first
time the DAO object is requested, the factory looks up the fully qualified class name of the
implementation class specified by the deployment descriptor key: "SAMPLEDAO.Impl" . It uses
the InitialContext class to access the Java Naming and Directory Interface (JNDI) and then
calls its lookup() method to find the String value. Finally, it loads the class dynamically by
calling Class.forName( ) and casting it to SampleDAO.
The final piece of information to get into the system is the association between
"SAMPLEDAO.Impl" and the actual class that implements the SampleDAO interface
(com.ks.myejb. dao.SampleDAOJDB CImpl). This is accomplished by creating an <env-
entry> entry in the ejb-jar.xml file. Below shows an excerpt from this file.
<ejb-jar>
<enterprise-beans>
<entity>
<description>Sample
Bean</description>
<ejb-name>SampleBean</ejb-name>
<home>com.ks.myejb. ejb.SampleHome</home>
<remote>com.ks.myejb. ejb.Sample</remote>
<ejb-class>com.ks.myejb. ejb.SampleEJB</ejb-class>
<persistence- type>Bean</persistence- type>
<prim-key-class>com.ks.myejb. ejb.SamplePk</prim-key-class>
<reentrant>False</reentrant>
<env-entry>
<env-entry-name>SAMPLEDAO.Impl</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>com.ks.myejb. dao.SampleDAOJDB CImpl</env-
entry-value>
</env-entry>
...
</entry>
...
</enterprise- beans>
<ejb-jar>
In this case, we define a java.lang.String key named "SAMPLEDAO.Impl" to be
"com.ks.myejb. dao.SampleDAOJDB CImpl". This will make a JNDI entry into any
class owned by the SampleBean class.
8. Session Facade
DesignPatterns 22
Brief Description
Session facade is one design pattern that is often used while developing enterprise
applications. It is implemented as a higher level component (i.e.: Session EJB), and it contains
all the iteractions between low level components (i.e.: Entity EJB). It then provides a single
interface for the functionality of an application or part of it, and it decouples lower level
components simplifying the design.
Think of a bank situation, where you have someone that would like to transfer money from
one account to another.
In this type of scenario, the client has to check that the user is authorized, get the status of
the two accounts, check that there are enough money on the first one, and then call the
transfer. The entire transfer has to be done in a single transaction otherwise is something goes
south, the situation has to be restored.
As you can see, multiple server-side objects need to be accessed and possibly modified.
Multiple fine-grained invocations of Entity (or even Session) Beans add the overhead of
network calls, even multiple transaction. In other words, the risk is to have a solution that has
a high network overhead, high coupling, poor reusability and mantainability.
The best solution is then to wrap all the calls inside a Session Bean, so the clients will have a
single point to access (that is the session bean) that will take care of handling all the rest.
Many business processes involve complex manipulations of business classes. Business classes
often participate in multiple business processes or workflows. Complex processes that involve
multiple business objects can lead to tight coupling between those classes, with a resulting
decrease in flexibility and design clarity. Complex relationships between low-level business
components make clients difficult to write.
The Session Facade pattern defines a higher-level business component that contains and
centralizes complex interactions between lower-level business components. A Session Facade
is implemented as a session enterprise bean. It provides clients with a single interface for the
functionality of an application or application subset. It also decouples lower-level business
components from one another, making designs more flexible and comprehensible.
Fine-grained access through remote interfaces is inadvisable because it increases network
traffic and latency. The "before" diagram in Figure 1 below shows a sequence diagram of a
client accessing fine-grained business objects through a remote interface. The multiple fine-
grained calls create a great deal of network traffic, and performance suffers because of the
high latency of the remote calls.
The first important benefit of the Session Facade pattern is that the client has a simpler interface and
doesn't need to know about the details of which entity beans and which methods to use. In my analogy, the
session bean is like the general contractor and the entity beans are the subcontractors.
The Session Facade pattern improves performance as only the calls between the client and the session
bean go across the network, while calls from the session bean to the entity beans are local to the EJB
container. Performance can be further enhanced through the use of local interfaces, introduced as part of
the EJB specification version 2.0. Local interfaces provide support for "lightweight" access from within the
EJB container, avoiding the overhead associated with a remote interface.
A third benefit of the Session Facade pattern relates to transactions. With container-managed transactions,
the container begins a transaction when a method starts to execute and ends the transaction when the
DesignPatterns 23
method returns. By enclosing the entity beans calls in the session bean method, the database operations
are automatically grouped together as a transactional unit. To ensure that they participate in the same
transaction, the entity bean methods should be assigned the "Required" transaction attribute in ejb-jar.xml
(or by using the WebLogic Administration Console).
Sample Code
Consider a Professional Services Application (PSA), where the workflow related to entity beans
(such as Project, Resource) is encapsulated in ProjectResourceMana gerSession, implemented
using the Session Facade pattern. Example 8.15 shows the interaction with Resource and
Project entity beans, as well as other business components, like Value List Handlers (see
"Value List Handler" on page 354) and Transfer Object Assemblers (see "Transfer Object
Assembler" on page 340).
Example 8.15 Implementing Session Facade - Session Bean
package corepatterns. apps.psa. ejb;
import java.util.*;
import java.rmi.RemoteExce ption;
import javax.ejb.*;
import javax.naming. *;
import corepatterns. apps.psa. core.*;
import corepatterns. util.ServiceLoca tor;
import corepatterns. util.ServiceLoca torException;
// default create
public void ejbCreate()
throws CreateException {
}
try {
// locate and connect to entity beans
connectToEntities( resourceId, projectId, ...);
} catch(...) {
// Handle exceptions
}
}
DesignPatterns 24
try {
if ((projectEntity == null) ||
(resourceEntity == null)) {
DesignPatterns 25
} catch(...) {
// Handle exceptions
}
}
...
...
The remote interface for the Session Facade is listed in Example 8.16.
Example 8.16 Implementing Session Facade - Remote Interface
...
...
...
}
The Home interface for the Session Facade is shown in Example 8.17.
Example 8.17 Implementing Session Facade - Home Interface
package corepatterns. apps.psa. ejb;
import javax.ejb.EJBHome;
import java.rmi.RemoteExce ption;
import corepatterns. apps.psa. core.ResourceExc eption;
import javax.ejb.*;
9. Service Locator
Enterprise applications require a way to look up the service objects that provide access to
distributed components. JavaTM 2 Platform, Enterprise Edition (J2EE) applications use Java
Naming and Directory Interface (JNDI) to look up enterprise bean home interfaces, Java
Message Service (JMS) components, data sources, connections, and connection factories.
Repetitious lookup code makes code difficult to read and maintain. Furthermore, unnecessary
JNDI initial context creation and service object lookups can can cause performance problems.
The Service Locator pattern centralizes distributed service object lookups, provides a
centralized point of control, and may act as a cache that eliminates redundant lookups. It also
encapsulates any vendor-specific features of the lookup process.
J2EE uses the JNDI tree to lookup, access and invoke business services on passing a unique registered
JNDI name. If the service is used by various clients, then the code for looking up the object gets duplicated
in various forms which makes it difficult to maintain the application. This pattern is used to address this
issue by storing the lookup values for all services in a single place and provide it on request.
DesignPatterns 29
Among other things, J2EE application servers provide a collection of services, such as JMS
Servers and JDBC Connection Pools. They provide access to these services through a Java
Naming and Directory Interface (JNDI) -- a central repository to register services, beans, and
environment variables.
The key to accessing services and loading beans is obtaining a reference to the JNDI server.
The only difficulty is that locating the JNDI server involves connecting to a specific port and
URL that are application server dependent. Thus, all classes that need to locate objects must
be aware of the connection information required to find the JNDI server.
Sun defines the Service Locator Pattern saying that it "centralizes distributed service object
lookups, provides a centralized point of control, and may act as a cache that eliminates
redundant lookups." And they further iterate that it "encapsulates any vendor-specific features
of the lookup process."
Because I spend most of my days concerned with the performance of J2EE applications and
application servers, JNDI lookups are particularly important to me. The core process in tuning
Web-based applications is to look at what occurs during every request
JNDI lookups are expensive and thus should only be made when necessary. Some would argue
that whenever your code needs to access a service or component that it is necessary to go
directly to the JNDI server, but unless you know that the JNDI server entries can change often,
it is far better to look up references once and then cache them.
Client
This is the client of the Service Locator. The client is an object that typically requires
access to business objects such as a Business Delegate (see “Business Delegate” )
Service Locator
The Service Locator abstracts the API lookup (naming) services, vendor
dependencies, lookup complexities, and business object creation, and provides a
simple interface to clients. This reduces the client's complexity. In addition, the
same client or other clients can reuse the Service Locator.
InitialContext
The InitialContext object is the start point in the lookup and creation process.
Service providers provide the context object, which varies depending on the type of
business object provided by the Service Locator's lookup and creation service. A
Service Locator that provides services for multiple types of business objects (such
as enterprise beans, JMS components, and so forth) utilizes multiple types of
context objects, each obtained from a different provider (e.g., context provider for
an EJB application server may be different from the context provider for JMS
service).
ServiceFactory
The ServiceFactory object represents an object that provides life cycle management
for the BusinessService objects. The ServiceFactory object for enterprise beans is
an EJBHome object. The ServiceFactory for JMS components can be a JMS
ConnectionFactory object, such as a TopicConnectionFact ory (for publish/subscribe
messaging model) or a QueueConnectionFact ory (for point-to-point messaging
model).
BusinessService
The BusinessService is a role that is fulfilled by the service the client is seeking to
access. The BusinessService object is created or looked up or removed by the ServiceFactory.
The BusinessService object in the context of an EJB application is an
enterprise bean. The BusinessService object in the context of a JMS application can
be a TopicConnection or a QueueConnection. The TopicConnection and
QueueConnection can then be used to produce a JMSSession object, such as
TopicSession or a QueueSession respectively.
DesignPatterns 30
Pattern Details
The ServiceLocator is a singleton class (meaning that there is only one defined per virtual
machine) that on loading establishes a connection to the JNDI server and creates a cache that
will maintain a mapping between requested resource/component names at the associated
resource/component. Listing 1 shows an excerpt from the Petstore code for the
ServiceLocator. java file.
static {
try {
me = new ServiceLocator( );
} catch(ServiceLocato rException se) {
System.err.println( se);
se.printStackTrace( System.err) ;
}
}
private ServiceLocator( ) throws ServiceLocatorExcep tion {
try {
ic = new InitialContext( );
cache = Collections. synchronizedMap( new HashMap());
} catch (NamingException ne) {
throw new ServiceLocatorExcep tion(ne);
}
}
methods are used for accessing JMS resources and the getDataSource( ) method returns a
JDBC Data Source. The final three methods load environment entries that are defined in your
deployment descriptors; this is a mechanism that you can use to customize the behavior of
your J2EE application at runtime.
Listing 2 shows the implementation of the getRemoteHome( ) method.
return home;
}
From listing 2 you can see the code first checks to see if the requested EJBHome has already
been loaded into the cache; if not it loads it through its cached InitialContext instance and
stores it in the cache. The remaining methods are similarly implemented, and I encourage you
to look through the Pet Store source code for more details.
Summary
The Service Locator Pattern is used to abstract application server details from JNDI lookups,
centralize all access to JNDI resources, and improve performance by caching resources and a
reference to the JNDI server itself. The Java PetStore application makes use of this pattern and
should be referenced for more implementation details.
.
__,_._,___