Home > Articles > Programming > Java

This chapter is from the book

Building the Architectural Prototype: Part 3

Part 3 of building the architectural prototype for our EJB solution covers the addition of CMP code to the skeleton code generated out of Rose. It also covers the changes necessary to the JSPs, servlet, and use-case control classes.

Adding Logic to the Generated Code

It's time to make our initial code generation effort bear some fruit. By itself it won't buy us much. We will start out easy and take the simple pathway of asking for all customers, their addresses, and the role each address plays. Let's begin by reviewing some of the key components of the CustomerBean class. Remember that bean classes implement all the business logic of our application, but they are even more instrumental in the part of the application that implements container-managed persistence (CMP). It is CMP that eliminates the need to write SQL code, and CMP is a major productivity boost to anyone building EJB applications today.

The following accessor methods are the cmp fields for the Customer bean:

abstract public Integer getCustomerId();
abstract public void setCustomerId(Integer val);

/**
* @cmp-field customerNumber
* @business-method
*/
abstract public String getCustomerNumber();
abstract public void setCustomerNumber(String val);

/**
* @cmp-field firstName
* @business-method
*/
abstract public String getFirstName();
abstract public void setFirstName(String val);

/**
* @cmp-field middleInitial
* @business-method
*/
abstract public String getMiddleInitial();
abstract public void setMiddleInitial(String val);

/**
* @cmp-field prefix
* @business-method
*/
abstract public String getPrefix();
abstract public void setPrefix(String val);

/**
* @cmp-field suffix
* @business-method
*/
abstract public String getSuffix();
abstract public void setSuffix(String val);

/**
* @cmp-field lastName
* @business-method
*/
abstract public String getLastName();
abstract public void setLastName(String val);

/**
* @cmp-field phone1
* @business-method
*/
abstract public String getPhone1();
abstract public void setPhone1(String val);

/**
* @cmp-field phone2
* @business-method
*/
abstract public String getPhone2();
abstract public void setPhone2(String val);

/**
* @cmp-field eMail
* @business-method
*/
abstract public String getEMail();
abstract public void setEMail(String val);

These fields represent the attributes that will be persisted to and retrieved from the database. The modeling tool (e.g., Rational's Rose, TogetherSoft's Together Control Center) generates these accessors for you on the basis of the input provided to the modeling tool. In EJB 2.0, unlike prior versions, the accessors are declared abstract because the persistence engine implemented by the container vendor's product (i.e., BEA's WebLogic, or IBM's WebSphere) is what actually implements the accessor. The selling point that beans can be transported between different container vendor products is now finally moving closer to reality. You may have noticed comments with some tag indicators between the methods; I will discuss those comments at the end of this code section.

The next important fields to look at in the CustomerBean class are the container-managed relationship, or cmr, fields. These are instrumental in implementing the relationships between beans. This information comes from the relationships that are drawn in the modeling tool between the beans:

s /**
   * Syntax:
   * cmr source ejb-name multiplicity
   * =======================================================
   * @cmr-field CustomerBean one
   * @business-method
   */
  abstract public Collection getRoles();
  abstract public void setRoles(Collection Role);
  /**
   * Syntax:
   * cmr source ejb-name multiplicity
   * =======================================================
   * @cmr-field CustomerBean one
   * @business-method
   */
  abstract public Collection getOrders();
  abstract public void setOrders(Collection Role);

Remember that there were two one-to-many relationships in which Customer was involved: one with Order and one with Role (via the association relationship between Customer and Address). Again, just as with the attribute accessors, the cmr accessors are declared abstract because it is up to the persistence engine implemented by the container to handle how relationships are physically traversed. Notice that these accessors return Collection objects because a Customer object can have many Role objects and many Order objects.

Let's look at the Role class next. I won't review Address here because it looks very similar to Customer, except it doesn't have a relationship with Order. Role is interesting because it contains relationships to both a single Customer and a single Address. Remember that the Role class defines the involvement between a Customer object and an Address object. Role is easy from an attribute standpoint, having only its primary key and roleName:

   /**
   * @cmp-field roleId
   * @primkey-field
   * @business-method
   */
  abstract public Integer getRoleId();
  abstract public void setRoleId(Integer val);
   /**
   * @cmp-field roleName
   * @business-method
   */
  abstract public String getRoleName();
  abstract public void setRoleName(String val);

The two cmr accessors look like this:

   /**
   * Syntax:
   * cmr source ejb-name multiplicity
   * =======================================================
   * @cmr-field RoleBean many
   * @business-method
   */
   abstract public Customer getCustomer();
   abstract public void setCustomer(Customer Customer);
   /**
   * Syntax:
   * cmr source ejb-name multiplicity
   * =======================================================
   * @cmr-field RoleBean many
   * @business-method
   */
   abstract public Address getAddress();
   abstract public void setAddress(Address Address);

Notice that unlike the cmr fields for Customer, the cmr fields for Role return actual objects of the Address and Customer classes.

There is quite a bit of power in what we have constructed so far. When we build a client to gain access to the services of our beans, you will see code that somewhat resembles this:

   role.getAddress().getPrimaryKey() 

