Applying Patterns in Context

You now know some of the common patterns that can be applied in J2EE applications. Using this knowledge, you can analyze the case study that has been followed throughout the book to see how they can be applied.

Analyzing the Case Study

Patterns are usually best understood in context. It helps to understand the intention of the design (and its associated code) and what it is trying to achieve so you can understand why a particular pattern helps in that situation. By examining the code for the case study, this section intends to

  • Identify some of the places where J2EE patterns have been applied in the case study

  • Examine some of those patterns in detail, including how the pattern looks in code

  • Consider what other patterns could have been applied in certain places and the changes that would be required to use those patterns

  • Understand why other J2EE patterns are not relevant to the design of the case study

The intention is to look at the central patterns that occur (or could occur) within the case study. This section will not contain an exhaustive list of all the patterns used.

When examining any design, it is important to understand the context in which design decisions were made. In this case, it is important to understand that the case study is, in places, intentionally simplistic. There are two reasons for this:

  • The case study is essentially a learning tool. Use of production-level code can sometimes obscure (or unnecessarily complicate) the underlying principles or steps required. Hence, various simplifications are made in places that result in sub-optimal design and code.

  • The case study follows the same disclosure sequence as the chapters of the book. Every effort is made to ensure that technologies are not used before they are introduced. Because of this, some parts of the design use alternative mechanisms that, again, can be sub-optimal.

The following sections examine different aspects of the case study and the J2EE patterns within it.

Session Façades and Entity EJBs

Probably the most obvious use of a J2EE pattern in the case study are the Session Façades that prevent the clients from accessing the Entity EJBs directly. This applies both for the standalone application clients and the servlets/JSPs. All of the session beans in the Agency (Advertise, AdvertiseJob, Agency, and Register) act in the role of Session Façades because they all manipulate data in Entity EJBs based on requests from the clients.

An example of this relationship is shown in Figure 18.2. This figure shows the class relationships between an AdvertiseClient, its associated AdvertiseBean (the Session Façade), and the CustomerBean that holds the back-end data. The implementation of the CustomerBean reflects the nature of this relationship because it only has a local home interface. This means that the CustomerBean is only intended for use by other EJBs such as the AdvertiseBean.

Figure 18.2. The AdvertiseBean acts as a Session Façade between the AdvertiseClient and the CustomerBean.


In terms of systemic qualities, the use of a Session Façade has the following impact:

  • Maintainability and flexibility— By decoupling the client from the details of the underlying data model, there is a large reduction in dependencies. Fewer dependencies between layers make the system easier to change for maintenance reasons or in the face of changing requirements.

  • Security— There is now one point of access for a particular piece of business logic and its associated data.

  • Performance— In some ways, a Session Façade will reduce the level of performance because extra code and method calls are introduced. However, designing the Session Façade interface to offer a coarse-grained, business-oriented interface in place of the underlying entity bean's fine-grained, data-oriented interface can frequently offset any reduction in performance because it reduces the number of remote method calls required to perform a task.

Before moving on, consider what you have just seen in terms of the general concept of patterns discussed at the start of this day's material. There are issues surrounding the use of Entity EJBs directly from remote clients. Most designers that have worked with EJBs for some time know of these issues. However, designers who are new to J2EE will not necessarily identify these issues. They may then produce poor quality systems until they learn about such issues by trial and error. By learning from such errors, the designer becomes better at designing J2EE applications. However, this is of little solace to the owner of the poor application on which the designer learned his or her trade.

The designers of the Agency application have used the Session Façade pattern where appropriate. Did they do this because they had learned from personal experience that direct Entity EJB access from distributed clients causes problems, or did they know this from studying J2EE patterns? To all intents and purposes it does not matter how they gained this J2EE design “wisdom,” what matters is that the delivered system is of higher quality than it would be without the application of this pattern. By learning and applying the patterns described today, you should be able to improve the quality of the J2EE applications on which you work.

Data Exchange and Value Objects

Within the Agency application, the Session bean data access methods return collections of strings that identify jobs and other domain concepts. The client then uses one of these strings to create another Session bean to retrieve the details associated with that job. An alternative would be to apply the Value Object pattern and use Value Objects to hold the information, such as job details, and to pass collections of these back and forth between the Agency Session bean and the clients.

The question of whether, how, and where to apply Value Objects revolves around the style of the application and the amount of data passed. When using an online catalog, such as those found in e-commerce applications, you will generally present the customer with a list of products. This list will be obtained as the result of some form of query (for example, all the books by Ian Fleming—or even all the products sold that relate to James Bond). The query results will be presented to the customer to make a choice. To make this choice, the customer needs more information about the product than just its name. Additional information could include its type (is it a video of GoldenEye, or a DVD, or a PlayStation game, or a book?), the price, availability, and so on. This means that for every query result, you would want to pass back multiple pieces of information. In this case, encapsulating them in a Value Object would make a lot of sense. You could then return a collection of such Value Objects to the Web-tier client, which would then display them to the customer.

