©1994-2003 Kevin Boone
Home     Section index     K-Zone home Software development

Site search

Glossary
Confused by computer jargon? Look it up!

Shameless plug


Now available!

Articles
- Ten-minute guide to setting up a WAP site

- Talk like your boss: new developments in managerese

More...

Development
File handling in the Linux kernel

Java development for the Sony-Ericsson P800

SunONE Application Server 7 FAQ

More...

Linux
Using Linux with the Treo 600

- Linux on the Tecra M1

- Some notes on openzaurus

More...

Download
Java stuff

Linux stuff

More...

(Please read the download policy)

Home automation
The X10 system

Linux TW723 driver

More...

The K-Zone
K-Zone computing

K-Zone law

K-Zone education and science

K-Zone motorcycles

K-Zone DIY

K-Zone railways

K-Zone martial arts

About the author

K-Zone home page

 
Computing
Java on the P800
J2ME FAQ
Perl for Java programmers
Web Service tutorial
CLASSPATH
EJBs on jBoss
CMP with jBoss
Bean managed persistence
C++ tutorial
Simple Java
JDK setup
Howto-appletviewer
Howto-dosprompt
EJB lifecycles
EJB transactions
S1AS7 FAQ
iAS 6.0 FAQ
iAS JMQ
Inside the J2EE reference implementation

 

Inside the J2EE Reference Implementation

By Kevin Boone
August 2000

Disclaimer

I have produced this document for educational purposes, to accompany my own classes and for others who are interested. Some of the features it describes are specific to Sun's implementation of an EJB server, and may change in subsequent releases of their J2EE product. Moreover, different container vendors may implement things differently. Therefore I don't recommend that direct use is made of any of the proprietary classes or interfaces described. Please note that although I do teach EJB technology for Sun, I am not involved with the development of the J2EE product, and nothing in this document should be construed as a statement of Company policy.

Introduction

In this document I explain in detail what is going on inside Sun's J2EE Reference Implementation server when a simple EJB is deployed and a method is executed. I assume familiarity with the the basic principles of EJB technology, such as the use of the home and remote interfaces. I also assume that the reader has some basic understanding of the J2EE product itself, and its deployment tools. For many developers, the level of detail exposed in this article is beyond what is required; however, a good way to develop a really thorough understanding of EJB technology is to study exactly what the client and the server are doing when an EJB is deployed and used.

A note on terminology

Because some terms are used differently by different authors, I will clarify at the outset the way in which I will use these terms.
Implementation class, bean class The class provided by the developer to implement the EJB's business functionality
EJB object The object on the EJB server that is an implementation of the EJB's remote interface. Some people use the term `remote object' for this, but `EJB object' is used in the EJB specification.
Home object The object on the EJB container that is an implementation of the EJB's home interface. This is often a subclass of the EJB container itself, but it doesn't have to be.
Stub, transport stub Any object that handles the client side of the client-server communications protocol, if required.
Skeleton Any object that handles the server side of the client-server communications protocol, if required.
Remote stub This often causes confusion; the `remote stub' implements the client side (the `stub side') of the client-server protocol to the EJB object (the remote object). It is important to realize that, despite the name, the remote stub is instantiated on the client, not the server.
Home stub The class that implements the client side of the client-server protocol to the home object.
Remote skeleton The class that implements the server side of the client-server protocol to the EJB object (`remote object). Note that the J2EE RI implements the remote skeleton and the EJB object as separate classes, but in some EJB servers these roles are combined.
Home skeleton The class that implements the server side of the client-server protocol to the Home object. Note that the J2EE RI implements the home skeleton and the home object as separate classes, but in some EJB servers these roles are combined.

The EJB

For purposes of illustration I will use one of the EJBs from the `bank' tutorial example, available from the JavaSoft Web site. The bank application actually has four EJBS: Manager, Teller, Customer and Account; we will focus on the `Manager' EJB, as it is the simplest. `Manager' is a stateless session bean. It is comprised of the standard class and interfaces, as shown below.
Implementation class bank.ManagerBean
Remote interface bank.Manager
Home interface bank.ManagerHome
Although the Manager bean has a number of business methods, I will assume that the client will only call one: getAllCustomers(). This method is specified, as usual, in the remote interface bank.Manager, and implemented in the bean implementation class bank.ManagerBean. It will be the job of the EJB server to create the implementations of the home and remote interfaces, along with the transport stubs. This happens at deployment time.

Deployment

During the deployment process, the J2EE server and the deployment tools automatically create supporting classes based on the bean's implementation class, home interface, and remote interface. These classes can be broadly summarized as follows.
  • `Container' classes; these are the actual implementations of the developer-supplied home interface and remote interface. They are created, complied and instantiated on the server, and control access to the instances of the bean class itself.
  • `Skeleton' classes; these classes handle the server side of the client-server communications protocol between the client and the `container' classes; they are instantiated on the server.
  • `Stub' classes; these classes handle the client side of the communication. These are downloaded to the client during deployment, and instantiated on the client when it executes.
Note that during deployment the developer's home interface, remote interface and bean class must also be copied to the server, as the container will create implementations of the interfaces, and instantiate the bean class on demand.
      The server-side classes are packaged into a JAR whose name starts with the application name, and which includes a serial number. During deployment the skeleton, sub and container classes will created as Java source files, compiled and packaged. The intermediate files are then deleted.
      The client-side classes are packaged into a JAR whose name is chosen by operator at deploy time.
      For the Manager EJB, the classes created on the server are as follows.
bank.ManagerBeanHomeImpl Home object that serves as the EJB container for instances of ManagerBean. This class implements the interface bank.ManagerHome, and extends com.sun.ejb.containers.EJBHomeImpl. A simplified version of this class is shown in listing 1 at the end of this document.
bank._ManagerBeanHomeImpl_Tie This class implements a CORBA `skeleton'; it mediates between the client's stub (bank._ManagerHome_Stub) and the container ManagerBeanHomeImpl
bank.ManagerBean_EJBObjectImpl This class implements the EJB object (`remote object') which will act as a proxy for the `real' bean instance bank.ManagerBean. It implements the remote interface bank.Manager, and extends com.sun.containers.StatelessSessionEJBObjectImpl. This class will handle serialization of accesses to the bean, creation and completion of transactions, and security validation. A simplified version of this class is shown in listing 2.
bank._ManagerBean_EJBObjectImpl_Tie This class implements a CORBA `skeleton'; it mediates between the client's stub (bank._Manager_Stub) and the EJB object ManagerBean_EJBObjectImpl
Each of these classes will be described in more detail as we examine the process of running a simple client application.
      After deployment, and any time the J2EE server is started, the deployment descriptor for the EJB is examined, and the JNDI name registered with the naming service. In addition, a home object is instantiated. This object (of class bank.ManagerBeanHomeImpl) will manage all requests to create, find and delete Manager EJBs. Note that Manager is a stateless session bean, and the home object will create and delete instances of the bean class as it sees fit; this creation and deletion is not controlled directly by the client.

Execution

The EJB's methods are executed in response to method calls on the client. The code in the client that is important from an EJB perspective is quite simple (line numbers are for reference in the text below):
1    InitialContext ic = new InitialContext();
2    Object lookup = ic.lookup("Manager");
3    ManagerHome home = (ManagerHome)
         PortableRemoteObject.narrow(lookup, ManagerHome.class);
4    Manager m = home.create();
5    Collection c = m.getAllCustomers(); 
We will now consider in detail what happens when this client code is executed. For simplicity, we will assume that this is the first time it has been run, and no other client has had occasion to use the `Manager' EJB.
  • 1. Get the initial naming context. Because we haven't indicated otherwise, there will be no URL or naming context factory associated with this naming context. This means that when the lookup is performed, it will be carried out using the default naming context class com.sun.jndi.cosnaming.CNCtx, and the default URL iiop://localhost:1050. (Note that when J2EE starts up, it indicates that its naming service is on port 1050). This default is appropriate if the client and the EJB server are actually on the same host. If not, we will need to instantiate InitialContext with the appropriate environment. The naming context CNCtx handles the client side of the CORBA naming service (`CoSNaming'). When the J2EE server started, it obtained name-object mappings from information that was provided (in the deployment descriptor XML) when the EJB was deployed.
  • 2. Look up the home interface for the EJB using the CORBA naming service on the specified server. The naming context client essentially maps the information about the EJB known to the naming service on the server, into a class that the client can make method calls on. In this case, the object returned by lookup("Manager") is an instance of the home stub class bank._ManagerHome_Stub. Notice how this name is derived from the name of the home interface bank.ManagerHome. Where is this class? It was generated automatically by the J2EE server when the bean was deployed, and returned in the `Client JAR' file. The stub class has the following signature:
    public class bank._ManagerHome_Stub 
    	extends javax.rmi.CORBA.Stub implements bank.ManagerHome 
    {
    static java.lang.Class class$javax$ejb$EJBMetaData;
    static java.lang.Class class$javax$ejb$RemoveException;
    static java.lang.Class class$bank$Manager;
    static java.lang.Class class$javax$ejb$CreateException;
    static {};
    public _ManagerHome_Stub();
    public java.lang.String _ids()[];
    static java.lang.Class class$(java.lang.String);
    public bank.Manager create() throws javax.ejb.CreateException, java.rmi.RemoteException;
    public javax.ejb.EJBMetaData getEJBMetaData() java.rmi.RemoteException;
    public javax.ejb.HomeHandle getHomeHandle() throws java.rmi.RemoteException;
    public void remove(java.lang.Object) throws java.rmi.RemoteException, javax.ejb.RemoveException;
    public void remove(javax.ejb.Handle) throws
    java.rmi.RemoteException, javax.ejb.RemoveException;
    }
    
    Note that this home stub class is a CORBA stub, and implements the bean's home interface bank.ManagerHome. It provides concrete implementations of methods like create() that are specified in the home interface, but does not implement any `real' functionality in them. Instead, methods in the home stub will communicate with the `home skeleton' on the server. This is the class bank.ManagerBeanHomeImpl_Tie. This will delegate method calls to the home object implementation bank.ManagerHomeImpl on the server. It is this class that will do the real work. The protocol used between the client and the EJB server is IIOP (Internet intra-ORB protocol; ORB is an abbreviation for Object Request Broker); the stub and the skeleton between them isolate the technicalities of this protocol from the client and the server.
  • 3. We now call PortableRemoteObject.narrow() to get an RMI-compatible stub that corresponds to the home stub returned by the lookup operation. In this case, the home stub is already RMI-compatible (we are using a Java implementation of a CORBA client, after all), so we get another instance of class bank._ManagerHome_Stub. In other words, narrow() has no effect here, and we could have written
    ManagerHome = (ManagerHome)ic.lookup ("Manager");
    
    and skipped the narrowing step. However, this would prevent the client from communicating with the EJB using other transport protocols, should we wish to do so in future. With or without the call to narrow(), we are doing a narrowing cast here; that is, we are casting something that can be any subclass of java.lang.Object (the defined return type for lookup()) into a ManagerHome. Some developers use the term `down-cast' for this operation.
  • 4. We call create() on the home interface.
    • This actually results in a call to bank._ManagerHome_Stub.create() (because we cast an object of this class into a ManagerHome).
    • The create() method in the stub communicates with the home object on the J2EE server. Specifically, the create call is passed through the stub and skeleton to the create() method in the home object bank.ManagerBeanHomeImpl on the server (see listing 1). This object then instantiates the EJB object (`remote object') of class bank.ManagerBean_EJBObjectImpl and the communication skeleton bank._ManagerBean_EJBObjectImpl_Tie (lines 6 and 7 in listing 1).
    • The home object calls setContainer() on the new remote object, so that the remote object can invoke container services later. It will need to do this, for example, to create new bank.ManagerBean instances.
    • Note that although we now have a home object and a remote object on the server, we don't yet have any actual bank.ManagerBean instances, and the bean is not ready to do any work. However, since stateless session beans can be pooled, on subsequent invocations of this client there may be existing instances of bank.ManagerBean in the pool.
    • The home object returns to the client, via its skeleton and stub, a reference to the newly-created EJB object. The home stub on the client actually returns to the client an instance of a the remote stub; this stub will handle future communication with the EJB object. In this example, the remote stub is an instance of a CORBA stub that implements bank.Manager. Its signature is shown below.
      public class bank,_Manager_Stub 
      		extends javax.rmi.CORBA.Stub implements bank.Manager 
      {
      static java.lang.Class class$javax$ejb$EJBHome;
      static java.lang.Class class$javax$ejb$RemoveException;
      static java.lang.Class class$java$lang$String;
      static java.lang.Class class$bank$BankException;
      static java.lang.Class class$java$util$Collection;
      static {};
      public _Manager_Stub();
      public java.lang.String _ids()[];
      static java.lang.Class class$(java.lang.String);
      public javax.ejb.EJBHome getEJBHome() throws java.rmi.RemoteException;
      public javax.ejb.Handle getHandle() throws java.rmi.RemoteException;
      public java.lang.Object getPrimaryKey() throws java.rmi.RemoteException;
      public boolean isIdentical(javax.ejb.EJBObject) throws java.rmi.RemoteException;
      public void remove() throws java.rmi.RemoteException, javax.ejb.RemoveException;
      public java.util.Collection getAllCustomers() throws java.rmi.RemoteException;
      // Other business methods omitted...
      }
      
      The client does not have to cast this reference, as it is defined to be of the proper type (it was created by a call to create() in the home stub, whose return type is bank.Manager.
    • Note that the remote stub contains implementations of the business methods getAllCustomers() etc. Of course, these methods do not implement the functionality of these methods, this is done by the implementation class bank.ManagerBean on the server. The stub simply passes method invocations through to the implementation via the remote skeleton and the EJB object.
    In summary, the chain of communication between the client and the EJB home object can be shown as follows:

                                  |
    Client -->     Home stub     -|->  Home skeleton           -->  Home object
               _ManagerHome_Stub  | _ManagerBean_HomeImpl_Tie     ManagerHomeImpl
                                  |
           (client host)          |                  (server host)
    

  • 5. Now that we have a remote reference to the bean, we can call any of its business methods. In this example we are calling getAllCustomers().
    • In fact, the client calls this method on the remote stub, which communicates with the remote skeleton (bank._ManagerBean_EJBObjectImpl_Tie which calls the corresponding method bank.ManagerBean_EJBObjectImpl.getAllCustomers() in the EJB object on the server.
    • The EJB object asks the container for an instance of bank.ManagerBean. Specifically, the EJB object creates an instance of com.sun.ejb.Invocation() and sets its fields to indicate the method that will be invoked, and the EJB object (itself) that is requesting it (lines 6-8, listing 1). This object is then passed to the container (line 9).
    • The container checks whether the client is authorized to call the requested method. If not, it throws an exception.
    • If the client is authorized, the container checks whether any instances of ManagerBean are available. If this is the first usage of this bean, it is only at this point that the container will realize that there is no bank.ManagerBean instance to pass the method call on to.
    • The container creates a new SessionContext object for the new bean instance it is about to create. This object will allow the bean to determine security and transaction attributes, for example. In fact, the container instantiates com.sun.ejb.containers.SessionContextImpl, which implements the SessionContext interface.
    • The container creates a new instance of bank.ManagerBean, by calling newInstance() on its class. A reference to this new instance is held in a pool for future invocations. Note that calling newInstance() causes the bean's no-arg constructor to be called, but does not initialize it in any other way.
    • The container calls setSessionContext() on the new instance of bank.ManagerBean, passing in the instance of com.sun.ejb.containers.SessionContextImpl created previously. The bean may store this reference, or ignore it, at the discretion of the developer. Of course, a good reason for storing it is that its methods can be used to check the security roles of the client.
    • The container begins a new transaction for the requested method call, if the transaction attributes specify that a transaction is required, and no transaction currently exists.
    • The container returns the reference to the new bean instance to the remote object. In fact, it simply initializes the ejb instance variable in the Invocation instance (line 10 in listing 2).
    • The remote object calls the business method getAllCustomers() in the bean instance (line 11).
    • The bean generates a list of customers (perhaps a list of bank.Customer instances) and returns it as a Collection. This return is to the EJB object.
    • The EJB object signals the container (line 12) that the method call is complete. The container will commit or roll back the transaction, if it initiated a transaction for this method call.
    • The EJB object returns the output from the business method to the remote skeleton (line 13), which passes it over the network to the remote stub, which unmarshalls the data and passes it back to the client.
  • On the client, the remote reference to the bean (the variable `m') will eventually go out of scope. At some point after this the garbage collection system will finalize it and remove it. This causes the remote stub to be finalized, and a finalization notification to be sent over the network to the remote skeleton. This signals the EJB object, which signals the container, which may decide to remove the EJB object and the bean instance. But that's a another story.

Listing 1: EJB home implementation sample

This class is representative of the home implementation that is generated automatically by the J2EE server when the EJB is deployed. Note that the listing is very short; there is only one method that is specific to the current EJB: create(). All the other work is handled by the superclass com.sun.ejb.containers.EJBHomeImpl.
1 package bank;

2 public final class BankHomeImpl 
    extends com.sun.ejb.containers.EJBHomeImpl
    implements ManagerHome 
{
3 public TestHomeImpl(com.sun.ejb.Container c) throws java.rmi.RemoteException 
  {
4 super(c);
  }

5 public Manager create() 
    throws java.rmi.RemoteException, javax.ejb.CreateException 
  {
6  ManagerBean_EJBObjectImpl ejbObject =
      (ManagerBean_EJBObjectImpl) createEJBObject(); // method in superclass
7  return (Manager)ejbObject.getStub();
}

Listing 2: EJB object implementation sample

This listing is representative of the EJB object class that is generated automatically by the J2EE server when the EJB is deployed. This class handles all requests for business methods sent from the client to the EJB, delegating them where appropriate to the real bean, bank.ManagerBean. For clarity, I have removed the exception handling code (which is quite extensive).
1 package bank;
2 import java.util.*;

3 public final class ManagerBean_EJBObjectImpl 
    extends com.sun.ejb.containers.StatelessSessionEJBObjectImpl 
    implements ManagerBean 
{
4 public ManagerBean_EJBObjectImpl() throws java.rmi.RemoteException { }

5 public Collection getAllCustomers() 
      throws java.rmi.RemoteException
    {
6   com.sun.ejb.Invocation i = new com.sun.ejb.Invocation();
7   i.ejbObject = this;
8   i.method = bank.Manager.class.getMethod("getAllCustomers", new java.lang.Class[] {});
9   this.getContainer().preInvoke(i);
10  bank.ManagerBean ejb = (bank.ManagerBean) i.ejb;
11  Collection c = ejb.getAllCustomers();
12  this.getContainer().postInvoke(i);
13  return c;
    }
}