Download Java 2 Enterprise Edition

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Java 2 Enterprise Edition
Case Study
CPSC550 Graduate Student Seminar
Spring 2004
Jeffrey A. Brown
March 15, 2004
History
Sun Microsystems announced plans for the unveiling of its Java 2 Platform, Enterprise
Edition (J2EE) in March 1999 with the details of the new platform to follow shortly.
J2EE is built on the Java 2 Platform, Standard Edition (J2SE), and takes advantage of
many of its built-in class libraries.
The J2EE platform uses the concept of the J2EE container, which exists to service
components instantiated within it. This allows the J2EE programmer to focus more on the
business logic of the application and less on the underlying “plumbing”.
The precursor of the J2EE container is the transaction processor. The transaction
processor was created to solve the problem of handling many concurrent users in an
efficient manner. It guarantees transactional integrity and manages the execution of
programs and the sharing of resources. A transaction is when several statements are
executed as a logical unit; either all of the statements execute successfully or none of
them are executed. The primitives that are used to control a transaction are BEGIN,
COMMIT, and ROLLBACK. In addition, the transaction manager can maintain a pool of
running program instances with the size of the pool based on the current user load. This
allows a system to support many more concurrent users than is possible if providing
duplicate resources for each user.
The J2EE platform is designed in a modular way and takes advantage of “best practices”
devised over the course of the history of interactive computing. The interactive
computing paradigm started with a mainframe computer containing all of the programs
and related data and a vast array of dumb terminals. With the advent of the PC, the client
machine could process both presentation and business logic, relieving some of the work
of the server. This model came to be referred to as the client/server or two-tier model.
The client would provide a request to the server, which would process the request and
return a result back to the client. Unfortunately, this model required the redistribution of
client code to each client machine when any of the business logic or presentation logic
changed. Finally, the business logic was separated out into its own tier resulting in the
three-tier model.
The components of the J2EE platform reside in one of these three tiers. JavaServer Pages
(JSP) and Servlets are presentation tier components and live in the web tier. Enterprise
JavaBeans (EJB) are business logic components and live in the middle (or business) tier.
The persistent data for the applications live in the enterprise integration (or data) tier and
are often stored in relational databases (RDBMS) or legacy systems.
Goal
The development of enterprise applications is hard work and unfortunately, as a result, is
easy to get wrong. There are many underlying services, sometimes referred to as
“plumbing” or infrastructure that need to be considered including the following:
1. Security.
2. Transactional processing.
3. Data persistence.
4. Concurrency of users.
5. Multiple client types.
6. Interoperability with legacy systems.
The goal of J2EE is to simplify the development of enterprise applications by taking
advantage of the benefits of component technology. A component is a “managed” object.
It lives within a container that provides its context and the underlying services that it
needs to operate properly.
In essence, we don’t need to “reinvent the wheel” for many of the underlying services
that we need when creating an enterprise application. Therefore, we can build a more
robust, scalable system faster and cheaper. This is a good thing!
Definitions
JavaServer Pages (JSP)
Provides the ability to “put snippets of Java code directly into a text-based document. A
JSP page is a text-based document that contains two types of text: static template data,
which can be expressed in any text-based format such as HTML, WML, and XML, and
JSP elements, which determine how the page constructs dynamic content.”
Servlets
Provides the ability to “define HTTP-specific servlet classes. A servlet class extends the
capabilities of servers that host applications accessed by way of a request-response
programming model.”
Java Messaging Service (JMS)
“A messaging standard that allows J2EE application components to create, send, receive,
and read messages. It enables distributed communication that is loosely coupled,
reliable, and asynchronous.”
Enterprise JavaBeans (EJB)
A component that is used to implement the business logic of an enterprise application.
They execute within a “container” which provides numerous services that simplify their
development.
Java Naming and Directory Interface (JNDI)
“Provides naming and directory functionality. It provides applications with methods for
performing standard directory operations, such as associating attributes with objects and
searching for objects using their attributes”.
Remote Method Invocation (RMI)
Provides the ability for “an object running in one Java Virtual Machine (JVM) to invoke
methods on an object running in another JVM. RMI provides for remote communication
between programs written in the Java programming language”.
Java API for XML – Remote Procedure Call (JAX-RPC)
“Uses the SOAP standard and HTTP so client programs can make XML-based remote
procedure calls (RPCs) over the Internet. JAX-RPC also supports WSDL so you can
import and export WSDL documents”.
SOAP with Attachments API for Java (SAAJ)
Provides “a low-level API upon which JAX-RPC depends. It enables the production and
consumption of messages that conform to the SOAP 1.1 specification and SOAP with
Attachments note”.
Java API for XML - Registries (JAXR)
Provides ability to “access business and general-purpose registries over the Web. JAXR
supports the ebXML Registry/Repository standards and the emerging UDDI
specifications. By using JAXR, developers can learn a single API and get access to both
of these important registry technologies”.
Java Management Extension (JMX)
Provides the tools “for building distributed, Web-based, modular and dynamic solutions
for managing and monitoring devices, applications, and service-driven networks”.
Common Object Request Broker Architecture (CORBA)
An open, vendor-independent architecture and infrastructure created by the Object
Management Group (OMG) that allows a CORBA-based program from any vendor, on
almost any computer, operating system, programming language, and network, can
interoperate with another CORBA-based program from the same or another vendor, on
almost any other computer, operating system, programming language, and network.
Features
There are a number of features that are supported in the J2EE platform including the
following:
1. Distributed processing
2. Component life-cycle management
3. Transaction processing
4. Pooling of numerous resource types
5. Security management
6. Data persistence
7. Interoperability with legacy systems
Although the J2EE platform can be used to create distributed applications. This is not
always a good idea. This is further explored in the “Significant Points” section below.
Component life-cycle management includes the instantiation, initialization, processing,
and destruction of the component. In some cases, when pooling EJB components, which
include session and entity beans, this list is augmented by passivation and activation as
well. Passivation is the process of serializing a component so that it can be removed from
the pool. Activation is the reverse process. The component is deserialized from storage
and brought back into the pool for subsequent processing. Finally, when dealing with
entity beans, store and load are included. Before each transaction, the J2EE container will
call the appropriate entity bean(s) to load its current state from the persistent store. After
the transaction has completed, the J2EE container will call the appropriate entity bean(s)
to write out their current state to the persistent store.
A transaction is when several statements are executed as a logical unit; either all of the
statements execute successfully or none of them are executed. The primitives that are
used to control a transaction are BEGIN, COMMIT, and ROLLBACK. The J2EE
container can be configured using an external file, known as the deployment descriptor,
so that it knows what methods are to be executed in a transaction. When a client invokes
one of these designated methods, the J2EE container intercepts that call and makes sure
that the appropriate transactional constraints are enforced. This eliminates the need to
place transactional demarcation in the methods, which helps keep the method focused on
the business logic at hand.
In addition to using the deployment descriptor to configure the transactional aspects of
the J2EE container for a particular application, the deployer can also use it to configure
the necessary security constraints of the application. This includes setting up application
users, groups, and roles and then designating which user/role combination(s) can execute
which methods. Once again, this allows the J2EE developer to keep non-business-related
concerns out of the component’s logic.
Structure
The overall architectural structure of the J2EE platform is illustrated in the following
diagram:
Notice how the J2EE components are built on top of the Java 2 Standard Edition (J2SE).
Version 1.4 of the J2EE platform introduces both web services (i.e. JAX-RPC, SAAJ,
and JAXR) and component management (i.e. JMX). The overall J2EE platform is
composed of two parts: the web container and the EJB container. As a whole, these two
parts are referred to as the J2EE container.
How To Use
This section of the case study introduces a simple example used to show how to create
EJB session beans, entity beans, and a remote client that accesses them. The example
illustrates how EJB technology can be used to implement the business logic necessary to
make a reservation on a cruise line.
Cabin Entity Bean Component
We’ll start with the EJB entity bean component, which is made up of a remote interface,
a home interface, a bean implementation class, and a primary key class.
The remote interface is named Cabin and it extends javax.ejb.EJBObject. The code is
given below:
package com.titan.cabin;
import java.rmi.RemoteException;
public interface Cabin extends javax.ejb.EJBObject {
public String getName() throws RemoteException;
public void setName(String str) throws RemoteException;
public int getDeckLevel() throws RemoteException;
public void setDeckLevel(int level) throws RemoteException;
public int getShip() throws RemoteException;
public void setShip(int sp) throws RemoteException;
public int getBedCount() throws RemoteException;
public void setBedCount(int bc) throws RemoteException;
}
This interface defines the business method API for the entity bean component. In general,
an entity bean’s business methods consist of simple getters and setters. We need to
remember that the purpose of the entity bean component is to provide an object-oriented
view of persistent data.
The home interface is named CabinHome and it extends javax.ejb.EJBHome. The code is
given below:
package com.titan.cabin;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
public interface CabinHome extends javax.ejb.EJBHome {
public Cabin create(int id) throws CreateException,
RemoteException;
public Cabin findByPrimaryKey(CabinPK pk) throws
FinderException,
RemoteException;
}
The methods in the home interface are mandated by the javax.ejb.EJBHome interface and
must be included. If desired, the J2EE programmer can add additional finder methods.
The bean implementation class is named CabinBean and it extends javax.ejb.EntityBean.
The code is given below:
package com.titan.cabin;
import javax.ejb.EntityContext;
public class CabinBean implements javax.ejb.EntityBean {
public
public
public
public
public
int id;
String name;
int deckLevel;
int ship;
int bedCount;
public CabinPK ejbCreate(int id){
this.id = id;
return null;
}
public void ejbPostCreate(int id){
// Do nothing. Required.
}
public String getName(){
return name;
}
public void setName(String str){
name = str;
}
public int getShip(){
return ship;
}
public void setShip(int sp) {
ship = sp;
}
public int getBedCount(){
return bedCount;
}
public void setBedCount(int bc){
bedCount = bc;
}
public int getDeckLevel(){
return deckLevel;
}
public void setDeckLevel(int level ){
deckLevel = level;
}
public void setEntityContext(EntityContext ctx){
// Not implemented.
}
public void unsetEntityContext(){
// Not implemented.
}
public void ejbActivate(){
// Not implemented.
}
public void ejbPassivate(){
// Not implemented.
}
public void ejbLoad(){
// Not implemented.
}
public void ejbStore(){
// Not implemented.
}
public void ejbRemove(){
// Not implemented.
}
}
The bean implementation class provides the actual business logic implementation. In
addition, there are other methods included above, setEntityContext(),
unsetEntityContext(), ejbActivate(), ejbPassivate(), ejbLoad(), and ejbStore() which are
mandated by the javax.ejb.EntityBean interface and must be included. The method
setEntityContext() is called by the J2EE container to provide the bean with its context
information. If the J2EE container needs to make space in the pool, it calls ejbPassivate()
to allow the bean to perform any logic necessary before it is serialized to secondary
storage. When it’s time to bring the bean back into the pool for further processing, the
J2EE container calls the ejbActivate() method. The ejbLoad() and ejbStore() method pair
is used to allow the bean to synchronize its current state with the underlying persistent
store.
The primary key class is named CabinPK and it extends java.io.Serializable. The code is
given below:
package com.titan.cabin;
public class CabinPK implements java.io.Serializable {
public int id;
public int hashCode( ){
return id;
}
public boolean equals(Object obj){
if(obj instanceof CabinPK)
return (id == ((CabinPK)obj).id);
return false;
}
public String toString(){
return String.valueOf(id);
}
}
The J2EE container uses the primary key class to guarantee uniqueness among the EJB
entity beans located in the underlying persistence store.
TravelAgent Session Bean Component
Next we’ll look at the EJB session bean component, which is made up of a remote
interface, a home interface, a bean implementation class. Since session beans don’t
represent persistent data, a primary class key in not needed.
The remote interface is named TravelAgent and it extends javax.ejb.EJBObject. The code
is given below:
package com.titan.travelagent;
import java.rmi.RemoteException;
import javax.ejb.FinderException;
public interface TravelAgent extends javax.ejb.EJBObject {
// String elements follow the format "id, name, deck level"
public String [] listCabins(int shipID, int bedCount)
throws RemoteException;
}
In essence, we can think of the session bean as the real business logic component. Each
of its methods relate to a use case as defined in the requirements of the associated
application. In our case with this example, the use case deals with the requirement of the
system to allow a client to get a list of cabins in the specified ship having the specified
bed count.
The home interface is named TravelAgentHome and it extends javax.ejb.EJBHome. The
code is given below:
package com.titan.travelagent;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface TravelAgentHome extends javax.ejb.EJBHome {
public TravelAgent create()
throws RemoteException, CreateException;
}
The methods defined in the home interface are mandated by the javax.ejb.EJBHome
interface and must be included.
The bean implementation class is named TravelAgentBean and it extends
javax.ejb.SessionBean. The code is given below:
package com.titan.travelagent;
import
import
import
import
import
import
import
import
import
com.titan.cabin.Cabin;
com.titan.cabin.CabinHome;
com.titan.cabin.CabinPK;
java.rmi.RemoteException;
javax.naming.InitialContext;
javax.naming.Context;
java.util.Properties;
java.util.Vector;
javax.ejb.EJBException;
public class TravelAgentBean implements javax.ejb.SessionBean {
public void ejbCreate() {
// Do nothing.
}
public String [] listCabins(int shipID, int bedCount)
throws EJBException{
try {
javax.naming.Context jndiContext =
new InitialContext();
Object obj = jndiContext.lookup("ejb/CabinHome");
CabinHome home = (CabinHome)
javax.rmi.PortableRemoteObject.narrow(obj,
CabinHome.class);
Vector vect = new Vector();
CabinPK pk = new CabinPK();
Cabin cabin;
for(int i = 1; ; i++){
pk.id = i;
try {
cabin = home.findByPrimaryKey(pk);
}
catch(javax.ejb.FinderException fe){
break;
}
// Check for bed count and ship ID match.
if (cabin.getShip() == shipID &&
cabin.getBedCount() == bedCount){
String details =
i+","+cabin.getName()+","+cabin.getDeckLevel();
vect.addElement(details);
}
}
String [] list = new String[vect.size()];
vect.copyInto(list);
return list;
} catch(javax.naming.NamingException ne){
throw new EJBException(ne);
} catch(java.rmi.RemoteException re){
throw new EJBException(re);
}
}
private javax.naming.Context getInitialContext()
throws javax.naming.NamingException{
Properties p = new Properties();
// ... Specify the JNDI properties specific to the vendor.
return new javax.naming.InitialContext(p);
}
public
public
public
public
void
void
void
void
ejbRemove(){}
ejbActivate(){}
ejbPassivate(){}
setSessionContext(javax.ejb.SessionContext cntx){}
}
The bean implementation class provides the actual logic that implements the session
bean’s business logic. The first step is to lookup the home interface of the appropriate
entity bean. In this case, it is referred to as “ejb/CabinHome”. In order to do this, the class
uses the Java Naming and Directory Interface (JNDI), which provides a common
abstraction layer for retrieving components. Once we have a handle to the home
interface, we can iterate through each of the Cabin entity bean components and determine
which ones meet the specified criteria. When one is found, we call two of its getter
methods, getName() and getDeckLevel(), in order to create a listing of cabins that we can
return to the client.
Remote Client
Finally we’ll look at the remote client itself. The code is given below:
package com.titan.travelagent;
import
import
import
import
import
import
import
import
import
com.titan.cabin.CabinHome;
com.titan.cabin.Cabin;
com.titan.cabin.CabinPK;
javax.naming.InitialContext;
javax.naming.Context;
javax.naming.NamingException;
javax.ejb.CreateException;
java.rmi.RemoteException;
java.util.Properties;
public class Client {
public static int SHIP_ID = 1;
public static int BED_COUNT = 3;
public static void main(String [] args){
try {
Context jndiContext = getInitialContext();
Object obj =
jndiContext.lookup("ejb/TravelAgentHome");
TravelAgentHome home = (TravelAgentHome)
javax.rmi.PortableRemoteObject.narrow(obj,
TravelAgentHome.class);
TravelAgent reserve = home.create();
// Get a list of all cabins on ship 1 with a
// bed count of 3.
String list [] =
reserve.listCabins(SHIP_ID,BED_COUNT);
for(int i = 0; i < list.length; i++){
System.out.println(list[i]);
}
} catch(java.rmi.RemoteException re){
re.printStackTrace();
} catch(Throwable t){t.printStackTrace();}
}
static public Context getInitialContext() throws Exception {
Properties p = new Properties();
// ... Specify JNDI properties specific to the vendor.
return new InitialContext();
}
}
The remote client also uses JNDI. This time to lookup the home interface of the session
bean named “ejb/TravelAgentHome”. It then uses the create() method to make a new
TravelAgent session bean component. The J2EE container provides the appropriate stub
object, which hides all of the distributed “plumbing” and allows the client to treat the
remote bean as a local object. At this point, the client simply invokes the listCabins()
method and then iterates through the returned list of cabin details printing it to the
console for inspection.
Applications
There are two common applications for the J2EE platform. The first is the web
application as illustrated in the diagram below.
The components of the web application physically reside within the J2EE web container
that is one part of the overall J2EE container. In general, this architecture consists of
Servlet/JSP components, a business interface, and the implementation of the business
interface using “plain old Java objects” (POJO). In order to maintain a separation of
concerns, the Servlet components are used to provide the navigational logic while the JSP
components provide the presentational logic (i.e. the view). There shouldn’t be any
business logic in either the Servlet or JSP components. All business logic should be in the
POJO layer.
The strengths of this approach include:
1.
2.
3.
4.
Simplicity
Speed
Easy to test
Scales well
The weaknesses of this approach include:
1. Only supports a web interface
2. Can’t use EJB transaction support
3. No built-in support for concurrent programming
The second is the distributed application as illustrated in the diagram below.
The components of the distributed application physically reside within both the J2EE web
container and the EJB container. In general, this architecture consists of Servlet/JSP
components, a business interface, and the implementation of the business interface using
EJB components. The design of the Servlet and JSP components is the same as above.
The major difference in this architectural approach is in the implementation of the
business interface, which allows access to the business logic through either the web
components or through remote components using Remote Method Invocation (RMI).
When using RMI, the accessing component can either be implemented using Java or
Common Object Request Broker Architecture (CORBA), providing numerous options to
the J2EE developer.
The introduction of the session bean component provides a distributive mechanism for
accessing the business logic in a container-provided transactional manner. The web
application approach above doesn’t get to take advantage of transactional services
provided by the container. In this case, the J2EE developer is responsible for managing
the transactions. In fact, this highlights one of the benefits of using EJB technology; the
J2EE developer can rely on the container to manage transactional constraints without
introducing it into the business logic itself.
An optional component of this approach is the introduction of the entity bean component.
Unfortunately, this may end up be more trouble than its worth.
The strengths of this approach include:
1. Supports all J2EE client types
2. Provides a shared middle tier
3. Permits distribution of application components across different physical servers
The weaknesses of this approach include:
1. Complex
2. Performance overhead
3. Hard to test and debug
Significant Points
J2EE simplifies enterprise development but it's still much harder than developing with
“plain old Java objects” (POJO). Since JSPs, Servlets, and EJBs are all components, it is
necessary to maintain not only the associated logic but the deployment descriptors as
well. Fortunately, tools like XDoclet and EJBGen help lessen this burden.
In addition, components are “managed” objects and therefore must reside and execute
within a container. This makes the job of both testing and debugging much harder since
the services supplied by the container must be factored in to the testing framework.
Sometimes, in simple cases, these services can be stubbed out but this isn’t always
possible. In these more difficult cases, the testing framework must support the ability to
test within the container itself.
The biggest question that a J2EE developer must face is whether to take a distributed or
non-distributed approach for the design and implementation of the enterprise application.
Although the J2EE platform can be used to create distributed applications, this isn’t
always the best approach to take. We can develop an extremely scalable application by
using the features of the web container alone. EJBs are not a requirement for developing
scalable systems. When taking the non-distributed approach it is important to use EJB
local interfaces. Unfortunately, the J2EE developer needs to make a conscience decision
on whether to use an EJB local interface and/or an EJB remote interface. The original
EJB specification only provided for EJB remote interfaces and therefore whenever an
EJB component was used, even when residing within the same J2EE container, RMI was
required for inter-component communication. The current version of the J2EE
specification allows for local interfaces as well as remote interfaces, which then treat
inter-component communication like a local in-process method call. This is obviously a
real boon in terms of minimizing performance overhead. However, it would be even
better if the EJB container could determine whether to use local or remote interfaces
based on context!
Another mantra of J2EE application development is to design for scalability from start to
finish. It is important to keep state requirements for the application small to non-existent
since stateless components are much more scalable! Sometimes this is impossible since
HTTP is a stateless protocol and applications often require state to be maintained. In
general, the rule of thumb is to keep state requirements as small as possible.
Summary
It is hard to create enterprise applications because of their complexity. J2EE helps
simplify the development of enterprise applications. The J2EE platform consists of
numerous components including JSP, Servlets, EJB, and JMS. JSP and Servlet
components reside in the web tier. EJB components reside in the business tier. JMS is
used as a messaging transport for communication between components. Finally, the J2EE
platform provides a number of helpful underlying services including life-cycle
management, transaction processing, resource pooling, security, and data persistence.
References
1. Java 2 Platform Enterprise Edition Specification, v1.4
http://java.sun.com/j2ee/j2ee-1_4-fr-spec.pdf
2. The J2EE 1.4 Tutorial
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html
3. Enterprise JavaBeans, 3rd Edition
Richard Monson-Haefel
O'Reilly & Associates
ISBN 0596002262
4. Enterprise JavaBeans: Developing Component-Based Distributed Applications
Thomas C. Valesky
Addison-Wesley
ISBN 0201604469
5. Expert One-on-One J2EE Design and Development
Rod Johnson
WROX Press
ISBN 0764543857
6. Core J2EE Patterns: Best Practices and Design Strategies
Deepak Alur, et. al.
Prentice Hall
ISBN 0130648841