Thursday, January 18, 2007

J2EE/EJB technology overview

J2EE/EJB technology overview


EJB Tutorials


The J2EE platform


The J2EE 1.3 specification (see Resources ) defines a standard mechanism, known as the J2EE platform, for hosting J2EE applications. The platform uses a component-based approach to provide a multitiered, distributed, transactional model for enterprise applications. If that sounds like a mouthful, it certainly is. A J2EE application server that is compliant with the J2EE specification must provide services and APIs for:

  • Enterprise JavaBeans Technology 2.0
  • JDBC API 2.0
  • Java Servlet Technology 2.3
  • JavaServer Pages Technology 1.2
  • Java Message Service 1.0
  • Java Naming and Directory Interface 1.2
  • Java Transaction API 1.0
  • Java Mail API 1.2
  • JavaBeans Activation Framework 1.0
  • Java API for XML Processing 1.1
  • J2EE Connector Architecture 1.0
  • Java Authentication and Authorization Service 1.0

As we will see, to manage this massive amount of functionality in a multitier, multithreaded, multiuser environment, the J2EE platform imposes many restrictions on developers, especially in regard to EJB components.



Back to top


Containers and components

The application server maintains control and provides services through an interface or framework known as a container. There are five defined container types in the J2EE specification. Three of these are server-side containers:

  • The server itself, which provides the J2EE runtime environment and the other two containers
  • An EJB container to manage EJB components
  • A Web container to manage servlets and JSP pages

The other two container types are client-side:

  • An application container for stand-alone GUIs, console, and batch-type programs -- the familiar Java applications started with the java command
  • An applet container, meaning a browser, usually with the Java Plug-in

There are three kinds of defined components:

  • Client components, which correlate to the client containers
  • Web components -- servlets and JSP pages
  • EJB components

In the server-side J2EE context, a component is a unit of functionality that is assembled, along with any required resources, into a J2EE application. Note that a "J2EE application" may be a single component. The server-side components can communicate with each other inside the J2EE environment (intra-VM on a single machine) or in a networked, multimachine, distributed environment. The client-side containers communicate with the J2EE container and server-side components through a client JAR generated at deployment time. Client-side containers can be on the same machine or, more typically, somewhere on the network. Also, components can be clients of other EJB components -- that is, they can call on the services of other beans.

While any or all of the containers and components may be on the same or multiple machines -- remember, J2EE is a distributed model -- conceptually, the architecture is almost always three tier. These tiers are client, business logic, and database/Enterprise Information Systems (EIS).




Packaging applications and components

Under J2EE, applications and components reside in Java Archive (JAR) files. These JARs are named with different extensions to denote their purpose, and the terminology is important.

  • Enterprise Archive (EAR) files represent the application, and contain all other server-side component archives (as mentioned below, JARs for EJB components, WARs for Web components, etc.) that comprise the application.
  • Client interface files and EJB components reside in JAR files.
  • Web components reside in Web Archive (WAR) files.

Deployment descriptors are included in the JARs, along with component-related resources. Deployment descriptors are XML documents that describe configuration and other deployment settings. Remember that the J2EE application server controls many functional aspects of the services it provides. The statements in the deployment descriptor are declarative instructions to the J2EE container; for example, transactional settings are defined in the deployment descriptor and implemented by the J2EE container. Most J2EE vendors provide a GUI tool for generating deployment descriptors and performing deployment because creating manual entries is tedious and error prone. The J2EE RI includes deploytool, which is the deployment vehicle we'll use in this tutorial.

The deployment descriptor for an Enterprise JavaBean component must be named ejb-jar.xml, and it resides in the META-INF directory inside the EJB JAR file. A JAR can contain multiple beans; if so, a single ejb-jar.xml file describes all the beans in the JAR. For more information concerning packaging and deployment, see Appendix B: Deployment.





EJB components

EJB components are server-side, modular, and reusable, comprising specific units of functionality. They are similar to the Java classes we create every day, but are subject to special restrictions and must provide specific interfaces for container and client use and access. In addition, they can only run properly in an EJB container, which manages and invokes specific life cycle behavior. You should consider using EJB components for applications that require scalability, transactional processing, or availability to multiple client types.

EJB components come in three varieties, each with its own defined role and life cycle:

  • Session beans. These may be either stateful or stateless and are primarily used to encapsulate business logic, carry out tasks on behalf of a client, and act as controllers or managers for other beans.

  • Entity beans. Entity beans represent persistent objects or business concepts that exist beyond a specific application's lifetime; they are typically stored in a relational database. Entity beans can be developed using bean-managed persistence, implemented by the developer, or container-managed persistence, implemented by the container.

  • Message-driven beans. Message-driven beans listen asynchronously for Java Message Service (JMS) messages from any client or component and are used for loosely coupled, typically batch-type, processing.