The style of the Agency application is somewhat different from this. Most of the information presented to the user is “top-level” information, such as a list of customers or locations. Information is retrieved and updated as required. Therefore, the application works more on the principle of “browse and drill down,” meaning that the “next level” of information (such as the details for a particular customer) is only fetched when required after a selection has been made at the higher level. Passing back collections of Value Objects would be overkill in this situation because only one set of information (applicant or job) is required at any one time.

However, the Value Object pattern can be applied to several of the agency EJB interfaces. Take, as an example, the Advertise interface (shown in Listing 18.1) that is used to update or retrieve information on a particular job advertiser. Because the individual data items, such as the advertiser's name, must be retrieved individually through calls to the Session EJB, the application exhibits excessive chattiness. This means that there are lots of network connections, each one retrieving a small amount of data. This is in contrast to the updateDetails method that takes all of the advertiser's details in a single method call. You can apply the Value Object pattern to convert the repeated method calls into a single method call that returns a single JavaBean. This single JavaBean would contain all of the required information.

To show this pattern-based refactoring in practice, consider two forms of the Advertise interface. The original Advertise interface is shown in Listing 18.1 and has one method for each property defined on the advertiser. The interaction between the client and the Session EJB when loading the advertiser's details is shown in Figure 18.3.

Listing 18.1. Original Advertise Interface
public interface Advertise extends EJBObject
{
  void updateDetails (String name, String email, String[] Address)
                         throws RemoteException;
  String getLogin() throws RemoteException;
  String getName() throws RemoteException;
  String getEmail() throws RemoteException;
  String[] getAddress() throws RemoteException;
  String[] getJobs() throws RemoteException;

  void createJob (String ref)
      throws RemoteException, DuplicateException, CreateException;
  void deleteJob (String ref) throws RemoteException, NotFoundException;
}

Figure 18.3. Interaction between the original AdvertiseClient and AdvertiseBean.


Listing 18.2 shows a refactored interface containing a single query method for the advertiser's details that returns a Value Object of type AdvertiseValueObject. The interaction between the refactored client and Session EJB when loading details is shown in Figure 18.4. As you can see, there are now only two remote method calls made during the load rather than four, thus reducing the chattiness of this part of the application. This change will also reduce the time taken to load the details, because the time taken to make the remote calls will generally dwarf the time spent in local processing.

Figure 18.4. Interaction between the refactored AdvertiseClient and AdvertiseBean.


Note that you can now also use the Value Object as a parameter to updateDetails when updating the advertiser's details. Although this change to the update does not improve network performance, it does mean that any changes to the information held per-advertiser need only be made to the Value Object and the code that manipulates Value Objects rather than changing the Advertise interface.

Listing 18.2. A Refactored Advertise Interface
public interface Advertise extends EJBObject
{
  void updateDetails (AdvertiseValueObject details) throws RemoteException;
  AdvertiseValueObject getDetails() throws RemoteException;

  String[] getJobs() throws RemoteException;

  void createJob (String ref)
       throws RemoteException, DuplicateException, CreateException;
  void deleteJob (String ref) throws RemoteException, NotFoundException;
}

Listing 18.3 shows a possible implementation for the AdvertiseValueObject. This is a very simple implementation and it would be quite possible to improve it, such as by providing multiple constructors or allowing smarter addition of address information. However, the code shown is the minimum you would need. Note that the Value Object conforms to the rules for a JavaBean in that it has a no-argument constructor, it uses getter/setter naming, and it is declared as implementing Serializable.

Listing 18.3. AdvertiseValueObject
public class AdvertiseValueObject implements java.io.Serializable
{
 private String _login;
 private String _name;
 private String _email;
 private String[] _address;

 public AdvertiseValueObject() {}

 public String getLogin() { return _login; }
 public void setLogin(String login) { _login = login; }
 public String getName() { return _name; }
 public void setName(String name) { _name = name; }
 public String getEmail() { return _email; }
 public void setEmail(String email) { _email = email; }
 public String[] getAddress() { return _address; }
 public void setAddress(String[] address) { _address = address; }
}

Using the AdvertiseValueObject, the code in the client (AdvertiseClient.java) that loads the details would change to that shown in Listing 18.4. The code has changed very little, but the bulk of the data retrieval methods (apart from getDetails and the getJobs method that is used in the loadJobs method) are now local rather than remote.