Although these look like just methods being invoked, they are handled by the persistence implementation in the container and will generate SQL access code to retrieve the desired objects (unless they are already cached from previous access). In the case illustrated here, the Address object is retrieved and then the primary key of Address is returned.

A Bit of Magic in Those Tags

As I mentioned earlier, you may have noticed some funny-looking Javadoc-like tags in the comments above each accessor. These tags are not put there by the modeling tool but are another alternative to help you with generating code. Most of the visual modeling vendors do a decent job of generating deployment descriptors, but depending on your container vendor, you have other options, particularly if you are using BEA's WebLogic product. Don't worry, though; if you included these tags and you use another product, such as IBM's WebSphere, it will still enhance your productivity immensely.

If you are not using a modeling tool (which is a big mistake in my opinion), then a gentleman named Cedric Beust is potentially a hero to you. Beust developed a marvelous "free" tool called EJBGen, which is a doclet. It uses the little-known ability of the javadoc utility to override the standard document engine with your own parser. EJBGen takes in your bean classes and generates not only the corresponding home, remote, and primary-key classes but also the deployment descriptors. This is where the tags (e.g., @cmp-field, @business-method) come into play.

Actually you can use this tool regardless of your container engine product, but you will have to play a bit with the vendor-specific deployment descriptors if you are using something other than WebLogic. Remember that there is one common descriptor that all vendors must implement: ejb-jar.xml. All the XML descriptors go into the META-INF directory in your implementation. The descriptors are the real workhorse in the EJB implementation and they are standard across all vendors. Other descriptor tags, depending on your particular vendor, will also be required. I won't spend more time on EJBGen, other than to refer you to Beust's Web site, at http://beust.com/cedric/ ejbgen, for more information.

Compiling the EJB Code

Once the code has been written and the deployment descriptors created, a few steps are required. Note that step 3, running the EJB compiler, will vary slightly with each vendor:

  1. All the Java components must be compiled, including the remote, home, and bean classes for each entity bean:

    c:\>javac –d %classdirectory% Customer.java
       CustomerHome.java CustomerBean.java Role.java
       RoleHome.java RoleBean.java
    Address.java AddressHome.java AddressBean.java
  2. An EJB JAR file must be created that contains the result of the previous compile and the XML deployment descriptors that reside in the META-INF directory:

    jar cv0f remulak.jar %metadirectory% %classdirectory%
  3. The EJB compiler must be run against the JAR file created in step 2: c:\>java –classpath %WL_HOME%/lib/weblogic_sp.jar; %WL_HOME% /lib/weblogic.jar weblogic.ejbc -compiler javac Remulak.jar ejb20_Remulak.jar

    c:\>java –classpath %WL_HOME%/lib/weblogic_sp.jar;
       %WL_HOME% /lib/weblogic.jar weblogic.ejbc
       -compiler javac Remulak.jar
    ejb20_Remulak.jar

The ejb20_Remulak.jar file can now be deployed and tested by the client. In WebLogic we do this by placing the JAR file in the applications directory of the deployment target.

Building a Simple Client to Test the Beans

Later in this chapter we will make our client much more appealing and flexible by enhancing the beans and JSPs created in Chapter 11. In this chapter, however, we will build a very rudimentary client that we will invoke from the command line to test the Customer, Role, and Address beans.

For a client to access a bean managed by an EJB container, it must first obtain a reference to the bean via the bean's home interface. The Java Naming and Directory Interface (JNDI) accomplishes this task for the client. Our client, which we will call ClientRemulak, will implement the initial bean-testing logic. To begin the process, we must first obtain a context to the JNDI services as implemented by our container product. This logic will be unique to the individual container product; however, all container products operate in the same fashion (with a few exceptions). The following logic obtains a JNDI context from the BEA WebLogic server:

/**
   * Get an initial context into the JNDI tree
   *
  */
  private Context getInitialContext() throws NamingException {
     try {
      // Get an initial context
      Properties h = new Properties();
      h.put(Context.INITIAL_CONTEXT_FACTORY,
         "weblogic.jndi.WLInitialContextFactory");
      h.put(Context.PROVIDER_URL, url);
      return new InitialContext(h);
     } catch (NamingException ne) {
      throw ne;
     }
}

After obtaining the context, we can obtain the address of a home interface. In our case we need access to the CustomerHome interface, as shown here:

package com.jacksonreed;

import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import javax.ejb.FinderException;
import java.rmi.RemoteException;
import java.util.Collection;

public interface CustomerHome extends EJBHome {

   public Customer findByPrimaryKey(Integer primaryKey)
     throws FinderException, RemoteException;

   public Collection findAllCustomers()
     throws FinderException, RemoteException;

   public Customer findByCustomerNumber(String customerNumber)
     throws FinderException, RemoteException;

   public Customer create(Integer CustomerId, String
   CustomerNumber,
       String FirstName, String MiddleInitial, String Prefix,
       String Suffix, String LastName, String Phone1, String
       Phone2,
       String EMail)
     throws CreateException, RemoteException;
}

We specifically want to access the findAllCustomers() method in the CustomerHome interface. The logic to get the reference to Customer Home uses the context retrieved earlier. Notice that the first method called in lookupCustomerHome() is getInitialContext():