This tutorial covers the EJB 2.0 specification (see Resources ), which has significant differences from the previous versions. In particular, container-managed persistence is newly implemented in the 2.0 version, and message-driven beans are a brand-new bean type.

Much of the code in bean methods will be familiar, but, to function effectively, EJB components must be able to access J2EE resources, and know how to respond to life cycle calls and callbacks from the EJB container. Unfortunately, the specification defines life cycle methods in three different areas: some are defined in The home interface; others are defined in the various bean interfaces defined in the javax.ejb package; still others are simply mandated by the specification. The tutorial discusses the complete life cycle methods for each bean type in the appropriate section. You will frequently see the pattern of methodName() in the exposed client interface, with a matching ejbMethodName() for life cycle methods.

According to the J2EE specification, "Applications must be able to access resources and external information in their operational environment without knowledge of how the external information is named and organized in that environment." J2EE makes use of the Java Naming and Directory Interface (JNDI) to accomplish this goal. EJB components fit under this definition of resources, and session and entity beans must provide a home interface to allow the container and clients to obtain a reference to a specific bean. The container makes the bean available via JNDI. Message-driven beans, which have no direct client, are an exception to the general rule for required interfaces.

It is very important to understand that EJB components are never accessed directly by clients. Instead, the container generates proxy objects that implement the home and component interfaces and are used by the client for communication with a bean. The proxy objects then call upon the bean to perform the requested task. This mechanism allows the container to coordinate and manage beans however and wherever it sees fit; the client remains blissfully unaware of the internal details.




EJB programming restrictions

For the container to properly carry out its duties, provide appropriate services, and manage the components within its environment, many restrictions apply when programming with EJB technology. These rules often cause much confusion and gnashing of teeth among developers, but it is best to understand and abide by them to avoid problems in your beans and applications. The following is reproduced verbatim from Section 24.1.2, "Programming restrictions," of the EJB 2.0 Specification (see Resources ):

"This section describes the programming restrictions that a Bean Provider must follow to ensure that the enterprise bean is portable and can be deployed in any compliant EJB 2.0 Container. The restrictions apply to the implementation of the business methods. Section 24.2, which describes the Container's view of these restrictions, defines the programming environment that all EJB Containers must provide.

  • An enterprise Bean must not use read/write static fields. Using read-only static fields is allowed. Therefore, it is recommended that all static fields in the enterprise bean class be declared as final.
  • An enterprise Bean must not use thread synchronization primitives to synchronize execution of multiple instances.
  • An enterprise Bean must not use the AWT functionality to attempt to output information to a display, or to input information from a keyboard.
  • An enterprise bean must not use the java.io package to attempt to access files and directories in the file system.
  • An enterprise bean must not attempt to listen on a socket, accept connections on a socket, or use a socket for multicast.
  • The enterprise bean must not attempt to query a class to obtain information about the declared members that are not otherwise accessible to the enterprise bean because of the security rules of the Java language. The enterprise bean must not attempt to use the Reflection API to access information that the security rules of the Java programming language make unavailable.
  • The enterprise bean must not attempt to create a class loader; obtain the current class loader; set the context class loader; set security manager; create a new security manager; stop the JVM; or change the input, output, and error streams.
  • The enterprise bean must not attempt to set the socket factory used by ServerSocket, Socket, or the stream handler factory used by URL.
  • The enterprise bean must not attempt to manage threads. The enterprise bean must not attempt to start, stop, suspend, or resume a thread; or to change a thread's priority or name. The enterprise bean must not attempt to manage thread groups.
  • The enterprise bean must not attempt to directly read or write a file descriptor.
  • The enterprise bean must not attempt to obtain the security policy information for a particular code source.
  • The enterprise bean must not attempt to load a native library.
  • The enterprise bean must not attempt to gain access to packages and classes that the usual rules of the Java programming language make unavailable to the enterprise bean.
  • The enterprise bean must not attempt to define a class in a package.
  • The enterprise bean must not attempt to access or modify the security configuration objects (Policy, Security, Provider, Signer, and Identity).
  • The enterprise bean must not attempt to use the subclass and object substitution features of the Java Serialization Protocol.
  • The enterprise bean must not attempt to pass this as an argument or method result. The enterprise bean must pass the result of SessionContext.getEJBObject(), SessionContext.getEJBLocalObject(), EntityContext.getEJBObject(), or EntityContext.getEJBLocalObject() instead."