Listing 18.4. Refactored loadDetails Method
...
public class AdvertiseClient ...
{
 ...
 private void loadDetails (Advertise advertise) throws RemoteException {
  AdvertiseValueObject details = advertise.getDetails();

  name.setText(details.getName());
  email.setText(details.getEmail());
  String[] address = details.getAddress();

  address1.setText(address[0]);
  address2.setText(address[1]);

  loadJobs(advertise);
 }
}

One thing to note is that the list of jobs associated with the advertiser is not included in the Value Object. The client manipulates the jobs separately from the rest of the advertiser details. Therefore, the updating and listing of jobs is kept separate from the manipulation of the advertiser's details.

In terms of systemic qualities, the use of a Value Object has the following impact:

  • Performance— There is now one remote method call to retrieve applicant data rather than five.

  • Scalability— Fewer calls across the network will reduce the amount of network bandwidth used.

  • Maintainability and flexibility— The reduction in dependencies and coupling brought about in the Register interface make subsequent changes to the applicant information easier to manage. Although changes to the contents of the Value Object will require changes in the client and server's internal data and the database schema, there is no need to change the remote interface.

There are also several variations on the Value Object pattern that you may find useful:

  • Partial Value Object— If only part of the data held by the server is required by the client, a Value Object can be used that encapsulates precisely the data required.

  • XML Value Object— You can extend the Value Object concept by passing an XML document instead of a Java object.

Data Access Without Entity EJBs

When you first started to look at the use of EJBs in the case study, on Day 4, “Introduction to Enterprise JavaBeans,” and Day 5, “Session EJBs,” Entity EJBs were not yet used to encapsulate the underlying data. Entity EJBs are a fundamental part of the J2EE architecture. They provide an extra level of flexibility by abstracting the underlying data source and, if using CMP, remove the need to write JDBC code. However, you may find that they do not bring any advantage for simpler, read-mostly applications. In this case, you may want to stick with direct database access from Session EJBs.

The Session EJBs from the Day 5 case study use direct database access for all queries and updates. Although direct database access is conceptually simpler than using an Entity EJB, it does mean that the data access code is intermingled with the business logic in the Session bean. This has a negative impact on

  • Maintainability— Changes to either the data access code or the business logic will require that you change the Session bean code.

  • Flexibility— Should the underlying data source change, such intermingled code is difficult to change.

To overcome these issues, you could apply the Data Access Object (DAO) pattern to the Session EJBs to separate the data access code from the business logic. Using a DAO to house the data access code means that the underlying data source becomes pluggable. The user of the DAO delegates all responsibility for the specifics of data access, such as mechanism, location, and error handling, to the DAO implementation. An example of this pluggability is shown in Figure 18.5.

Figure 18.5. A DAO acts as an adapter between the user and the specific data source.


To achieve this level of pluggability, the data and operations associated with the underlying data store must be abstracted. A J2EE DAO will use three mechanisms for this:

  • The DAO defines a Java interface that will be implemented by all forms of the DAO. This interface will provide creation, retrieval, update, and deletion (CRUD) methods in much the same way that an EJB home interface does.

  • One or more Value Objects represent the data to be used with the CRUD methods. Collections of Value Objects represent returned data. Because the Value Object is an abstraction of the underlying data, this helps to decouple the user of the data from the underlying data access mechanism, such as a JDBC ResultSet.

  • Optionally, a factory can be used to determine the type of DAO used. This allows the user of the DAO to leave the implementation selection until runtime.

So, how could you apply a DAO in the pre-entity agency? Well, the first thing would be to decide on the granularity of the DAO or DAOs to be used. You could potentially create one large DAO to represent all of the underlying data in the system, but this would be somewhat unwieldy and difficult to maintain. Probably, the cleanest design would be to create one DAO per table (per type of data) and then see how that worked. Should this cause performance problems, the DAOs could potentially be merged or optimized later.

To illustrate the application of a DAO, consider the two main data types used by the Advertise Session EJB—job and customer. Focusing on the job information, the first thing to do would be to define the interface and Value Object to be used by a potential job-related DAO. Listing 18.5 shows a DAO interface, JobDAO, that could be used for this purpose.

Listing 18.5. JobDAO, a DAO Interface for Use with Job Information
public interface JobDAO
{
 public Collection findByCustomer(String customer) throws Exception;
 public void deleteJob(String ref, String customer) throws Exception;
 public void createJob(String ref, String customer) throws Exception;
}

The methods to create and delete jobs are fairly self-explanatory. The findByCustomer method returns a Collection that will contain Value Objects representing all the jobs found that are associated with the given customer. An example of such a Value Object is shown in Listing 18.6. This time, the Value Object has several read-only properties that are set when it is initialized.

