ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Building Java Enterprise Applications, Vol I: Architecture

Business Logic, Part 1

Related Reading

Building Java Enterprise Applications
By Brett McLaughlin

by Brett McLaughlin

This excerpt is Chapter 8 from Building Java Enterprise Applications, Vol I: Architecture, published in March 2002 by O'Reilly.

You have now completed the data layer of your application, and are ready to dive into the business layer. If you recall from Chapter 2, the business layer incorporates your application's business logic. Specifically, you will need to provide access to your entity beans, business calculations, and a scheduling facility. In this chapter, I'll detail the access to entity beans already in place, and discuss how to handle more complex business tasks. Chapter 9 then details the scheduling process.

First, I'll discuss the façade pattern, in which you use session beans to access entity beans. This access method is used instead of allowing direct access to entity beans, and is key to a sound strategy in building enterprise applications. I'll also outline the problems and penalties associated with this approach, giving you the information you need to make good decisions in your own applications. This pattern goes hand in hand with the manager component discussed in Chapter 6 when working with directory servers. I'll illustrate the pattern with a simple example, an OfficeManager session bean.

From there, I'll move on to slightly more complex session beans. You'll see how a single session bean can perform operations on multiple beans and on other Java components. You'll build a UserManager component, which will administrate users, and will operate upon the User entity bean as well as the LDAPManager directory server component. This should give you an idea of how to handle these more complex tasks.

Finally, I'll spend some time detailing the difference between stateless and stateful beans, and demonstrate how stateful beans can generally be converted into simpler, more efficient stateless session beans. You'll also see how helper classes can make stateless beans appear as stateful ones, allowing your clients to get simple interfaces while your beans remain fast and lightweight. I'll explore these concepts in developing an AccountManager component for working with user accounts.


Before Going On

At this point, you need to make sure you have some components in place. In addition to the code covered in the first seven chapters, you also need to make sure that the other Forethought entity beans are in place and available for access. These beans are detailed in Appendix E. These should be deployed in your EJB container, as many will be referred to in this chapter. You can either type these beans in yourself, or download the source code from the book's web site, http://www.newInstance.com.


The Façade Pattern

Also in this series:

Business Logic, Part 3
In Part 3 of our excerpt from Building Java Enterprise Applications (Vol. 1, Architecture), Brett McLaughlin addresses issues of statelessness and statefulness.

Business Logic, Part 2
In Part 2 of our excerpt from Chapter 8 of Building Java Enterprise Applications, Vol I: Architecture, Brett McLaughlin builds a UserManager component, and illustrates why managers are a good thing.

I've already mentioned the façade pattern in several earlier chapters, but never truly delved into the pattern's details. It's appropriate to do that now, and see why an extra layer of abstraction is necessary. In practice, most developers instinctively know that they should use a layer of session beans that prevent direct entity bean access, but then convince themselves to abandon this approach because they cannot justify it. I'll try and provide you some justification for that decision here.

Data Schema Exposure

The most obvious rationale for using session beans to abstract entity beans is that the approach also abstracts the structure of your data stores. To understand this better, you may want to take a second look at the actual structure of the Forethought database schema, and the SQL used to create it. Figure 8-1 shows the Forethought OFFICES table to serve as an example.

Diagram.
Figure 1. The Forethought OFFICES table.

The presumption is that you do not want to expose the inner workings of your application's data store, or even the specifics of how that data is stored. In other words, letting users (also known as potential hackers) know your database schema is a bad idea. Problems in this area arise when allowing direct access to the entity bean layer. The methods in entity beans typically map directly to underlying fields in the data schema, as shown in Figure 8-2.

Diagram.
Figure 8-2. Mapping the Office entity bean to the OFFICES table.

As you can see, for each column in the OFFICES table, a corresponding method exists in the Office entity bean. The same, of course, occurs for the rest of the database schema.

It is trivial to examine entity beans and then extrapolate the data schema from them, which is precisely the situation you are trying to avoid in application design. This becomes a simple problem to overcome with the introduction of session beans that abstract these details. For example, consider an OfficeManager bean that provides methods to add, update, and delete Forethought offices. The remote interface for this bean is shown in Example 8-1.

Example 8-1: The OfficeManager Remote Interface