Local and remote interfaces

Note: The following discussion of bean interfaces applies only to session and entity beans. Message-driven beans effectively run within a batch environment on the server and are represented only by the bean class -- no interfaces are used.

Clients access a session or entity bean through the bean's interfaces. The EJB container generates the interface implementations to enforce and manage this behavior, acting as a conduit for communication between the client and the bean. In versions before the EJB 2.0 specification, all beans were defined and implemented as distributed, remote components. As a result, the two interfaces required of beans were termed the home interface (which in general defines life cycle methods) and the remote interface (which in general defines functional business methods).

Internally, J2EE uses Java Remote Method Invocation over Internet Inter-ORB Protocol technology (RMI-IIOP; see Resources ) to enable remote, distributed method calls and applications. While this approach provides many benefits, it does involve a large amount of overhead and a corresponding performance hit as stubs are referenced, parameters go through the marshaling process, and objects are tossed around the network.

Considerations of performance, practicality, and typical usage in the field resulted in the introduction of local interfaces in the EJB 2.0 specification. As noted, prior terminology referred to the home interface and the remote interface; at this point, depending on which approach is used, it's better to use the terms local interface and local home interface or remote interface and remote home interface. Either of the local home or remote home interfaces is referred to as the home interface; either of the local or remote interfaces is referred to as the component interface. This tutorial refers to the interfaces in these terms and uses these conventions for names.

When using J2EE technologies, it is normal to focus on distributed, or remote, beans, but you should keep the local option in mind, when applicable. It may surprise you to learn that a bean can have local interfaces, remote interfaces, or both. However, the client must write to a specific (that is, local or remote) interface. There are some issues to keep in mind when using local interfaces:

  • The beans must run in the same VM -- they are, after all, local.
  • Parameters running under a local interface are sent by reference rather than being copied, as is the case for remote objects. Unexpected side effects can result if you ignore this distinction and do not code accordingly.

Typically, you'll decide whether to use local or remote access based on:

  • The type of client. Unless the client is always expected to be a Web component or another bean, choose remote access.

  • Whether the beans are tightly or loosely coupled. If beans depend on each other and interact frequently, you should consider local access.

  • Scalability. Remote access is inherently scalable and should be used if scalability is an important factor.

With the advent of local interfaces in the EJB 2.0 specification, it is recommended that entity beans should almost always be based on local access. When using local interfaces, most performance issues regarding very fine-grained data access go away. If the client is remote, the standard design pattern has the client use a remote interface to access a session bean, which then acts as a liaison to the entity bean. The session bean communicates with the entity bean through a local interface (from a patterns viewpoint, this technique is called a Session Facade, which can actually be used in either a remote or local context).

Performance note: The tutorial examples all have Web components (JSP pages) as clients. For the purposes of the tutorial, remote interfaces are used for communication between page and bean. Remote interfaces will work for any client. However, the servlet engine will almost always be running inside the J2EE container and in the same VM. This means that you can use local interfaces for servlet and JSP clients, with a resulting performance boost.

For other considerations regarding local and remote interfaces, consult the EJB 2.0 specification (see Resources ).




The home interface

Note: The following discussion of bean interfaces applies only to session and entity beans. Message-driven beans effectively run within a batch environment on the server and are represented only by the bean class -- no interfaces are used.

A bean's home interface specifies methods that allow the client to create, remove, and find objects of the same type. The home interface may also provide definitions for home business methods for entity beans. Home business methods are methods that are not specific to a particular bean instance. While the developer writes the home interface, the container creates the implementation for client interaction. In essence, the home interface provides bean management and life cycle methods.

The client uses a JNDI lookup to locate a bean's home interface. The EJB 1.1 specification introduced the environment naming context (ENC) as a means of enhancing portability and avoiding name clashes in the JNDI namespace. In practice, this means that you should preface the lookup string with java:comp/env/. The container is required to recognize a lookup coded in this manner as an alias or nickname rather than the direct JNDI name. A deployment descriptor entry links the alias to the actual JNDI entry. Use of the ENC also means that the developer doesn't have to worry about hardcoded JNDI names; the ENC effectively makes them softcoded. The sample code below locates the remote home interface for a bean with a JNDI name of ejb/MyEJB (this could be a completely different external name, depending on the ejb-ref entry in the deployment descriptor). This code, and the following code for local home lookup, is virtually identical in every client's bean access routine:

      InitialContext ic = new InitialContext();