Listing 18.6. JobValueObject, a Value Object for Use with Job Information in a Job DAO
public class JobValueObject implements java.io.Serializable
{
 private String _ref;
 private String _customer;
 private String _description;
 private String _location;

 public JobValueObject(String ref, String customer,
            String description, String location) {
  _ref = ref;
  _customer = customer;
  setDescription(description);
  setLocation(location);
 }

 public String getRef() { return _ref; }
 public String getCustomer() { return _customer; }
 public void setDescription(String description) {
  description = description;
 }
 public String getDescription() { return _description; }
 public void setLocation(String location) { _location = location; }
 public String getLocation() { return _location; }
}

The DAO interface and Value Object can now be used by an implementation of the DAO DirectJobDAOImpl. The implementation uses the JDBC code previously embedded in the Entity-less AdvertiseBean. Such a DAO is shown in Listing 18.7.

Listing 18.7. DirectJobDAOImpl, a DAO Implementation That Uses JDBC Calls
public class DirectJobDAOImpl extends DirectDAOImpl implements JobDAO
{
 public DirectJobDAOImpl(String jndiName)
  throws SQLException, NamingException {
  super(jndiName);
 }

 public void createJob(String ref, String customer) throws Exception {
  PreparedStatement stmt = null;
  try {
   Connection connection = acquireConnection();
   stmt = connection.prepareStatement(
      "INSERT INTO Job (ref,customer) VALUES (?, ?)");

   stmt.setString(1, ref);
   stmt.setString(2, customer);

   stmt.executeUpdate();
  }
  catch (SQLException e) {
    error("Error creating Job "+ customer +":"+ref, e);
  }
  finally {
   releasePreparedStatement(stmt);
   releaseConnection();
  }
 }

 public void deleteJob (String ref, String customer) throws Exception {
  PreparedStatement stmt = null;
  Connection connection = null;
  try {
   connection = acquireConnection();
   connection.setAutoCommit(false);
   stmt = connection.prepareStatement(
      "DELETE FROM JobSkill WHERE job = ? AND customer = ?");

   stmt.setString(1, ref);
   stmt.setString(2, customer);
   stmt.executeUpdate();

   stmt = connection.prepareStatement(
      "DELETE FROM Job WHERE ref = ? AND customer = ?");

   stmt.setString(1, ref);
   stmt.setString(2, customer);
   stmt.executeUpdate();
   connection.commit();
  }
  catch (SQLException e) {
   try {
    if (connection != null) {
     connection.rollback();
    }
   }
   catch (SQLException ex)
    {}
   error("Error deleting job "+ref+" for "+customer, e);
  }
  finally {
   releasePreparedStatement(stmt);
   releaseConnection();
  }
 }

 public Collection findByCustomer(String customer) throws Exception {
  PreparedStatement stmt = null;
  ResultSet rs = null;
  Collection jobs = new TreeSet();
  try {
   Connection connection = acquireConnection();
   stmt = connection.prepareStatement(
      "SELECT ref FROM Job WHERE customer = ?");

   stmt.setString(1, customer);
   rs = stmt.executeQuery();

   jobs.clear();
   while (rs.next())
   {
    jobs.add(rs.getString(1));
   }
  }
  catch (SQLException e) {
   error("Error loading jobs for "+customer, e);
  }
  finally {
   releasePreparedStatement(stmt);
   releaseResultSet(rs);
   releaseConnection();
  }
  return jobs;
 }

 private void error (String msg, Exception ex) throws Exception {
   String s = "DirectJobDAOImpl: "+msg + "
" + ex;
   System.out.println(s);
   throw new Exception(s + ex);
 }
}

The DAO takes advantage of a programmer-defined superclass that manages the JDBC connection on its behalf. The subclass calls the methods acquireConnection and releaseConnection as required. All the rest of the JDBC manipulation is performed in the DirectJobDAOImpl class. The user of the class simply provides the JNDI string identifying the resource from which the data can be obtained. In this case, the resource will be the data source from which the Agency data can be obtained.

One thing to notice about the DAO implementation shown in Listing 18.7 is that it does not maintain a cache of jobs for the specific customer in the way that the Entity-less AdvertiseBean does. It would be quite possible to create a customer-specific form of the DAO that would cache this information to improve efficiency, if that was found to be beneficial.

Figure 18.6 shows the relationships between the different classes for this job DAO. The AdvertiseBean will instantiate a DirectJobDAOImpl and will call its methods to create, delete, and list jobs. This simplifies the code in the AdvertiseBean enormously and means that the code calls a single, meaningful method rather than containing many lines of JDBC calls and error handling.