package com.forethought.ejb.office;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface OfficeManager extends EJBObject {

    public OfficeInfo get(String city, String state) throws RemoteException;

    public OfficeInfo add(String city, String state) throws RemoteException;

    public void update(OfficeInfo officeInfo) throws RemoteException;

    public boolean delete(String city, String state) throws RemoteException;
    public boolean delete(OfficeInfo officeInfo) throws RemoteException;
}

This manages to hide some of the details of database schema implementation. While it might seem obvious to you that the OFFICES table contains a CITY and STATE text column, you are seeing through the eyes of someone who already knows the database schema. Figure 8-3 shows how this exact session bean might map to several different database implementations; therefore, it does hide the database schema by providing logical methods on entities, instead of physical methods.

Diagram.
Figure 8-3. Mapping the OfficeManager to different database schemas.

"Obfuscation" means "to make so confused or opaque as to be difficult to perceive or understand." It's often used to describe the process of scrambling bytecode so that it cannot be decompiled, and is used here to represent the same concept with respect to the database schema in use.

You can see that it's no longer obvious exactly how the database is laid out. Using session beans and the façade design pattern will aid in security by providing this layer of obfuscation over the data schema.

Finally, for those of you still unsure why this is worth going on about, let me explain why this obfuscation is so critical. Many of you are probably wondering why it is important to abstract your database schema from your presentation layer; wouldn't the developers and designers of one layer work with, or even be the same people as, the developers and designers of the other? That would seem to be the case, at least in many situations. In fact, you will code the Forethought application from front to back, so it might seem silly to go to this trouble.

