Today, more and more developers want to write distributed transactional applications for the enterprise, and leverage the speed, security, and reliability of server-side technology. One approach is to use a multitiered model where a thin-client application invokes business logic that executes on the server.
Normally, thin-client multitiered applications are hard to write because they involve many lines of intricate code to handle transaction and state management, multithreading, resource pooling, and other complex low-level details. The Enterprise JavaBeans (EJB) architecture makes these applications easy to write because it separates the low-level details from the business logic. You concentrate on creating the best business solution and leave the rest to the underlying architecture.
To show you how it works, this article walks through a very simple web-based application where the thin-client is a servlet. The servlet invokes enterprise Beans for database reads and writes and to perform a simple calculation.
An enterprise Bean is a body of code with fields and methods to implement modules of business logic. Client programs interact with one or more Beans, and an enterprise Bean can be implemented to interact with other enterprise Beans. An enterprise Bean is a building block that can be used alone or with other enterprise Beans to build a complete and robust thin-client multitiered application.
There are two types of enterprise Beans: session Beans and entity Beans. An enterprise Bean that implements a business task is a session Bean, and an enterprise Bean that implements a business entity is an entity Bean.
Session Beans | Entity Beans |
---|---|
Fields contain conversation state. | Represents data in a database. |
Handles database access for client. | Shares access for multiple users. |
Life of client is life of Bean. | Persists as long as data in database. |
Can be transaction aware. | Transactional. |
Does not survive server crashes. | Survives server crashes. |
Note: In the Enterprise Java Beans specification, EJB Server support for session Beans is mandatory. EJB Server Support for entity Beans is currently optional, but becomes mandatory for version 2.0 of the specification.
A high-level view of the simple example application is shown in the diagram. The client application is a servlet that receives data from and sends data to a browser, and interacts with an entity Bean and a session Bean through their home and remote interfaces. The EJB Home Server handles all the low-level details including the database read and write operations.
An enterprise Bean's remote interface describes the Bean's methods, or what the Bean does. A client program or another enterprise Bean calls the methods defined in the remote interface to invoke the business logic implemented by the Bean.
An enterprise Bean's home interface describes how a client program or another enterprise Bean creates, finds, and removes that enterprise Bean from its container.
The container, shown in light blue (cyan), provides the interface between the enterprise Bean and the low-level platform-specific functionality that supports the enterprise Bean.
You do not need to know how an enterprise Bean is implemented to use it; all you need to know are its methods. You might or might not write your own enterprise Beans to use in an application. It is possible and often desirable to use enterprise Beans written by a provider that you assemble into an application.
Deployment tools and an EJB Home Server are essential to running the application code. The deployment tools generate enterprise Bean containers, which are classes that provide an interface to the low-level implementations in a given EJB Server. The server provider can include containers and deployment tools for their server and will typically publish their low-level interfaces so other vendors can develop containers and deployment tools for their server.
The simple example uses the EJB Server created by EJBHome. Visit this site for a free reference implementation that includes the EJB Server and deployment tools for generating containers. The site also provides tutorials to walk you through example programs that come with the download.
The source code files for the example are listed below.
The example program requires a database table named
bonus
with the following fields:
CREATE TABLE BONUS SOCSEC Integer, BONUS Real, primary key (SOCSEC)
The source code follows naming conventions so the EJB Home Server can find the application classes, containers, and database table. For example with The source for a given enterprise Bean, the enterprise Bean, home interface, and primary key class names use the remote interface name as a prefix.
Other naming conventions are used within the source code and discussed below where they appear. While source file-naming conventions are standard across EJB Server implementations, naming conventions used within the source are specific to the EJB Server or deployment tool you use.
BonusServlet.java
.getBonus
getSocSec
addBonus
create
findByPrimaryKey
int bonus
int socsec
getBonus
getSocSec
addBonus
ejbCreate
BonusPK.java
, primary key with these fields and methods:
socsec
BonusPK
Calc.java
, the remote interface with this method:
calcBonus
CalcHome.java
, the home interface with this method:
create
CalcBean.java
, an enterprise Bean with these public fields and methods:
int bonus
int calcBonus
ejbCreate
When the entity and session Beans are deployed, a number of container source and class files are generated with the following prefixes. A container is a set of classes generated by the deployment tool that manages an enterprise Bean's persistence, transactional properties, and security.
The thin-client BonusServlet.java declares variables and parameter values of specific types to locate the database table, create the home interface, and invoke the entity and session Bean methods.
Note: To keep the code simple, data values that the servlet would normally receive from user input to a browser form is hard coded.
These are the import and declarations statements.
The entity Bean remote interface type is Bonus
and maps to the database table name, which is also
Bonus
.
The socsec
and bonus
variables
map to the fields in the database table. Variable names
that map to the database table or fields in the database are
case insensitive.
import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.Properties; import javax.naming.*; //location of Bean classes import com.ejbhome.examples.*; public class BonusServlet extends HttpServlet { //Entity Bean home and remote interface variables BonusHome homebonus; Bonus theBonus, rec1; //Session Bean home and remote interface variables CalcHome homecalc; Calc theCalculation; int socsec = 0, retsocsec = 0; int bonus = 0, retbonus = 0;
The EJB Home Server uses the beans.properties
configuration file
to map enterprise Beans to the home interfaces for their respective container
classes. Here is how the mapping looks. The container classes are not created
until the Beans are deployed, but you can put the entry into this file
before you deploy because the naming convention is consistent.
The values calcs
and bonuses
are used
in the next code segment for the enterprise Bean lookup. The
values in this file and the strings used in the code have to match
exactly so the EJB Home Server can look up the enterprise Bean home
interface.
calcs=com.ejbhome.examples.EJBHomeCalcHome bonuses=com.ejbhome.examples.EJBHomeBonusHome
Here is the servlet init
method implementation. The
JNDI API
is used to look up the Beans and containers.
public void init(ServletConfig config) throws ServletException{ try{ Properties env = new Properties(); env.put("java.naming.factory.initial", "com.ejbhome.naming. //spi.rmi.RMIInitCtxFactory"); Context ctx = new InitialContext(env); //Look up home interfaces and containers //for entity and session beans. //Strings map to settings in the beans.properties file. homebonus = (BonusHome)ctx.lookup("bonuses"); homecalc = (CalcHome)ctx.lookup("calcs"); } catch (Exception NamingException) { NamingException.printStackTrace(); } }
In the next code segment, the socsec
and bonus
variables are initialized with values that would normally come from user
input to a form in the browser. These values are used to create the entity Bean
home interface and two records
in the Bonus
table. After each record is
created, its data is retrieved from the database and displayed
in the browser. For simplicity, the code to display the data in
the browser is not
included in the segment, but here is the entire source file for the
BonusServlet.java servlet.
try{ socsec = 555220000; bonus = 200; for(int i = 1; i < 3; i++){ // Use the entity Bean home interface to // create a record theBonus = homebonus.create(socsec, bonus); // Get the data from the record and display // it in the browser retbonus = theBonus.getBonus(); retsocsec = theBonus.getSocSec(); socsec += 1; } } catch (Exception CreateException) { CreateException.printStackTrace(); }
This next code segment uses the primary key class to retrieve a record from the database, and uses the home interface to retrieve the data in the record fields. For simplicity, the code to display the retrieved data in the browser is not included in the segment, but here is the entire source file for the BonusServlet.java servlet.
try{ //Instantiate a primary key object BonusPK PK = new BonusPK(555220000); //Locate a record with the primary key rec1 = hmebonus.findByPrimaryKey(PK); //Use the entity Bean home interface //to retrieve current bonus value retbonus = rec1.getBonus();
This next code segment creates the
session Bean's home interface,
and uses the home interface to call
the session Bean's
calcBonus
method.
The hard-coded values passed to the
calcBonus
method would
normally be received
by user input to a form in the browser.
For simplicity, the code to display the final bonus in
the browser is not
included in the segment, but here is the
entire source file for the
BonusServlet.java servlet.
//Calculate bonus int base = 100; int perf=5, company=2; theCalculation = homecalc.create(); int calc = theCalculation.calcBonus( base, perf, company); System.out.println(calc); rec1.addBonus(calc); retbonus = rec1.getBonus(); } catch (Exception FinderException) { FinderException.printStackTrace(); }
The thin-client servlet produces the following output. Of course, you need the entity and session Beans compiled and deployed before the application will actually work. The next sections describe the source files for the enterprise Beans and enterprise Bean deployment.
Record 1
Soc Sec: 555220000
Bonus Total: 200
Record 2
Soc Sec: 555220001
Bonus Total: 200
Calculate Bonus for 555220000
Bonus Before: 200
Bonus After: 1200
The example entity Bean represents a row in the Bonus
table. At runtime, there can be any number of simultaneous entity Bean
instantiations to represent different rows in the table. The thin-client
servlet instantiates two entity Beans to create the two rows in the
Bonus
table.
socsec (Primary Key) | bonus |
---|---|
555220000 | 200 |
555220001 | 200 |
However, the thin-client servlet does not work directly with the entity Bean
to create rows or access data, but creates an instance of the
home interface. The home interface extends
EJBHome
and has methods that define how the entity Bean is
created and located in its container. It also follows the rules of
the Remote Method Invocation (RMI) API in that the arguments and return
types for each method must be serializable and the methods should throw
java.rmi.RemoteException
as one of its exception.
package com.ejbhome.examples; import javax.ejb.*; import java.rmi.*; public interface BonusHome extends EJBHome { Bonus create(int bonus, int socsec) throws CreateException, RemoteException; Bonus findByPrimaryKey(BonusPK socsec) throws FinderException, RemoteException; }
When the home interface is instantiated, the EJB Home Server also creates
the remote interface and enterprise Bean instances. Here is the
remote interface. It extends
EJBObject
and declares the BonusBean
methods.
It also follows the rules of the RMI API in that the arguments and return
types for each method must be serializable and the methods should throw
java.rmi.RemoteException
as one of its exception.
package com.ejbhome.examples; import javax.ejb.*; import java.rmi.*; public interface Bonus extends EJBObject { int getBonus() throws RemoteException; int getSocSec() throws RemoteException; void addBonus(int bonus ) throws RemoteException; }
The EJBHome server requires a container-managed entity Bean to have a primary key class with a public primary key field (or fields, if using composite primary keys). You can have the container manage an enterprise Bean or write the code to manage the Bean yourself. In this example, both Beans are container-managed, and you should always let the container manage an entity Bean.
Here is the primary key class. The primary key in the Bonus
table is socsec
, and so socsec
is a public
field in this class that is assigned a value when the class is
constructed.
package com.ejbhome.examples; public class BonusPK implements java.io.Serializable { public int socsec; public BonusPK(int socsec) { this.socsec = socsec; } public BonusPK() {} }
Now for a look at the entity bean
source code. It implements EntityBean
and the
developer-defined and interface methods. It should follow the rules of
the RMI API in that the arguments and return
types for each method must be serializable and the methods should throw
java.rmi.RemoteException
as one of its exception.
package com.ejbhome.examples; import java.rmi.RemoteException; import javax.ejb.*; public class BonusBean implements EntityBean { public int bonus = 0; public int socsec = 0; protected EntityContext ctx; public int getBonus() throws RemoteException { return bonus; } public int getSocSec() throws RemoteException { return socsec; } public void addBonus(int bonus) throws RemoteException { this.bonus += bonus; } public void ejbCreate(int socsec, int bonus) throws CreateException, RemoteException { this.socsec=socsec; this.bonus=bonus; } // Other interface methods public void setEntityContext( javax.ejb.EntityContext ctx) throws RemoteException { this.ctx = ctx; } public void unsetEntityContext() throws RemoteException { ctx = null; } public void ejbRemove() throws RemoteException, RemoveException { } public void ejbActivate() throws RemoteException { } public void ejbPassivate() throws RemoteException { } public void ejbLoad() throws RemoteException { } public void ejbStore() throws RemoteException { } }
The session Bean performs a simple calculation. Its source code is similar to the entity Bean source code with the few differences described here.
The home interface
does not have a FindByPrimaryKey
method because no database access is involved. A session Bean
could perform database access, and would then need a
FindByPrimaryKey
method, but the simple example does not
need it.
package com.ejbhome.examples; import javax.ejb.*; import java.rmi.*; public interface CalcHome extends EJBHome { Calc create() throws CreateException, RemoteException; }
The remote interface declares the session Bean's one method.
package com.ejbhome.examples; import javax.ejb.*; import java.rmi.*; public interface Calc extends EJBObject { int calcBonus(int base, int perf, int company) throws RemoteException; }
The session Bean
implements SessionBean
and
the developer-defined and interface methods.
package com.ejbhome.examples; import java.rmi.RemoteException; import javax.ejb.*; public class CalcBean implements SessionBean throws Remote Exception { public int bonus; protected SessionContext ctx; public int calcBonus(int base, int perf, int company) { int calc = (base*perf*company); this.bonus += calc; return this.bonus; } public void ejbCreate() throws CreateException, RemoteException {} // Other interface methods public void setSessionContext( javax.ejb.SessionContext ctx) throws RemoteException { this.ctx = ctx; } public void ejbRemove() throws RemoteException { } public void ejbActivate() throws RemoteException { } public void ejbPassivate() throws RemoteException { } public void ejbLoad() throws RemoteException { } public void ejbStore() throws RemoteException { } }
The Deployer tool generates these container classes for BonusBean
and CalcBean
.
BonusBean | CalcBean |
---|---|
EJBHomeBonusBean.class EJBHomeBonusBean.java EJBHomeBonusHome.class EJBHomeBonusHome.java EJBHomeBonusHome_Skel.class EJBHomeBonusHome_Skel.java EJBHomeBonusHome_Stub.class EJBHomeBonusHome_Stub.java EJBHomeBonusMetaData.class EJBHomeBonusMetaData.java EJBHomeRemoteBonus.class EJBHomeRemoteBonus.java EJBHomeRemoteBonus_Skel.class EJBHomeRemoteBonus_Skel.java EJBHomeRemoteBonus_Stub.class EJBHomeRemoteBonus_Stub.java | EJBHomeCalcBean.class EJBHomeCalcBean.java EJBHomeCalcHome.class EJBHomeCalcHome.java EJBHomeCalcHome_Skel.class EJBHomeCalcHome_Skel.java EJBHomeCalcHome_Stub.class EJBHomeCalcHome_Stub.java EJBHomeCalcMetaData.class EJBHomeCalcMetaData.java EJBHomeRemoteCalc.class EJBHomeRemoteCalc.java EJBHomeRemoteCalc_Skel.class EJBHomeRemoteCalc_Skel.java EJBHomeRemoteCalc_Stub.class EJBHomeRemoteCalc_Stub.java |
If you want to develop thin-client multitiered applications for the net, Enterprise JavaBeans is the way to go. You can write business applications with reusable and modular enterprise Beans and leave the system-level programming to the underlying architecture. You will not be alone--the Enterprise JavaBeans architecture has been adopted, supported, and put on the product road maps of more than 25 companies.
Here are links to additional information on Enterprise JavaBeans.
An Introduction to Enterprise JavaBeans Technology provides information on how enterprise Beans fit into the Enterprise JavaBeans architecture.
EJB Specification describes the Enterprise JavaBeans architecture in great detail.
Beginner's Guide to Enterprise JavaBeans in the October 1998 JavaWorld.
How to Write a Session EJB in the July 1998 JavaWorld.
© 1994-2005 Sun Microsystems, Inc.