Figure 18.6. Relationships between the classes that make up the job DAO.


The updated createJob method from the Entity-less AdvertiseBean is shown in Listing 18.8. You may notice that this code is now very similar to the code in the version of the AdvertiseBean that uses an Entity EJB. This is not surprising, because the DAO is essentially taking on the role of the Entity EJB in abstracting the data manipulation from the business logic.

Listing 18.8. createJob Method from the Entity-less AdvertiseBean Updated to Work with a Job DAO
...
public class AdvertiseBean ...
{
 ...
 public void createJob (String ref)
  throws DuplicateException, CreateException {
  try {
   jobDAO.createJob(ref, login);
  }
  catch(Exception ex) {
   error("Could not create job for " + ref, ex);
  }
 }
 ...

It is worth noting that in the Sun Java Center definition of Session Façade, the data objects protected from client access can be Entity EJBs, Session EJBs, or DAOs.

In terms of systemic qualities, the use of a Data Access Object has the following impact:

  • Maintainability and flexibility— By separating out the data access from the business logic, it is possible to upgrade or replace the data access without affecting the business logic and vice versa.

  • Performance— As when adding any extra layer, there will be a certain reduction in performance due to extra object instantiations and method calls. However, these could potentially be offset by optimizations and caching in the DAO layer.

Messages and Asynchronous Activation

On Day 10, “Message-Driven Beans,” message-driven beans were added to the Agency to match jobs with applicants and store the results of this matching process for later assessment. The reason for using asynchronous processing was due to the significant amount of time that this may take when the number of jobs or applicants has grown large.

The conversion of a synchronous EJB invocation into a message-driven form is defined by the Service Activator pattern.

In terms of systemic qualities, the use of a Service Activator has the following impact:

  • Performance— Because the searching for matching jobs and applicants takes place asynchronously, the level of performance perceived by the client is better (in other words, the server returns immediately rather than “hanging” while the search is performed).

  • Scalability— The client updates job or applicant information by calling a method on one of the relevant Session EJBs, such as AdvertiseJobBean. Because each Session EJB will take up server resources (sockets, memory, and so on), the client should use the EJB as quickly as possible. The use of message-driven searching reduces the time that the client uses the Session EJB and so aids scalability.

  • Availability— Should the server run out of message-driven beans in the pool (or should the server on which they run become unavailable), the messages can still be queued for delivery. If the service were accessed synchronously, a server outage would cause errors and stop the processing of client requests.

Composing an Entity

Although Entity EJBs are often used to represent rows in a database table, this mapping of table to Entity EJB is not necessarily optimal. If an Entity bean is directly based on an underlying database table, and then if the database table changes, so must the Entity EJB and all code that uses that Entity EJB. If there are many related tables, the application code may have to instantiate and use many Entity EJBs to access all of the data it needs. Each additional Entity EJB adds to the complexity of the application code. As more Entity EJBs are required, more RMI calls must be made to access their data, potentially increasing the traffic on the network and slowing down access to the data. Also, the application code may need to contact Entity beans from multiple data stores to retrieve all the data it requires.

To reduce the complexity for the application code and provide a more coherent view of the application's data, a single Entity can be used to represent data stored in multiple places (tables, databases, data stores). This single Entity bean can present a single, coarse-grained interface to the application code (usually a Session EJB).

In J2EE pattern terms, this is termed a Composite Entity. An example of this was seen in the Agency case study on Day 6, “Entity EJBs,” when the JobBean was a BMP Entity EJB managing data from both the Job and JobSkill tables. Consequently, the JobBean becomes a coarse-grained entity and any potential JobSkill Entity EJB is removed from the design. You performed the same design refactoring when you implemented the ApplicantBean to access both the Applicant and ApplicantSkill tables.

The JobBean and ApplicantBean BMP Entity EJBs both used JDBC directly to retrieve and update the required data. However, a good case can be made for combining the Composite Entity pattern with the DAO pattern so that the actual JDBC calls are handled in dependent DAO objects.

In terms of systemic qualities, the use of a Composite Entity has the following impact:

  • Maintainability and flexibility— By reducing the number of Entity EJBs, the application becomes easier to manage and maintain. Also, the use of one Entity EJB per-table effectively exposes the underlying database schema to the client.

  • Performance— Any use of a composite interface as compared to multiple interfaces should reduce the chattiness of the application. However, some of this gain can be offset by a longer load time for the composite. The composite will potentially have to load more data than it needs to serve a simple request. However, there are several strategies, such as lazy loading, that can help address this.

Composing a JSP

JSPs are easy to write and deploy. It is sometimes easy to forget that they are fully-featured and powerful Web components. Poor use of JSP functionality can have as much of an impact on the performance and scalability of your application as poor use of EJBs. J2EE Presentation Tier patterns, such as those listed in the “J2EE Presentation-Tier Patterns” section you saw earlier, can help you improve the quality of your Presentation Tier components and their interactions.

Consider some of the implications of using JSP functionality to deliver Web functionality. One common strategy when developing Web sites is to create one or more templates that define a standard layout for pages displayed to the user. As well as using common colors and styles, each page can display common information (the current location on the site) or functionality (a Home button) in a consistent place. Such templates help to improve the user interface of Web sites and make them easier to navigate. HTML frames have traditionally been used to divide the screen into separate areas to provide this templated look.

When developing JSP-based Web applications, the same principles of usability and navigability still apply. In the case of a JSP, the @include tag can be used to bring in standard functionality in specific parts of a page. The rest of the content in the page is determined by the JSP that is including the templated sections. This style of page composition using JSPs is captured in the J2EE Composite View pattern.

As an example of this, consider the agency.jsp from Day 13, “JavaServer Pages,” the first few lines of which are shown in Listing 18.9. This JSP uses the @include tag to bring in the code from header.jsf, as shown in Listing 18.10. This header fragment brings in a set of standard page elements, both functional, such as the error page definition, and visible, such as the style sheet definition. The most visible part of this included fragment is the heading (in the <H1></H1> element) showing the agency name that can be seen at the top of Figure 18.7.

Listing 18.9. Partial Listing of agency.jsp from Day 14
HTML>
 <%@include file="header.jsf" %>
 <BODY>
  <HR><H1>${agency.agencyName}</H1><P>
  <H2>Customers</H2>
  <H3>Existing Customer</H3>
  <FORM action='<c:url value="/customer/advertise" />' >
   <TABLE>
    <TR>
     <TD>Select Customer</TD>
...

Figure 18.7. The heading that shows the name of the Agency is part of a templated header included in agency.jsp.


Listing 18.10. header.jsf
<%@ taglib prefix="c" uri="/c.tld" %>
<%@page errorPage="/errorPage.jsp" %>
<jsp:useBean id="agency" class="web.AgencyBean" scope="request" />
<HEAD>
 <TITLE>J2EE in 21 Days Agency Web Application</TITLE>
 <LINK rel="stylesheet" type="text/css" href='<c:url value="/agency.css" />' >
</HEAD>

The header.jsf fragment is included in all of the JSP pages that are provided for the Agency's Web interface. All of the other pages (apart from agency.jsp) also include the footer.jsf fragment at the bottom of the page. The admin.jsp page, shown partially in Listing 18.11, is an example of this. The footer.jsf fragment, shown in Listing 18.12, provides a button to take the user back to the main Agency JSP. The resulting admin page is shown in Figure 18.8.

Listing 18.11. Partial Listing of Day 14 admin.jsp
<HTML>
 <%@include file="header.jsf" %>
 <BODY>
  <HR><H1>${agency.agencyName}</H1><P>
  <H2>Administration</H2>
  <H3>Modify Existing Location</H3>

...

  <%@include file="footer.jsf" %>
  </BODY>
</HTML>

Listing 18.12. footer.jsf
<HR>
<FORM>
  <INPUT type="button" value="Return to Agency Menu"
         onClick=location='<c:url value="/agency" />' >
</FORM>

Figure 18.8. admin.jsp includes both a header and a footer to provide consistent style and a standard button to return to the main screen.


In terms of systemic qualities, the use of a Composite View has the following impact:

  • Maintainability— Because the template used for the pages is modular, particular parts of the template can be re-used throughout the application.

  • Manageability— Use of the pattern has benefits in that cleanly separated page fragments make it easier to make changes. However, a plethora of fragments also presents a challenge to manage all of these pieces in a cohesive way.

  • Flexibility— The use of a view manager to conditionally include fragments of content makes the application interface very flexible.