/**
 * Look up the customer bean's home interface using JNDI
 */
private CustomerHome lookupCustomerHome()
  throws NamingException
{
  Context ctx = getInitialContext();

  try {
    Object home = (CustomerHome) ctx.lookup("remulak.
    CustomerHome");
    return (CustomerHome) PortableRemoteObject.narrow(home,
         CustomerHome.class);

  } catch (NamingException ne) {
    throw ne;
  }
}

As reviewed in Chapter 9, the key ingredient to the Enterprise Java-Bean framework and the flexibility gained at deployment time consists of the deployment descriptors. The deployment descriptor allows, among other things, the ability to express in a query-neutral fashion, the logic executed by finder methods, such as findAllCustomers(). This neutral query logic is called EJB-QL and is an EJB 2.0 standard. EJB-QL statements are parsed and turned into SQL queries by the persistence manager implemented by the EJB container. What follows is a snippet from the ejb-jar.xml file produced by either the visual modeling tool vendor (Rational, TogetherSoft) or as a result of a utility like EJBGen:

<query>
   <query-method>
     <method-name>findAllCustomers</method-name>
     <method-params/>
   </query-method>
   <ejb-ql><![CDATA[ WHERE customerId IS NOT NULL]]>
   </ejb-ql>
</query>
<query>
   <query-method>
     <method-name>findByCustomerNumber</method-name>
     <method-params>
        <method-param>java.lang.String</method-param>
     </method-params>
   </query-method>
   <ejb-ql><![CDATA[ WHERE customerNumber = ?1]]>
   </ejb-ql>
</query>

The findAllCustomers() method specified by the <method-name> tag maps directly to the method implemented in the CustomerBean class. The information between the <ejb-ql> tags represents the generic query that the container converts to SQL at runtime. There is also a findByCustomerNumber() method that provides a similar implementation but returns only a Customer matching a given customer number. The following is the CustomerHome interface, which reflects the create and find methods:

package com.jacksonreed;

import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import javax.ejb.FinderException;
import java.rmi.RemoteException;
import java.util.Collection;

public interface CustomerHome extends EJBHome {
   public Customer findByPrimaryKey(Integer primaryKey)
     throws FinderException, RemoteException;

   public Collection findAllCustomers()
     throws FinderException, RemoteException;

   public Customer findByCustomerNumber(String customerNumber)
     throws FinderException, RemoteException;

   public Customer findByCustomerId(Integer customerId)
     throws FinderException, RemoteException;

   public Customer create(com.jacksonreed.CustomerValue custVal)
     throws CreateException, RemoteException;
}

Remember that the client engages with both the home and the remote interfaces, and they, in turn, work with the bean instance.

Let's now turn our attention back to our client, ClientRemulak. The findAllCustomers() method will select rows from the database and, in turn, instantiate objects in our code. The customerHome variable was returned from the lookupCustomerHome() method shown earlier. After retrieving the collection of Customer objects with the findAllCustomers() method, we use an Iterator object to cycle through the collection and retrieve the related Role objects and associated Address objects:

private void findAllCustomers()
   throws RemoteException, FinderException {

   Integer addrKey;

   log("Querying for all customers\n");
   Collection custCol =
customerHome.findAllCustomers();

   Iterator custIter = custCol.iterator();
   if(! custIter.hasNext()) {
     log("No customers were found with a null customerId");
     return;
   }

   while (custIter.hasNext()) {
     Customer cust = (Customer) custIter.next();
     log("customer id is " + cust.getPrimaryKey() +
       " customer number is " + cust.getCustomerNumber() +
         " last name is " + cust.getLastName() );

     Collection roleCol = cust.getAllRoles();

     Iterator roleIter = roleCol.iterator();
     if(! roleIter.hasNext()) {
      log(" No roles for this customer");
     }
     else {
      while (roleIter.hasNext()) {
         Role role = (Role) roleIter.next();
         log(" role id is " + role.getPrimaryKey() +
           " role name is " + role.getRoleName() );
         log(" address id is " + role.getAddress().
         getPrimaryKey() +
           " address line 1 is " + role.getAddress().
            getAddressLine1() +
           "\n" );
         }
      }
   }
}

It's time to run ClientRemulak from the DOS command prompt:

C:\> java com.jacksonreed.ClientRemulak

Beginning com.jacksonreed.ClientRemulak . . .

Querying for all customers

customer id is 1234 customer number is abc1234 last name is
Reed
   role id is 3456 role name is Billing
     address id is 1234 address line 1 is 6660 Delmonico Drive
   role id is 1234 role name is Shipping
     address id is 1234 address line 1 is 6660 Delmonico Drive
 customer id is 2345 customer number is abc2345 last name is
 Becnel
   role id is 2345 role name is Mailing
     address id is 2345 address line 1 is 2323 Happy Boy
     Lane

 customer id is 3456 customer number is abc3456 last name is
Young
   No roles for this customer
Ending com.jacksonreed.ClientRemulak . . .

The foundation has been laid. It is amazing how little code has to be written when the container-managed persistence support offered by the EJB specification is being used.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020