However, as the era of service-based computing takes off, this process becomes vital. Instead of providing complete applications, the J2EE specification (as well as Microsoft's .NET platform, UDDI, SOAP, and other developments) indicates that organizations are focusing more on components than on complete applications. Interchanging data components from one application and company with presentation components from another application and company is becoming common and even standard. As a result, it is unsafe to assume that only you or your company's developers will be accessing your business layer and EJBs. You should assume that your EJB layer will be exposed to many others, some of whom you want to provide access but not application information to. For all of these reasons, a sound design of the business layer can save you some trouble, even make you a hero, when your pointy-haired boss insists that now the beans you worked on must be accessible by a new partner, but that the partner doesn't get database schema information. Suddenly, the work done on your session beans really begins to pay off!

I'll run briefly through the rest of the OfficeManager classes, as the actual implementation is fairly trivial. Example 8-2 is the home interface for the bean.

Example 8-2: The OfficeManager Home Interface

package com.forethought.ejb.office;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface OfficeManagerHome extends EJBHome {

    public OfficeManager create( ) throws
CreateException, RemoteException;
}

As you can see, this is a stateless session bean, which is the most efficient session bean. I'll discuss this more later. You can see from the implementation class in Example 8-3 that no state is required for the bean to function, and therefore using a stateless bean makes sense in this case.

Example 8-3: The OfficeManager Implementation Class

package com.forethought.ejb.office;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import javax.naming.Context;
import javax.naming.InitialContext;

import com.forethought.ejb.util.SessionAdapter;

public class OfficeManagerBean extends SessionAdapter {

    public void ejbCreate( ) throws CreateException {
        // No action required for stateless session beans
    }

    public OfficeInfo get(String city, String state) throws RemoteException {
        Office office = getOffice(city, state);
        if (office != null) {
            return office.getInfo( );
        } else {
            return null;
        }
    }

    public OfficeInfo add(String city, String state) {
        try {
            // Get an InitialContext
            Context context = new InitialContext( );

            // Look up the Office bean
            OfficeHome officeHome = (OfficeHome)
                context.lookup("java:comp/env/ejb/OfficeHome");
            Office office = officeHome.create(city, state);
            
            return office.getInfo( );
        } catch (Exception e) {
            // Any problems - just return null
            return null;
        }
    }

    public void update(OfficeInfo officeInfo) throws RemoteException {
        Office office = getOffice(officeInfo.getId( ));
        office.setInfo(officeInfo);
    }

    public boolean delete(String city, String state) {
        Office office = getOffice(city, state);
        return delete(office);
    }

    public boolean delete(OfficeInfo officeInfo) {
        Office office = getOffice(officeInfo.getId( ));
        return delete(office);
    }

    private Office getOffice(int id) {
        try {
            // Get an InitialContext
            Context context = new InitialContext( );

            // Look up the Office bean
            OfficeHome officeHome = (OfficeHome)
                context.lookup("java:comp/env/ejb/OfficeHome");
            Office office = officeHome.findByPrimaryKey(new Integer(id));
            
            return office;
        } catch (Exception e) {
            // Any problems - just return null
            return null;
        }
    }

    private boolean delete(Office office) {
        if (office == null) {
            return true;
        }
        
        try {
            office.remove( );
            return true;
        } catch (Exception e) {
            // any problems - return false
            return false;
        }
    }

    private Office getOffice(String city, String state) {
        try {
            // Get an InitialContext
            Context context = new InitialContext( );

            // Look up the Office bean
            OfficeHome officeHome = (OfficeHome)
                context.lookup("java:comp/env/ejb/OfficeHome");
            Office office = officeHome.findByLocation(city, state);
            
            return office;
        } catch (Exception e) {
            // Any problems - just return null
            return null;
        }
    }

You'll notice that this bean uses a new finder method on the Office entity bean, findByLocation( ). You can add this method to your OfficeHome class:

public Office findByLocation(String city, String state)
    throws FinderException, RemoteException;

Here's the relevant addition for the ejb-jar.xml file:

<query>
  <query-method>
    <method-name>findByLocation</method-name>
    <method-params>
      <method-param>java.lang.String</method-param>
      <method-param>java.lang.String</method-param>
    </method-params>
  </query-method>
  <ejb-ql>
    <![CDATA[WHERE city = ?1 AND state = ?2]]>
  </ejb-ql>
</query>

Before leaving the façade pattern behind, there are a few other details related to this first business-related bean worth detailing. First, notice that the manager beans are placed within the same package as the related entity bean. This provides logical groupings of managers and the related entities, and also makes access from manager component to entity bean simple (notice that there weren't a lot of import statements required in the source code).

Additionally, the method names have been changed a bit; instead of create( ), setCity( ), and getState( ), the more conventional method names add( ), update( ), and delete( ) are used. This is more in line with an administrative component, and moves away from the strict conventions required in CMP entity beans. It also provides an easier-to-use interface for client code.

Performance Penalties

In the interests of full disclosure, you should certainly be aware that the façade pattern, with all of its positives, does have some negatives. The significant problem with using this pattern is that it can introduce some performance penalties into your applications. Using an extra bean for communication (the session manager component) means that additional RMI calls must be made. This, of course, causes increases in network traffic, serialization of arguments, and all of the costs that any RMI call has. If both entity beans and session beans reside in the same EJB container on a single server, these costs shrink to almost nothing (in fact, most advanced EJB containers have optimizations for these "in-VM" calls, and will essentially drop the calls off the RMI stack and make the calls locally, removing any RMI penalties at all); however, this is often not the case It's more common to have session beans in one EJB container and entity beans in another, often on completely different physical machines.

However, even this problem can be overcome. Instead of packaging all entity beans and deploying them on one server, and packaging all session beans and deploying them on another, you can use more logical (and sensible) groupings to improve performance. Remember that you packaged a session bean, the SequenceBean, with the Forethought entities already. In this same fashion, manager components that implement the façade design pattern can be packaged with entity beans, ensuring that RMI communication is as fast as possible; this also provides logical divisions between entities and their accessor classes (the manager components) and business-driven components (the rest of the session beans). Figure 8-4 shows this configuration in action.

Diagram.
Figure 8-4. Logical separation of beans.

Here, the manager components are packaged in the forethoughtEntities.jar archive, and in that way, become simple entities.

Additionally, almost all manager components turn out to be stateless; in other words, each method of the component operates on its own without any saved information. This also helps to offset penalties incurred through using the façade pattern. As mentioned several times, stateless session beans outperform all other types of entity beans substantially. Interestingly enough, entity beans consume the most resources of any bean, as often one single instance is shared for containers (although there are as many variations on this theme as there are container vendors). So it is safe to make your manager session beans stateless.

These changes address the major downside of using the façade pattern; there are really no other penalties (other than some extra coding) to this approach. Clearly it makes sense, then, to implement it in the Forethought application as well as in your own.


View catalog information for Building Java Enterprise Applications Vol I: Architecture

Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.