  • Performance— The runtime generation of a page has a negative impact on performance, particularly when using a view manager. It may be best to have parts of the standard template pre-included when the JSPs are updated and refreshed in the Web application.

JSPs and Separation of Concerns

When designing an application, it is always important to correctly partition the components so that each component, or group of components, performs a specific task or tasks. This separation of concerns leads to fewer dependencies, cleaner code, and a more maintainable and flexible application. The separation of concerns becomes very important when those concerns relate to the skills of the people who must maintain and update parts of the application. In the case of JSPs, there are two distinct skill sets required—user interface design and writing J2EE-level Java code. Because it is rare to find these skills in the same person, these two aspects should be kept apart as much as possible.

Following this principle, the JSPs developed on Day 13 contain a certain amount of Java code to generate dynamic output, but not all of the code required is visible in the JSP pages. The JSPs use three JavaBeans, AgencyBean, JobBean, and CustomerBean, to perform the more involved processing required, such as the interaction with the business-tier EJBs. The J2EE pattern View Helper describes various ways in which encapsulated Java components can be used to hide a lot of the business processing required to create the desired output or view.

To see this in action, consider the AgencyBean, part of which is shown in Listing 18.13. As you can see, the constructor locates an Agency EJB and the rest of the code consists of methods that wrap calls to the EJB. If you look back to Listing 18.10, you can see that an AgencyBean declaration is included in header.jsf so that all pages will have an instance in scope. However, because the bean is scoped by request, the same bean can be shared if requests are forwarded to other JSPs or servlets within the application.

Listing 18.13. Selected Highlights of AgencyBean.java
...

public class AgencyBean
{
 Agency agency;

 public String getAgencyName() throws RemoteException {
  return agency.getAgencyName();
 }

 public Collection findAllApplicants() throws RemoteException {
  return agency.findAllApplicants();
 }

 public void createApplicant(String login, String name, String email)
  throws RemoteException, DuplicateException, CreateException {
  agency.createApplicant(login,name,email);
 }

 public void deleteApplicant (String login)
  throws RemoteException, NotFoundException {
  agency.deleteApplicant(login);
 }

 ...

 public AgencyBean ()
  throws NamingException, RemoteException, CreateException {
  InitialContext ic = null;
  ic = new InitialContext();
  AgencyHome agencyHome =
           (AgencyHome)ic.lookup("java:comp/env/ejb/Agency");
  agency = agencyHome.create();
 }
}

The use of the bean in the page makes it easy to build up JSP content based on the functionality exposed by the bean. The section of JSP code in Listing 18.14 is able to use the bean without the overhead of including the EJB-specific discovery code. This makes the page easier to maintain.

Listing 18.14. Simple Use of the AgencyBean in the Day 14 agency.jsp
<FORM action='<c:url value="/customer/advertise" />' >
 <TABLE>
  <TR>
   <TD>Select Customer</TD>
   <TD>
    <SELECT name="customer">
     <c:forEach var="customer" items="${agency.customers}" >
      <OPTION>${customer}
     </c:forEach>
    </SELECT>
   </TD>
  </TR>
  <TR><TD colspan="2"><input type="submit" value="Show Customer"></TD></TR>
 </TABLE>
</FORM>

The relationships and interaction between the client, the JSP, and the bean are depicted in Figures 18.9 and 18.10. Everything to the right of the AgencyBean JavaBean is transparent to the agency JSP.

Figure 18.9. Class relationships for the AgencyBean View Helper.


Figure 18.10. Sample interaction diagram for the AgencyBean View Helper.


As well as using JavaBeans, there are other strategies for delegating code to components outside the JSP. The most powerful strategy is probably the one using a custom tag library. The code fragment in Listing 18.15 shows how even more of the Java code is removed by using the custom agency:useCustomer tag that removes the JNDI name lookup of the Customer EJB from the Web page. This JSP code can now be easily manipulated and interpreted by a Web page designer and the tools used for Web page design.

Listing 18.15. A Tag Library Used as a View Helper to Remove Code from the Agency JSP
<HTML>
 <%@include file="header.jsf" %>
 <agency:useCustomer login="${param.customer}"/>
 <BODY>
  <HR><H1>${agency.agencyName}</H1><P>
  <H2>Customer details for: ${cust.login}</H2>

One other strategy for removing code from a JSP is to simply store the code in a JSP fragment and then include the fragment where appropriate. Although this can be effective in some cases, it lacks the encapsulation of the other mechanisms. It is important to note that the importing of these fragments relates more to the View Helper pattern than the Composite View pattern, as it may seem at first glance.

In terms of systemic qualities, the use of View Helpers has the following impact:

  • Maintainability— If the code and the HTML of the JSP are separated, it makes it far easier to maintain the two parts. There is much less chance of accidental changes than when code and HTML are mixed.

  • Flexibility— If the code behind the helpers changes, for example to use different EJBs or direct database access, there is no need for the JSP to be altered.