Object oRef =
ic.lookup( "java:comp/env/ejb/MyEJBBean" );
MyEJBRemoteHome MyEJBHome =
(MyEJBRemoteHome)PortableRemoteObject.
narrow( oRef, MyEJBRemoteHome.class );

The PortableRemoteObject.narrow() method is required to ensure conversion to the proper remote interface type.

For a bean that provides a local home interface instead of, or in addition to, a remote home interface, the following code is typical:

      InitialContext ic = new InitialContext();
MyEJBLocalHome MyEJBHome =
(MyEJBHome)ic.lookup( "java:comp/env/ejb/MyEJBBean" );

Figure 1. The javax.ejb.EJBHome and javax.ejb.EJBLocalHome interfaces
The javax.ejb.EJBHome and javax.ejb.EJBLocalHome interfaces

The remote home interfaces that you write extend the javax.ejb.EJBHome interface; your local home interfaces extend the javax.ejb.EJBLocalHome interface. Within the home interface, the specification normally requires that you implement at least one create() method.

The create() and remove() methods have very different effects for entity beans than they do for other bean types. As will be seen in the appropriate sections, for entity beans the methods create and delete persistent data (think SQL INSERT and DELETE statements for RDBMS datastores). For session beans, these methods create (or draw from a pool) and disassociate bean instances. The create() methods also ask the container to return the component interface for the requested Enterprise JavaBean instance.

The prototype for a remote home interface create() method looks like this:

  MyEJBRemote create() throws RemoteException, 
CreateException;

For a local home interface create() method, the code looks like this:

  MyEJBLocal create() throws CreateException;

You may notice later that there is no direct corresponding create() method in your bean. Instead, your bean defines, per the specification, an ejbCreate() method to match each interface create() method, which the container calls before persisting new data for entity beans and after bean instantiation for the other EJB component types. The ejbCreate() method for session and message-driven beans may be viewed as similar to the init() method in applets and servlets.

The pattern of pairs of methodName() and ejbMethodName() is repeated often for bean contract methods, with methodName() used for the client interface and ejbMethodName() used in the bean's implementation. Remember that the container always calls the bean's methods, so it knows that it needs to preface the method with ejb. See the relevant bean contract sections in the EJB 2.0 specification for complete information (available from Resources ).




The component interface

Note: The following discussion of bean interfaces applies only to session and entity beans. Message-driven beans effectively run within a batch environment on the server and are represented only by the bean class -- no interfaces are used.

Enterprise JavaBean functionality is obtained through the bean's component interface, which defines the business methods visible to, and callable by, the client. Again, the developer writes the component interface, and the container creates the implementation for client interaction.

The client uses a home interface's create() method to obtain a reference to a bean's component interface. In the entity bean section, we will see that a component interface may also be returned by findByPrimaryKey() and other finder methods. The sample code below locates the home interface for a remote bean with a JNDI name of ejb/MyEJB, then obtains the component interface by invoking <HomeInterface>.create(). This code, and the following code for local home lookup, is virtually identical in every client's EJB access routine:

      // get the bean's Home interface
InitialContext ic = new InitialContext();
Object oRef =
ic.lookup( "java:comp/env/ejb/MyEJBBean" );
MyEJBRemoteHome MyEJBHome =
(MyEJBRemoteHome)PortableRemoteObject.
narrow( oRef, MyEJBRemoteHome.class );

// get the component interface
MyEJBRemote MyEJB = MyEJBHome.create();

Again, note that PortableRemoteObject.narrow() must be used on the object returned from the JNDI lookup rather than Java language casts for remote types.

For a bean that provides a local home and component interface, the code looks like this:

      // get the EJB's Home interface
InitialContext ic = new InitialContext();
MyEJBLocalHome MyEJBHome =
(MyEJBHome)ic.lookup( "java:comp/env/ejb/MyEJBBean" );

// get the component interface
MyEJBLocal MyEJB = MyEJBHome.create();


Figure 2. The javax.ejb.EJBObject and javax.ejb.EJBLocalObject interfaces
The javax.ejb.EJBObject and javax.ejb.EJBLocalObject interfaces

When writing a remote interface, your interface extends javax.ejb.EJBObject. When writing a local interface, your interface extends javax.ejb.EJBLocalObject. Remember that the component interface is the client's view of your bean's functionality. Therefore, this is the place where you define all of the methods that should be available to the client.

The component interface's remove() method disengages the current bean from the client for session beans but deletes data from the datastore for entity beans. The life cycle and associated methods are discussed more completely for each bean type in the relevant sections ahead.

No comments: