Tải bản đầy đủ (.pdf) (69 trang)

Expert one-on-one J2EE Design and Development phần 7 ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.51 MB, 69 trang )






The AbstractLocalStatelessSessionServiceLocator subclass implements the located( )
template method to check that that the object is actually an EJB local home, and to invoke an abstract
setEjbHome( ) method that must be implemented by subclasses that know the application-specific type
of the EJB home, enabling them to obtain EJB references from it:












420

Brought to you by ownSky
Infrastructure and Application Implementation
It's always safe to cache a reference to a local EJB home interface. Although the EJB specification (§6.2.1)
implies that it's safe to cache a remote home, there is a slight chance that a cached remote home reference
may become "stale" in some servers (for example, if the remote server hosting the EJB was restarted
during the client's lifetime).
The following UML class diagram illustrates the superclasses for both local and remote SLSB service
locators, and how a LocalSLSBBoxOf f iceFactory class might be used in the sample application to


create BoxOffice objects (actually EJBs) as required:





Let's look at how these superclasses can be used to simplify the implementation of a real service
locator. The BoxOff iceFactory interface is the public interface of our application-specific typed
service locator:
public interface BoxOfficeFactory {
BoxOffice getBoxOffice() throws FatalException;
}
Extending the

com.interface21.ejb.access.AbstractionLocalStatelessSessionServiceLocator
superclass makes it trivial to implement this interface:

public class LocalSLSBBoxOfflceFactory

extends AbstractLocalStatelessSessionServiceLocator
implements BoxOf ficeFactory {

The implementation of the protected abstraction setEjbHome() method caches the home
interface, after casting it to the appropriate type. This method will be invoked once, when the
service locator is initialized:
Private BoxOfficeHomehome;
protected void setEjbHome(EJBLocalHome home)
{ this.home = (BoxOfficeHome) home; }
421
Brought to you by ownSky

The implementation of the getBoxOffice( ) method from the BoxOfficeFactory interface, which will
be invoked by code using the EJB, creates a new SLSB reference as required. EJBCreate exceptions are
caught and an unchecked exception, FatalException, is rethrown:





The following bean definition shows how such a service locator could be defined in the sample
application's bean factory. The only property that must be set is jndiName, inherited from the
AbstractJndiLocator superclass:
<bean name="boxOfficeFactory"

class="com.wrox.expertj2ee.ticket.framework.ejb.LocalSLSBBoxOfficeFactory" >

<property name=" jndiName">ejb/BoxOf f ice</property>
</bean>

This is a simple approach that works very well with local EJBs. It hides EJB access from code that uses the
business interface. One disadvantage is that it requires a factory interface and an implementing class for
each EJB. This isn't usually a major problem, as with sensible design few systems need a vast number of
EJBs. Slightly more seriously, all code that uses the EJB must work with the factory object: it's impossible
to hold on to a reference to the business interface.
Transparent Dynamic Proxy: An Alternative to the Typed Service Locator for Local EJB Access
An alternative to the Service Locator pattern that achieves the same end is to conceal the factory in a
custom bean definition, as described when we discussed our "bean factory" approach above. The custom
bean definition looks up and caches the bean's home interface before returning an object of the required
interface, which is actually a dynamic proxy wrapping a new instance of the SLSB. Before each method
invocation, the dynamic proxy - which is threadsafe - transparently creates a new instance of the SLSB.
Although the custom bean definition doesn't know the type of the EJB, it knows that an SLSB local

interface must expose a create ( ) method without arguments, which it can use a BeanWrapper object to
invoke by name. Benchmarks show that this approach has only a small performance overhead.
This use of dynamic proxies and reflective method invocation sounds complex, but the complexity is
hidden within framework code; it enables us to write less application code. Applications need only define
a bean using the custom bean definition, as follows. Note the specification of theJNDI name and business
interface:
<bean name="boxOffice"
definitionClass=
"com.interface21.ejb.access.LocalstatelessSessionProxyBeanDefinit ion ">

<customDef inition property="business Interf ace">

com.wrox.expertj2ee.ticket.boxof f ice.BoxOffice
</customDefinition>

422
Brought to you by ownSky
Infrastructure and Application Implementation
<customDefinition property="jndiName">
ejb/BoxOffice
</customDefinition>
</bean>
That's it: calling code can simply obtain a BoxOff ice object like this, and cache it:
BoxOffice boxOffice = (BoxOffice) beanFactory.getBean("boxOffice");

Most likely, this won't even be necessary: objects that need to use the BoxOffice interface can simply
expose a bean property and have the application context set it to the boxOffice reference at run time.
This approach has the advantage of requiring no custom code to access an EJB. We need only define a
bean using the custom definition.
This is the approach used in the sample application, although it would be simple to switch to the Typed

Service Locator approach, code for which is included in the download.
See the code in the com. interface21. ejb. access.
LocalStatelessSessionProxyBeanDef inition class for the implementation of
this approach.
Using the Business Delegate Pattern for Remote EJB Access
A higher level of decoupling is achieved by the Business Delegate pattern, in which a client-side object
exposes business methods that are implemented by calls to the EJB. This is more of a facade than a proxy
approach, as the business delegate may change method signatures - for example, changing exception
types or combining method calls, or even invoking multiple EJBs.
As the Business Delegate pattern requires more work to implement than the Service Locator, I'll focus on
its use with remote interfaces, with which it delivers real value (its benefits are reduced with local EJB
access). The benefits of a business delegate approach include:
o The business delegate may be able to retry failed transactions if this is indicated, without calling
code being complicated.
o It's possible to handle exceptions from the server in a consistent way without every client
needing to catch J2EE infrastructure-related exceptions such as java.rmi.RemoteException
and javax.naming.NamingException. The business delegate can catch such low-level
exceptions, log them and throw an application-specific exception that makes more sense to
client code. Or it may be appropriate to wrap EJB-tier exceptions as unchecked exceptions to
simplify client code. This makes sense when exceptions are fatal - for example, if we've
established that there's no point retrying after encountering a remote exception that prevents
execution of a use case.
o It may be possible to implement operations at a higher level of abstraction. For example, if a
particular use case requires multiple EJB invocations, these may be able to be performed in the
business delegate.
o It may be possible to introduce caching by providing a caching implementation of the
business delegate interface. This will benefit all clients.
423
Brought to you by ownSky
The drawback of using a business delegate is the potential duplication of the business interface exposed by

the EJB tier on the client-side. However, using a business delegate offers an opportunity to provide an
interface that best fits the needs of clients. For example, we can simplify the interface exposed by the EJB
tier by omitting irrelevant operations. We can simplify client code by only throwing checked exceptions if
they're part of the API, and the client can be expected to handle them.
It's superficially attractive for the business delegate to implement the session bean's business methods interface.
However, this fails to decouple EJB tier and client tier, and cannot deliver some of the benefits we've identified,
such as concealing remote exceptions and simplifying the client API. Hence, I don't recommend copying
method signatures in the business delegate (the approach recommended in EJB Design Pattern. If this is
appropriate, the Service Locator pattern is simpler and should be preferred.
Our framework provides easy support for implementing business delegates. The same superclass we used
for the typed service locator, AbstractLocalStatelessSessionServiceLocator, can be used as a
superclass for a business delegate. Instead of merely returning an instance of the EJB on demand, a
subclass would implement business operations, calling the EJB as necessary.
As the sample application uses EJBs with local interfaces, there is no need to use the Business Delegate
pattern, which requires more work to implement than a service locator.
The following example illustrates the use of a business delegate to access a remote EJB. It uses a
hypothetical EJB with a remote interface that exposes two int values: one of which can be cached, and one
of which cannot. We saw this EJB's business methods interface and component interface under
Implementing EJBs above. The remote interface exposes the following business methods:
int getUncacheableValue() throws RemoteException;
int getCacheableValue() throws RemoteException;

By using the Business Delegate pattern, we can simplify calling code by hiding remote exceptions and
rethrowing unchecked fatal exceptions, and improve performance by caching the cacheable data value
for a given period.
When using the Business Delegate pattern, it is good practice to define a business delegate interface,
which application code will work with. This enables a caching implementation to be substituted without
impact on client code, for example. The interface might look as follows for this EJB:
public interface MyBusinessDelegate {


int getUncacheableValue() throws FatalException;

int getCacheableValue() throws FatalException; }

The following implementation extends the AbstractRemoteStatelessSessionServiceLocator
framework superclass, which performs the necessary JNDI lookup and provides it with an EJB home it can
cache in the setEjbHome() method:
public class MyBusinessDelegatelmpl
extends AbstractRemoteStatelessSessionServiceLocator
implements MyBusinessDelegate {
private static long MAX_DATA_AGE = 10000L;
private CalculatorHome home;
424
Brought to you by ownSky
Infrastructure and Application Implementation










It's important to abstract access to EJBs. With EJBs with local interfaces, use the Service
Locator pattern, or the Business Delegate if there is a good reason that calling code
shouldn't work with the EJB interface. With EJBs with remote interfaces, use the Business
Delegate pattern, and conceal remote access details from clients.
Using JMS

Like JDBC, JMS is a complex API. Working with it directly can obscure the purpose of application
code. Thus an abstraction layer is appropriate. The com.interface21.jms.JmsTemplate class applies
the same approach as the JdbcTemplate we examined in Chapter 9. We should try to conceal in
framework code the complexity of the necessary JNDI lookups and the requirement to work with
multiple JMS API objects.

425
Brought to you by ownSky
Let's consider com.interface21.jms.JmsTemplate class's support for Pub/Sub messaging. On startup,
a JmsTemplate instance does a JNDI lookup for the TopicConnectionFactory. The JNDI name is
passed into the constructor, as it may differ between application servers. The following method uses the
cached TopicConnectionFactory to invoke a callback interface to create a message given a
TopicSession, which it then publishes:








The com. interface21.jms.JmsException exception is unchecked, meaning that we have a. choice as
to whether to catch it. Calling code can implement the PubSubMessageCreator inner interface of
JmsTemplate (shown below) without deep knowledge of JNDI:
public interface PubSubMessageCreator {

Message createMessage (TopicSession topicSession) throws JHSException; }

426
Brought to you by ownSky

Infrastructure and Application Implementation
Thus a message can be published with the following code:




Message consumers can also benefit from a simplifying infrastructure. Note that it's often best to use
MDBs as message consumers, as the EJB container provides valuable infrastructure for JMS message
consumption; however, we can't always use EJB. We always need to implement the MessageListener
interface. However, we don't want to have to deal with the complexity of registering a listener with a topic,
which also involves multiple JNDI lookups. The JmsTemplate class provides a convenience method for
this, with the following signature:
public TopicConnection subscribeToTopicNonDurable (String topicName,
MessageListener listener, String messageSelector)
throws Jms Except ion;

A custom bean definition of class com.interface21.jms.JmsListenerBeanDefinition can
automatically register any JMS listener to a given topic. The following example is from the sample
application, in which the
com.wrox.expertj2ee.ticket.referencedata.support.DataUpdateJmsListener JMS listener
notifies caching objects of a data update:
<bean name="referenceDataListener"

def initionClass="com.interface21.jms.JmsListenerBeanDef inition ">

<customDefinition property="listenerBean">

com.wrox.expert j2ee.ticket.referencedata.support.DataUpdateJmsListener
</customDef inition>


<customDefinition property="topicConnectionFactoryName">

jms/TopicFactory
</customDef inition>

<customDefinition property="topicName">

jms /topic/referencedata
</customDef inition>

<property name="cachingCalendar" beanRef =" true">

calendar
</property>
</bean>

This enables us to implement a simple JMS listener that responds to events and refreshes data cached in
another application component without the need to write any low-level JMS code, as shown in the
following listing:
427
Brought to you by ownSky




This simple abstraction doesn't take care of all JMS requirements - the JMS API includes many features
such as transacted sessions, which pose more problems for abstraction. However, it provides a simple
infrastructure for the commonest needs.
Implementing Business Logic
As a professional developer, you know how to program in Java. What I want to discuss here is how to put

an application together and how to focus your programming skills on your application's domain. Thus the
following discussion of the sample application does not address low-level details such as the
implementation of the adjacent seating algorithm, but focuses on how the sample application's
components are configured and work together.
We'll look both at how the sample application uses standard J2EE infrastructure and how it uses the
infrastructure we've discussed so far in this chapter.
The sample application's code is available in the download accompanying this book. Please refer to it a
necessary during the following discussion, as it's impractical to provide a complete listing of all the classes
mentioned.
Implementing the Sample Application
Let's now consider some of the key business logic of the sample application. Let's focus on two key issues:
how we obtain reference data about genres, shows, and performances; and how we obtain information
about seat availability and implement the booking process.
These use cases are described in detail in Chapter 5, so I'll just provide a quick summary of the requirement here,
along with the high-level design decisions taken in the last few chapters concerning them.
428

Brought to you by ownSky
Infrastructure and Application Implementation
Genre, show, and performance data changes rarely and can be shared between all users of the
application. The retrieval of this data need not be transactional.
The booking process requires transactional data access, and involves the holding of user session state. We
have chosen to hold user session state in an HttpSession object in the web container.
Defining Business Interfaces
The first step is to define the necessary business interfaces. These will not be web-specific; however,
they will run in the web container in a collocated application.
These interfaces should be able to be implemented with or without EJB without affecting code that uses
them. In a collocated application such as our sample application, we'll use the Service Locator pattern,
discussed above, to hide the details of EJB access completely.
There is only one area in which retaining the option of using EJB affects our design: exception handling. As

EJBs handle unchecked exceptions as fatal system exceptions, as we saw in Chapter 10, we are unable to
use unchecked exceptions in a business interface that might be implemented by an EJB. Occasionally this
calls for compromises.
Consider the case of a request to find the number of seats free for a performance, given a performance ID
that doesn't match any performance. Such a request cannot occur in our application, as the user can only
request availability using links from pages that will provide valid performance IDs. Thus it makes no sense
to complicate application code by catching a checked NoSuchPerformanceException.
However, given that we would like the ability to implement our BoxOffice interface using EJB, we must
choose the lesser of the following two evils: make all our application exceptions (including
NoSuchPerformanceException) checked and cope with the overhead of catching checked exceptions
where this serves no useful purpose; or lose the ability to switch between EJBs and other business object
implementations transparently. In the present application we opt for the use of checked exceptions. We
could use the Business Delegate pattern, to catch checked exceptions from EJBs and rethrow unchecked
exceptions. However, this requires more work to implement.
In the sample application, we will want two separate interfaces to handle the functionality we're
considering here: one to deal with reference data, and one to deal with availability and the booking
process. Let's consider each in turn.
The com.wrox.expertj2ee.ticket.referencedata. Calendar interface handles reference data
requirements:
public interface Calendar {

List getCurrentGenres();

Show getShowtint(id) throws ReferenceDataException;

Performance getPerformance(int id) throws ReferenceDataException;
}

The Genre, Show, and Performance objects are lightweight value objects, disconnected from the data
source. They expose a tree structure, navigable downwards: from a genre we can find shows, and from

each show we can get a list of performances. The entire tree can be cacheable for up to one minute, or as
long as we know that there has been no change to reference data (the complexity of caching objects
individually is unwarranted, as it would deliver little benefit).
429
Brought to you by ownSky
The com. expert J2ee .ticket . boxof f ice.BoxOf f ice interface exposes information about seating
availability and exposes the booking process. It includes the following methods:






The first two methods expose seat availability. The allocateSeats( ) method creates a reservation,
while the confirmReservation( ) method turns an existing reservation into a purchase. There is little
opportunity for caching here, and the two booking methods are transactional.
The ReservationRequest object, used as a parameter to the allocateSeats( ) method, is a
command, as is the PurchaseRequest parameter to the confirmReservation( ) method. The use of
command objects has the following advantages, where multiple individual parameters would otherwise
be required:
o It's possible to add parameter data without breaking method signatures.
o Commands provide an excellent basis for event publication if necessary, using JMS or a simple
Observer implementation. For example, we could easily publish an event with a command
attached to enable an e-mail to be sent when a user makes a successful reservation.
o It may be possible to bind HTTP request parameters values onto a command. Most MVC web
application frameworks can automatically populate JavaBean properties of command objects from
HTTP request parameters. This can greatly simplify application request processing code in the web
tier.
A listing of the ReservationRequest command shows that such commands are merely simple data
holders. This object is serializable not to enable remote invocation, but to allow it to be stored in an

HttpSession, which may need to be replicated across a cluster or written to a persistent store if
swapped out of application server RAM:
public class ReservationRequest implements Serializable {
private static final long MILLIS_PER_MINUTE = 1000L * 60L;

private int performanceID;
private int seatsRequested;

430
Brought to you by ownSky

























431
Brought to you by ownSky
Infrastructure and Application Implementation









Business interface return values, such as the Reservation object, are coded to interfaces. This enables
implementations to return an immutable implementation, to prevent manipulation - inadvertent or
malicious - by calling code.
Determining Implementation Strategy
The Calendar interface involves non-transactional data access. Thus there is no need to implement it
using an EJB; we can use DAOs in the web container.
The BoxOffice interface involves transactional data manipulation. Thus a stateless session bean with a
local interface is an obvious implementation choice, because using CMT can simplify application code.
These decisions will not affect code that uses these objects. Thus if we wish to increase or decrease use of
EJB in the future, or use a different data-access strategy, there will be limited impact on the codebase.
Let's consider the implementation of the Calendar interface first. The
com.wrox.expertj2ee.ticket.referencedata.jdbc.JdbcCalendar class provides a
threadsafe implementation using the JDBC abstraction packages discussed in Chapter 9. The source
code is similar to that shown in Chapter 9. The JdbcCalendar class implements the

com.interface21.beans.factory.initializingBean interface, enabling it to load data in its
initialization of the afterPropertiesSet() method.

432

Brought to you by ownSky
Infrastructure and Application Implementation
As all other application objects are coded to use the Calendar interface, rather than the
JdbcCalendar implementation, caching can be handled by the
com.wrox.expertj2ee.ticket.referencedata.support.CachingCalendar class,
which proxies any underlying Calendar implementation (such as a JdbcCalendar) and
uses copy-on-write to construct a new Calendar implementation when its refresh()
method is invoked. Such use of a caching proxy is a natural idiom for interface-based
design, which can significantly benefit performance while keeping caching logic separate
from application implementation.
Implementing the BoxOffice
The BoxOfficeEJB uses two helper objects: the BoxOfficeDAO interface, which hides the details of data
access behind an abstraction layer and which we looked at in Chapter 9; and the CreditCardProcessor
interface, which provides the ability to process credit card payments. The sample application uses a
dummy implementation of this interface; in a real application, an implementation would probably
contact an external payment processing system.
The com.wrox.expertj2ee.boxoffice.ejb.BoxOfficeEJBLocal EJB component interface
extends the BoxOffice interface (its "business methods" interface) as well as
javax.ejb.EJBLocalObject, as required by the EJB specification:
public interface BoxOfficeLocal extends BoxOffice, EJBLocalObject {}

The local home interface, com.wrox.expertj2ee.boxoff ice.ejb.BoxOfficeHome, specifies the single
create method required of all stateless session beans:
public interface BoxOfficeHome extends EJBLocalHome {


BoxOfficeLocal create() throws CreateException; }

The bean implementation class will extend our

com.interface21.ejb.support.AbstractStatelessSessionBean framework class and

implement the BoxOffice business interface:

public class BoxOfficeEJB extends AbstractStatelessSessionBean
implements BoxOffice {

Extending AbstractStatelessSessionBean forces us to implement a method with the signature
ejbCreate(), matching the create() method on the home interface, which we use to obtain and cache
in instance variables helper objects defined in the bean factory:
public void ejbCreate() {
logger.config("Trying to load data source bean");
this.dao = {BoxOfficeDAO)
getBeanFactory().getBean("dao");
logger.config("Data source loaded OK: [" + this.dao + "]");
this.CreditCardProcessor = (CreditCardProcessor)
getBeanFactory().getBean("CreditCardProcessor");
logger.config("Credit card processing loaded OK: [" +
his.CreditCardProcessor + "]"); t


433
Brought to you by ownSky
The corresponding bean definitions in the ejb-jar.xml deployment descriptor use the JNDI bean
factory syntax described earlier in this chapter, as follows:
<env-entry>

<env-entry-narne>beans.dao.class</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>
com.wrox.expertj2ee.ticket.boxoffice.support.jdbc.
JBoss300racleJdbcBoxOfficeDao
</env-entr value> y-
</env-entry>
<env-entry>
<env-entry-name>beans.creditCardProcessor.class</en -entry-name> v
<env-entry-type>j va.lang.String</env-entry-type> a
<env-entry-value>
com.wrox.expertj2ee.ticket.boxoffice.support.DummyCreditCardProcessor
</env-entry-value>
</env-entry>
Please see the download for a complete listing of this EJB. However, let's look at the implementation of the
confirmReservation() method, which illustrates use of CMT. This method is marked in the
deployment descriptor to run in a transaction. We use programmatic rollback via the setRollbackOnly()
method of the SessionContext if we fail to authorize a credit card transaction. Before we attempt credit
card authorization we attempt to create a booking. This makes it very unlikely that the user can be debited
for a purchase, yet fail to be given a booking.
This method will throw only checked exceptions, as we want to preserve exceptions for clients:
public Booking confirmReservation(PurchaseRequest purchase)
throws ExpiredReservationTakenException,
CreditCardAuthorizationException,
BoxOfficelnternalException,
InvalidSeatingRequestException {

Before we attempt to authorize payment, we create a purchase record that will create a permanent
record in the event of a later, unexpected failure:
int purchaseld = dao.createPurchaseRecord(purchase.getReservation());

String authorization = null;

We use CMT to roll this data update back if the credit card transaction is declined (in the highlighted code
below). In the unlikely event that payment is accepted, but we fail to update the purchase record with the
authorization code (a fatal internal error), we create a log record but throw a checked exception.
Throwing a runtime exception would result in the transaction being rolled back, which would lose the
new purchase record, which should remain in the database:
try {
authorization = creditCardProcessor.process(
purchase.getReservation().getTotalPrice(),
purchase.getCardNumber(), purchase.getExpiry());
// Complete data update for purchase
434
Brought to you by ownSky
Infrastructure and Application Implementation











By using EJB with CMT, we've simplified our application code: transaction management adds
minimal complexity.
Note that we can catch the generic com.interface21.dao.DataAccessException described in Chapter
9 without introducing dependency on the use of JDBC to implement the BoxOfficeDAO data access

interface.
Using JMS to Propagate Data Updates
For efficiency we choose to cache reference data until we know that it has been updated. However, such
updates must work across a cluster of servers, so we cannot use the support for the Observer design
pattern built into our application framework - we must use JMS. A JMS listener will cause cached data to
be refreshed on messages to a data update topic. When the Admin interface is available, it will publish
JMS messages on changes to reference data; before the Admin interface is completed, administrators will
need to access a restricted URL that will cause a reference data update event to be published after
updating the database.
Note that as the cached data isn't held in the EJB container, we cannot use an MDB as a
message consumer.
We saw the implementation of the

com.wrox.expertj2ee.ticket.referencedata.support.DataUpdateJmsListener class, which
consumes JMS messages, in our discussion of JMS infrastructure above. This invokes the refresh()
method of the CachingCalendar object, causing all reference data to be reread from the database.


435

Brought to you by ownSky
Pulling It All Together
Now we have our interfaces and implementations, we can use our application framework to hook them
up. The following excerpts from the ticket-servlet.xml application context XML definition file
illustrate how the application components we've discussed are wired up as JavaBeans.
The following two bean definitions define a Calendar implementation that uses JDBC, and a "caching
calendar" that creates new objects using the realCalendar definition when it receives update events.
Note that the singleton attribute of the realCalendar definition is set to false, so a new object will
be returned each time the cachingCalendar asks for an instance of this bean definition:
<bean name="realCalendar"


singleton="false"

class="com.wrox.expertj2ee.ticket.referencedata.jdbc.JdbcCalendar">
</bean>

<bean name="calendar"

class="com.wrox.expertj2ee.ticket.referencedata.support.CachingCalendar">
</bean>

The following custom bean definition, discussed earlier, registers aJMS listener that notifies the
CachingCalendar object when reference data is updated:
<bean name="referenceDataListener"
definitionClass="com.interface21.jms.JmsListenerBeanDefinition">
<cus tomDe f ini t ion property="1i stenerBean">
com. wrox.expertj 2ee.ticket.referencedata.support.DataUpdateJmsListener
</customDefinition>
<customDefinition property="topicConnectionFactoryName">
jms/TopicFactory
</customDefinition>
<customDefinition property="topicNarae">
jms/topic/referencedata
</customDefinition>
<property name="cachingCalendar" beanRef="true">
calendar
</property>
</bean>
The BoxOffice bean definition uses the dynamic proxy bean definition discussed above to hide the
complexity of EJB access. We specify the business interface the EJB implements and the bean's JNDI name-

<bean name="boxOffice"
definitionClass="
com.interface21.ejb.access.LocalstatelessSessionProxyBeanDefinition">
<customDefinition property="businessInterface">
com.wrox.expertj2ee.ticket.boxoffice.BoxOffice
</customDefinition>
<customDefinition property="jndiName">
436
Brought to you by ownSky
Infrastructure and Application Implementation
ejb/BoxOffice
</customDefinition>
</bean>

There is no need for any application objects to perform JNDI lookups or use the EJB API directly: they can
simply use the object created by this bean definition as an implementation of the
com.wrox.expertj2ee.ticket.boxoffice.BoxOf f ice business interface.
Using the infrastructure discussed in this chapter, application components that use these interfaces need
simply expose bean properties that can be set by the framework appropriately: they don't need to depend
on framework code and they don't need to look up objects managed by the framework. For example, the
application's main web-tier controller exposes calendar and boxOffice properties, which the
framework sets automatically based on the following bean definition:
<bean name=" ticketController"

class= "com.wrox.expertj2ee.ticket .web.TicketController">

// Other properties omitted

<property name="calendar" beanRef="true">calendar</property>
<property name="boxOf f ice" beanRef="true">boxOff ice</property>


</bean>

The controller doesn't need to do any configuration lookup, but merely has to implement the following
methods to save references to instance variables:
public void setBoxOff ice(BoxOff ice boxOffice)

{ this.boxOffice = boxOffice; }

public void setCalendar(Calendar calendar)
{ this.calendar = calendar; }

We will discuss the web tier in detail in the next chapter. As this example indicates, it is very closely
integrated with the overall infrastructure we've examined.
Summary
In this chapter we've discussed the importance and benefits of a strong generic infrastructure backing
application implementation. While a J2EE application server provides powerful standard infrastructure
forJ2EE applications, we need to use additional infrastructure components to make the J2EE APIs easier
to use, ensure that application code focuses on addressing business problems, and solve problems that the
J2EE specifications do not.
To illustrate these points, and to provide a potential basis for your own applications, we've examined some of
the key infrastructure packages used by the sample application, and the problems they exist to solve.

437
Brought to you by ownSky
We've considered the importance of infrastructure that enables central configuration management.
We've seen how making application components JavaBeans can allow them to be configured by a generic
application framework, removing the need for application code to implement configuration management
such as property file lookups. In combination with interface-based design - in which code is always
written to interfaces, rather than classes - the use of JavaBeans is an incredibly powerful means of

assembling and parameterizing applications without modifying Java code.
Once configuration management is centralized, it is possible to read configuration from any source - such as
XML documents, properties files, or JNDI - without any need to change application code. Configuration
via JavaBean properties ensures consistency across applications and between architectural tiers. For
example, EJBs can use the same approach to configuration management as ordinary Java classes.
We've looked at several layers of infrastructure that enable us to achieve central configuration management:
o A package that abstracts the use of reflection to manipulate JavaBeans. This is valuable basic
infrastructure that can be used not only in configuration management, but also for other tasks such as
initializing JavaBean objects from HTTP requests (as discussed in the next chapter), simplifying
request processing in web applications. This functionality is so useful that several open source
projects, such as Apache Commons, provide their own implementations; however, I am yet to see
a really good one, and the com. interface21 .beans package described in this chapter is the most
sophisticated I know of.
o A "bean factory" that uses this lower-level functionality to maintain a registry of application objects. When
application objects are defined as JavaBeans, they need not depend on this infrastructure; infrastructure
can work behind the scenes to set bean properties to configure objects, including setting relationships
between objects, based on definitions held outside Java code.

With the combination of built-in support for primitive properties, the ability to set relationships
between managed objects, and the availability of the standard JavaBeans PropertyEditor
functionality to represent object types as strings, concise bean factory syntax can define complex
application configurations. We looked at standard bean factory implementations based on XML
documents, properties files, and environment variables defined in EJB JAR deployment
descriptors.
o An "application context" that builds on the bean factory concept to implement the Observer design
pattern, provides a standard way to look up messages to meet internationalization requirements, and
allows application components to communicate as necessary at run time.
The standard J2EE infrastructure makes some simple things difficult, which has caused widespread
frustration withJ2EE in practice. This can be addressed by a simplifying infrastructure that makes J2E
APIs and services easier to work with. We've seen how infrastructure can be used to simplify working with

complex J2EE APIs, including:
o Support classes that make it easier to implement EJBs and reduce the likelihood of
common errors.
o Infrastructure classes that help to implement the Service Locator and Business Delegate J2I patterns,
decoupling code that uses EJBs from dependence on JNDI and EJB APIs. We saw that the Service
Locator pattern is usually simplest and most appropriate for accessing EJBs through local interfaces,
while the Business Delegate is the better choice to access EJBs through remote interfaces. In this
case the Business Delegate can be used to conceal the details of remote access from client code.
438
Brought to you by ownSky
Infrastructure and Application Implementation
o Classes that make it easier to send and consume JMS messages, concealing the necessary
JNDI lookup, simplifying error handling and the need to work with multiple JNDI API
objects.
Whether or not you choose to use the specific infrastructure described in this chapter, all these issues
should be addressed outside application code, which should concentrate on the problem domain and
implementing business logic.
Finally, we looked at some of the core implementation of the sample application, illustrating how it is
simplified through use of appropriate infrastructure.
















439
Brought to you by ownSky

Web-Tier MVC Design
In this chapter we'll look at web-tier design and how to ensure that a web interface is a simple and
maintainable layer built on business objects and overall application infrastructure.
This is a very important topic. A poorly designed web interface is one of the quickest paths to an
unmaintainable application and overall project failure. Experience with poorly designed web interfaces
that proved hugely expensive in development effort and business opportunity first made me passionate
about J2EE design.
After surveying the problems we seek to avoid in web application design, we'll focus on the MVC
architectural pattern, which has proven the key to writing successful web interfaces.
After examining MVC theory and the concepts shared between successful implementations of the MVC
pattern, we'll look at three open source MVC web application frameworks: Struts, Maverick, and
WebWork. We'll then look at the design and use of a simple but powerful web application framework
integrated with the infrastructure that I introduced in the last chapter, and which we'll use for the sample
application. Like the infrastructure components we've seen so far, this framework is intended for use in
real applications, and not merely as a demo.
I chose to implement this framework rather than use an existing framework for several reasons:
o To demonstrate the potential to integrate an MVC web application framework into an overall
application infrastructure, and the benefits that this brings.
o To formalize the relationship between web-tier components and business objects, which are
independent of the web interface. I feel that the relationship between web-tier components and
business objects is often neglected in MVC applications, with too much application code being
web-specific.
441

Brought to you by ownSky
o Because I have considerable experience in this area and have implemented previous
production frameworks, from which this is a natural evolution.
o Because no existing MVC web framework meets all the design goals we will discuss.
o Because the design of this framework clearly illustrates the MVC concepts central to
this chapter, and especially the complete decoupling of views from controllers. We'll
see the benefits of this when we look at alternative view technologies in the next
chapter.
Naturally, this framework builds not only on my experience, but also on the concepts used in existing
frameworks. It borrows some concepts from Struts and Maverick, in particular.
We'll also look at the issue of session state management and data validation in web applications.
Finally, we'll look at implementing the web tier of the sample application using the MVC framework
discussed in this chapter, paying special attention to how web components access business objects.
The Challenges of Web Development
Why is designing web interfaces so hard and why are the consequences of getting it wrong so dire? Some
of the many reasons include the following:
o Web interfaces change frequently
For example, re-branding may change the look and feel, but not necessarily the workflow, of
a web application. A successful application can accommodate such change without the
need to change business objects or even web-tier control code.
o Web interfaces involve complex markup
Typical web sites contain nested tables and lengthy JavaScripts; pages may even be
designed using GUI tools that produce hard-to-read markup. Usually only a small
proportion of the content is dynamic. It's vital that the generation of complex markup
doesn't obscure the generation of dynamic content. Getting this markup right is the
business of specialists; Java developers are seldom good at it and their skills are better
employed elsewhere. Thus separation of the roles of markup developer and Java developer
is crucial.
o Web interfaces use a very different model compared to traditional UIs in languages
such as Java

The web programming model is punctuated by requests and responses; it's impossible to
update GUI components dynamically when an underlying model changes, as in a
traditional MVC approach such as Java Swing.
o HTTP requests can carry only string parameters
Thus we frequently need to convert string values to and from Java objects. This can be
difficult and error-prone.
o Web interfaces make it difficult to validate user input, as we have limited control over
the client browser
We must be prepared to reject invalid user input and make users resubmit it. In contrast, we
can use custom controls to prevent users entering invalid data in Swing or VB interfaces, as
we have complete control over UI presentation.
442
Brought to you by ownSky
Web-Tier MVC Design
o HTML offers a limited choice of UI controls
Again, this is in sharp contrast to traditional GUI applications, which offer a rich range of controls
and the ability to implement custom controls.
o Ensuring that a web site looks right and works correctly in all common browsers can be
difficult
This can significantly complicate markup and add to the challenge of separating presentation issues
from Java programming issues.
o There are many efficiency considerations
Often we cannot restrict or predict the number of users using a web application. Both performance
and concurrency are likely to be particularly important in web applications.
o Web interfaces are relatively hard to test
This is one of many reasons that we should ensure that as little application functionality as possible is
web-specific.
Lessons Learned in Java Web Development
Designing web interfaces to achieve maintainable and extensible applications requires discipline. The best
motivation for achieving this discipline is awareness of the consequences of failing to achieve it. The

following brief discussion of the history of Java web development serves to illustrate the dangers that we
must avoid.
The Shortcomings of Servlet-only Solutions
The first Java web technology was servlets. The first release of the Servlet API back in 1997 was an
improvement on the dominant existing technology, the primitive Common Gateway Interface (CGI)
standard for server-side script invocation. Instead of scripts written in languages such as C or Perl, which
usually caused a new process to be created to handle each request, servlets allowed dynamic content to be
generated by reusable Java objects. Servlets had full access to the power of the Java language, including
JDBC and the Java object model, and so elevated web development to true OO development.
At least in theory - the Servlet specification was a big step forward, but developers soon ran into problems
building real applications. Servlets proved very good at invoking business objects. However, generating
complex markup from Java code is awkward. If markup strings are embedded in servlet and helper classes,
changes to the presentation of web content always require modification of Java code and recompilation;
Java developers must always be involved. Furthermore, it can be very difficult to see how a complete page
is assembled from markup littered throughout Java objects, making sites very difficult to maintain.
Servlets proved much more effective at generating binary content. However, this is a rarer requirement.
Generating markup from Java objects such as servlets is clumsy and leads to
unmaintainable web applications. The servlet specification alone is not enough to meet the
challenges of most web applications.


443
Brought to you by ownSky
It was clear that additional infrastructure was needed. Many developers used templating approaches, in which
markup was held outside Java code, with servlets responsible for generating only the dynamic content.
However, there was no widely adopted standard.
JSP: Promise and Temptation
Thus the release of the JSP 0.92 specification in 1998 created widespread excitement. JSP was a standard
templating solution, endorsed by Sun. Although JSP pages are transparently compiled into servlets by a web
container, JSP pages use a very different approach from servlets. While servlets are Java objects capable of

generating markup, JSP pages are markup components that permit escaping into Java as necessary to initiate
processing.
JSP was soon in wide use, and overenthusiastic adoption led to a host of new problems. Experience quickly
demonstrated that a model based on escaping into Java as necessary to generate dynamic content was a new
evil in place of the old need to generate markup from Java code. The release of JSP 1.0 in 1999 was a
significant improvement on JSP 0.92, but did not change the underlying model or address these problems.
"JSP Model 1" Architecture
To understand these problems, let's consider the simplest and most obvious architecture for a J2EE web
application's interface. Imagine that we use JSP pages to implement the entire web interface. Each JSP
handles incoming requests, creating domain objects from request parameters and using them to invoke
business objects. Each JSP then renders the result of the business processing. The JSP infrastructure makes it
easy to implement this approach, using JSP pages with a mix of markup and embedded Java code. The
standard JSP infrastructure allows JSP pages:
o To automatically populate JavaBeans declared on them with request parameter values, using the
<jsp:useBean> standard action
o To access and manipulate session attributes and application-wide ServletContext attributes
o To perform any kind of logic using scriptlets (blocks of Java code embedded in JSP pages)
This kind of web application architecture is often termed the JSP Model 1 architecture. Core J2EE Patterns
uses the more meaningful, but rather overblown, term, Physical Resource Mapping Strategy. I' use the older
term, JSP Model 1, in this chapter.
This approach is simple to implement. All we need to do is write the JSP pages and provide the support classes.
There is no need to write servlets or define URL-to-servlet mappings in the web.xml deployment descriptor.
Development-deployment cycles are often short, as JSP pages can be recompiled automatically by the web
container as they are modified.
444
Brought to you by ownSky
Web-Tier MVC Design
However, this approach mostly produces very poor results in practice. Applications built using this
roach (and far too many are, even today), face the following severe problems:
o Each JSP must either know how to handle all possible paths resulting from a request (for example,

the path on invalid or missing parameters), resulting in complex conditionals; or must forward to
another JSP as necessary, making application workflow hard to follow. It's a mistake to commit to
one JSP when it's too early to predict what content we'll need to display.
o Broken error handling. What if, halfway down the JSP, after the buffer has been flushed so it's too
late to forward to another page, we encounter an exception? Although the standard JSP error page
mechanism works well enough for trivial Model 1 systems, it won't help us in this plausible
scenario.
o The web interface is tied to JSP. In fact, JSP is just one view technology available to J2EE
applications. JSP pages are unsuited to presenting binary data, and other technologies such as XSLT
are superior at tackling some problems.
o Java code in JSP scriptlets is hard to test and cannot be reused. Not only is this approach not
object-oriented, it fails to deliver even the code reuse we would achieve in a well-written
procedural application.
o JSP pages will contain a mish-mash of markup and Java code, making them hard for either Java
developers or markup experts to maintain.
o JSP pages will bear little resemblance to the markup they generate. This defeats the point of using
JSP pages, which are essentially markup components.
The JSP Model 1 architecture, in which JSP pages handle incoming requests, results in
non-maintainable applications. JSP Model 1 should never be used in real applications: its
only use is in prototyping.
The Temptation of the JSP Standard Infrastructure
Hopefully you're already convinced that JSP pages are not the ultimate answer to J2EE web interfaces. In
case you aren't, let's consider some of the shortcomings of the JSP standard infrastructure, which makes
it easy to attempt many things that subvert good design, and makes the JSP Model 1 architecture plausible
on first impression.
Request parameters can be mapped onto JSP beans in two ways. Individual request parameters can be
mapped onto a bean property using a <jsp:setProperty> action like this:
<jsp:setProperty name="bean" property="propertyName" param="paramName" />

Alternatively, all request properties can be mapped onto a bean in a single <jsp:setProperty>

action like this:
<jsp:setProperty name="bean" property="*" />
445
Brought to you by ownSky

×