  • Performance— As with any layering technique, there is the potential for some degradation in performance.

Client-Side Proxies and Delegates

The JavaBeans in the View Helper discussion also act as a shield between the JSP and the business tier functionality. For example, the JSP has no need to know that it is using an EJB when it instantiates an AgencyBean JavaBean. The JavaBean encapsulates all of the EJB handling (the custom tag library also does this in later versions). In this role of client-side proxy, the JavaBean provides an implementation of another J2EE pattern, the Business Delegate.

The role of a Business Delegate is to reduce the coupling between the client tier or Web tier code and the business tier implementation. As indicated, encapsulating the implementation of the business logic (as an EJB) reduces coupling. This encapsulation includes not only the lookup of resources but also error handling, retries, and failover, if appropriate. The Business Delegate can also perform caching and mapping of EJB exceptions to application exceptions.

When Business Delegates and Session Façades are used together, there is commonly a 1:1 mapping between them. This occurs in the Day 13 Agency code where the AgencyBean, JobBean, and CustomerBean JavaBeans each act as a Business Delegate for the Agency, Job, and Customer Session Façade EJBs, respectively. The primary reason that the Business Delegate does not contain business logic is that such business logic should be factored back into an associated Session Façade.

In terms of systemic qualities, the use of Business Delegates has the following impact:

  • Maintainability— The Business Delegate encapsulates the business tier access code. Any changes required to this code can be made in one place.

  • Reliability and availability— Retry and failover strategies can be implemented by a Business Delegate to improve the reliability and availability of the access to business tier services.

  • Performance— As with any layering technique, there is the potential for some degradation in performance. However, a Business Delegate can also cache information if appropriate, having a positive impact on performance.

Locating Services

Because the case study is designed to be simple, it tends to obtain EJB home and remote references on a per-call basis. In a production system, this is one of the areas where caching would be of benefit. The Service Locator pattern describes a way to speed up the retrieval of EJB remote interfaces.

The Service Locator is a derivative of the GoF Singleton pattern. It acts as a central, common service on the presentation or Web tier from which EJB remote references can be obtained. The Service Locator will obtain and cache a reference to the home interfaces of the EJBs it serves. These cached home interfaces are then used to dispense EJB remote interfaces as required. The client code does not need to perform the home interface lookup every time and so becomes more efficient.

In terms of the case study, both the application clients and Web-tier components could use such a service to improve the way they obtain EJB remote references.

In terms of systemic qualities, the use of Service Locator has the following impact:

  • Performance— Caching the EJB home interfaces should reduce the time taken to obtain an EJB remote reference. Also, because there are fewer JNDI lookups, there will be less network traffic.

  • Reliability and availability— Retry and failover strategies can be implemented by a Service Locator to improve the reliability and availability of the access to business tier services.

  • Maintainability— Because all of the EJB lookup (or JMS connection lookup) takes place in one component, there is one place for updates.

Any Other Business

Other J2EE patterns could be applied to the case study if the requirements were different. For example, the case study uses standard J2EE security in a fairly straightforward way. However, if there were more complex security requirements, a Front Controller servlet could be used in the Web tier to enforce such security in a uniform manner without having to place security code in each JSP and servlet. If other functionality, such as logging and audit, were required, the Intercepting Filter pattern could be applied to chain together all of the required common “transparent” functionality (security, logging, and so on) ahead of any access to JSPs and servlets.

Because the data displayed by the Web-based interface is fairly standard, there is no requirement for pre-processing of information in a Front Controller. Nor does any form of routing take place such that the client is shown different pages depending on the context information they submit (for example, a cookie or form field). If such pre-processing and conditional display were to form part of the case study, the patterns Service to Worker and Dispatcher View could be considered when deciding where to place the different responsibilities.

The case study is not an e-commerce system, as many J2EE systems are. Such systems tend to deal with catalogs of products and spend most of their time displaying static information and less time updating information (for example, a customer will browse many pages before placing an order). For e-commerce systems, other patterns in the J2EE catalog, such as Value List Handler and Fast Lane Reader, become relevant.

The different patterns are relevant in different contexts. Some patterns will be irrelevant in some cases, so don't assume that all patterns will be applicable in your system.

Refactoring the Case Study

As you have seen, there are various ways in which the case study can be refactored to improve its systemic qualities. The reason that such refactoring can be performed is down to the context in which the application was written, namely that parts of it were simplified for educational purposes.

As noted earlier, most development work is performed on existing code, so identifying a potential application of a pattern in such code and applying the appropriate refactoring is a useful skill to build. The reason that you would want to apply the refactoring shown is to make the case study a more efficient, maintainable, and scalable application. But why are you doing that? Well, probably because you want to apply that application in the real world to serve real customers. In that case, the context for the application has changed from being an educational J2EE showcase to being a live production application. The change in context alters the requirements and the forces on which the application is based. Such changes will almost inevitably show areas in which changes are required.

As any application is altered, whether that is the addition of new functionality or a change to the systemic quality requirements, certain original design decisions may no longer make as much sense as they did in the original design context. As a result, refactoring of applications and the use of different patterns in different areas over time should become a way of life for software designers and developers. Without suitable maintenance, software will erode over time as the world changes around it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.44.94