| CONTENTS | PREV | NEXT | INDEX | Designing Enterprise Applications with the J2EETM Platform, Second Edition |
The J2EE application programming model for EIS integration defines a set of design choices, guidelines, and recommendations for application component providers. These guidelines enable an application component provider to develop an application based on its overall functional and system requirements. The application programming model focuses on the following aspects:
The following sections describe each of these aspects from the perspective of relational database access using JDBC API, with the exception of transactions, which are discussed in Chapter 8. An important point to note is that the following sections are not meant to be a programmer's guide to using the JDBC API. While the following discussions refer to the JDBC API, the concepts are generic and apply to any client APIs (including the Common Client Interface) supported by resource adapters.
When an application requires access to an enterprise information system, an application component provider is responsible for developing code to access resources managed by the enterprise information system, including tables, stored procedures, business objects, and transaction programs. The application component provider also has to write the business and application logic when developing functionality for applications that target enterprise information system.
In the J2EE programming model, a container assumes primary responsibility for managing connection pooling, transactions, and security. The level of service provided is based on the declarative specification of the application requirements by an application component provider or deployer. This leaves an application component provider to concentrate on developing code to access the data and functions managed by an enterprise information system.
Application development tools can simplify EIS integration. By supporting end-to-end application development, tools also minimize the difficulties of working with vendor-specific client APIs. Different tools provide different functionality within the application development process. Generally, tools can be divided into these functional areas:
- Data and function mining tools, which enable application component providers to look at the scope and structure of data and functions in an existing information system
- Object-oriented analysis and design tools, which enable application component providers to design an application in terms of enterprise information system functionality
- Application code generation tools, which generate higher level abstractions for accessing data and functions. A mapping tool that bridges different programming models, such as an object to relational mapping, falls into this category.
- Application composition tools, which enable application component providers to compose application components from generated abstractions (such as those described in previous bullets). These tools typically use the JavaBeans component model to enhance ease of programming and composition.
- Deployment tools, which are used by application component providers and deployers to set transaction, security, and other deployment time requirements
A component can access data and functions in an enterprise information system in several ways, either directly by using the appropriate client API or indirectly by abstracting the complexity and low-level details of an EIS access API into higher level access objects. Access objects can take different forms--they can be data access objects (as described in Chapter 5), command beans, or records. There are advantages to using access objects, as follows:
- An access object can adapt a low-level API for accessing EIS data and functions to an easy-to-use API that is consistent across multiple types of enterprise information systems. For example, an access object may follow a design pattern that maps EIS function parameters to setter methods and return values to getter methods. The application component provider uses an EIS function by first calling the appropriate setter methods to set up the parameters, then calling the method corresponding to the EIS function, and finally calling the getter methods to retrieve the results.
- An access object facilitates a component's ability to adapt to different EIS resources. For example, a component can use an access object to adapt its persistent state management to a different database schema or to a different type of database.
- A component can be composed from access objects that support the JavaBeans model or, by using application development tools, the component can be linked with generated access objects. This simplifies the application development effort.
Access objects primarily provide a programming technique to simplify application development. Because of this, we recommend that they be used whenever an application component provider needs to access data or functions in an EIS. In some cases, tools may be available to generate such access objects. In other cases, the application component provider may need to hand-code an access object.
6.4.3.1 Types of Access ObjectsThere are different types of access objects, and these different access objects are used for different purposes. This section highlights the different access objects and their purposes.
6.4.3.1.1 Command BeansAn access object can encapsulate one or more EIS functions, such as business functions or stored procedures. This type of access object is referred to as a command bean. An application component uses a command bean to interact with a resource adapter and execute an EIS function. The command bean is associated with a remote EIS function, hiding the low-level programming aspects of accessing the function. The application component, rather than having to program through the CCI or EIS client-side API, instead accesses the EIS function by programming to the command bean's interface.
Code Example 6.1 implements a command bean that drives a purchase requisition business process in an enterprise resource planning system by mapping purchasing functions to method calls on a PurchaseFunction object.
PurchaseFunction pf = // instantiate access object for
// PurchaseFunction
// set fields for this purchase order
pf.setCustomer ("Wombat Inc");
pf.setMaterial (...);
pf.setSalesOrganization (...);
pf.execute ();
// now get the result of purchase requisition using getter methods
| Code Example 6.1 Command Bean Code |
When an access object encapsulates access to persistent data, such as that stored in a database management system, it is called a data access object (DAO). Often, tools generate data access objects based on the database schema. Data access objects can provide a consistent API across different types of database management systems. The sample application uses data access objects that correspond to order objects stored in different types of databases.
The principal advantage of data access objects is that they decouple the user of the object from the programming mechanism that accesses the underlying data. The DAO exposes the same interface to its clients regardless of the API it uses to access the EIS data. Even changes to the schema or function specification of the EIS may not impact the DAO's user interface. This means that the user's programming model does not have to change when EIS access mechanisms change or there are modifications to the EIS schema.
Code Example 6.2 shows a data access object that provides access to products in a product catalog. This product catalog is stored in a non-relational database. CatalogDAO is an object that provides a simple interface for getting products in the product catalog. CatalogDAO extends a DAO base class that may be specific to a tool or an EAI framework.
public class CatalogDAO extends com.example.tool.DAO {
private RecordFactory rf;
public CatalogDAO(Connection cx, RecordFactory rf) {
super(cx);
this.rf = rf;
}
public Collection getAllProducts() throws DAOException {
try {
MappedRecord input =
rf.createMappedRecord("PRODUCT_INPUT_RECORD");
input.put("ORDER-ID", "*");
IndexedRecord output =
rf.createIndexedRecord("PRODUCT_INFO_RECORD");
InteractionSpecImpl ixSpec = new InteractionSpecImpl();
ixSpec.setFunctionName("GET_PRODUCTS");
ixSpec.setInteractionVerb
(InteractionSpec.SYNC_SEND_RECEIVE);
Interaction ix = cx.createInteraction();
ix.execute (ixSpec, input, output);
java.util.Iterator iterator = output.iterator();
while (iterator.hasNext()) {
// Get a record element and extract value
// Add element to the collection
}
// Return Collection
} catch (ResourceException re) {
// ... Handle exception
}
}
// other DAO class methods ....
}
| Code Example 6.2 Example of a Data Access Object |
Code Example 6.3 shows how an application component might use the CatalogDAO data access object. The application component first instantiates the CatalogDAO object and then uses a get method to retrieve products in the product catalog.
public Product getProduct(String productID, Locale locale) {
try {
CatalogDAO dao = ProductCatalogDAOFactory.getDAO ();
return dao.getProduct (productID, locale);
} catch (...) {
//... Handle exceptions
}
}
| Code Example 6.3 Using a Data Access Object |
An access object can aggregate access to other access objects, thus providing a higher level of abstraction and functionality. For example, a PurchaseOrder aggregate access object can access a purchase order business function using a command bean and can also use a data access object to maintain persistent attributes of the purchase order. An aggregate access object can also encapsulate logic to process multiple access objects in a specific order. Such aggregate access objects are generated by tools.
Another type of access object, called a record object, is used to represent a data structure. It can be used to hold input or output data for an EIS function. A record object can be a custom implementation, in which case a tool generates the object from the meta information in a repository, or it can be a generic implementation, in which case it extracts meta information from a metadata repository at runtime. Such meta information includes type mapping and data representation.
6.4.3.2 Using Access ObjectsA component can use access objects in different ways depending on the functionality they offer. Some common ways to use access objects are:
6.4.3.3 Guidelines for Access Objects
- Define a one-to-one association between components and access objects. That is, each access object encapsulates the EIS functionality required by a particular component. This approach enables components to have Web access to the EIS resources encapsulated by an access object.
- Define components to aggregate the behavior of multiple access objects. This approach is often used when a component accesses multiple EIS resources or adds additional business logic to the functionality defined by multiple EIS resources.
There are some general guidelines to follow in developing access objects:
- An access object should not make assumptions about the environment in which it will be deployed and used.
- An access object should be designed to be usable by different types of components. For example, if an access object follows the set-execute-get design pattern described previously, then its programming model should be consistent across both enterprise beans and JSP pages.
- An access object should not define declarative transaction or security requirements of its own. It should follow the transaction and security management model of the component that uses it.
- All programming restrictions that apply to a component apply to the set of access objects associated with it. For example, an enterprise bean isn't allowed to start new threads, to terminate a running thread, or to use any thread synchronization primitives. Access objects should conform to the same restrictions.
It is important that application servers and components manage connections efficiently. Connections are expensive to create and remove. If an application server creates a new connection for each component's request, and then destroys the connection when the component completes its work, it is virtually impossible to support large numbers of users. To avoid this problem, J2EE application servers support connection pooling. While each application server can implement its own connection pooling mechanism, adhering to the Connector architecture ensures that pooling is efficient, scalable, and extensible.
An application server, by providing connection pooling, enables connections to be shared among client sessions so that a larger number of concurrent sessions can access an EIS. If each component acquires an EIS connection and holds it until its removal, it is difficult to scale up an application to support thousands of users. Since holding on to an EIS connection across long-lived instances or transactions is expensive, and since there is often a physical limitation to the number of connections to an EIS, components must manage connections efficiently. Application component providers need to follow sound connection management practices.
Connection management is especially important when applications migrate from two-tier to multitier component-based architecture. For example, a two-tier JDBC application may share a single connection across an entire application. However, after migrating to component-based partitioning, the same application must deal with shared connections across multiple component instances.
Application developers should follow the standard J2EE programming model for connection management; that is, application code should use the JNDI namespace to look up a connection factory instance. The same programming model is used for the creation of JDBC, CCI, or JMS connections.
In the standard programming model, the component provider specifies connection factory requirements for an application component in the deployment descriptor. For example, a bean provider specifies four elements in the deployment descriptor for a connection factory reference. (Refer to the EJB 2.0 specification for details on deployment descriptor elements for EJB components.)
The res-auth element should be set to Container so that the container manages the EIS sign on while creating connections. See Section 6.4.5 on page 196.
The application component looks up a connection factory instance in the component's environment using the JNDI API.
// obtain the initial JNDI Naming context
Context ctxt = new InitialContext();
// perform JNDI lookup to obtain resource manager connection factory
javax.sql.DataSource ds = (javax.sql.DataSource)
ctxt.lookup("java:comp/env/jdbc/CatalogDB");
The JNDI name passed in the method NamingContext.lookup is the same as that specified in the res-ref-name element.
Next, the application component invokes the getConnection method on the connection factory to get an EIS connection. The returned connection instance represents an application-level handle to an underlying physical connection.
// Invoke factory to obtain a connection java.sql.Connection con = ds.getConnection();
Once it has acquired the connection, the application component uses it to interact with the EIS. When the application component completes its work, it should invoke the Connection.close method on the acquired connection instance. Closing the connection enables the application server to manage the connection pool more effectively.
The bean provider can control the extent that connections are shared. By default, other enterprise beans in the application that use the same resource in the same transaction context can share the connection. The bean provider can set the res-sharing-scope deployment descriptor element to Unshareable to indicate that a connection not be shared. Keep in mind, though, that sharing connections to a resource manager allows the container to optimize connection and local transaction use. It is recommended that connections be marked Shareable.
A J2EE application is typically composed of components of different types: JSP pages, servlets, and enterprise beans. These component types vary in terms of their support for container-managed activation and passivation, execution of an instance for multiple clients, sharing of an instance across multiple clients, and other factors. Since connection management can vary by component type, an application component provider must account for such differences when deciding on a connection management model for an application. Here are a few examples that illustrate these differences.
A JSP page or servlet acquires and holds on to a connection to an EIS, whether that connection is initiated through the CCI or JDBC, in relation to the life cycle of its HTTP session. The JSP page or servlet can handle multiple HTTP requests across a single HTTP session, provided that those requests come from Web clients using the same EIS connection.
A stateful session bean can share an open connection and its client-specific query results across multiple methods. However, keep in mind that stateless session beans are designed to retain no state specific to a client. As a result, while stateless session beans can share a connection across methods, they maintain no client-specific state associated with the connection.
For entity beans, the EJB specification identifies methods that are allowed to perform EIS access through a connection. These include ejbCreate, ejbPostCreate, ejbRemove, ejbFind, ejbActivate, ejbLoad, ejbStore, and any business methods from the remote interface. An entity bean cannot access an EIS from within the setEntityContext and unsetEntityContext methods because a container does not have a meaningful transaction or security context when these two methods are called.
An application component provider follows the security model defined for the particular J2EE component--enterprise bean, JSP page, or servlet. Here are some guidelines for handling security in all types of components:
6.4.5.1 EIS Sign On
- An application component provider should declaratively specify security requirements for an application in the deployment descriptor. The security requirements include security roles, method permissions, and the authentication approach for EIS sign on.
- Security can be managed at the application level by an application component that is security aware. The component provider should include a simple programmatic interface through which the component manages security. This programmatic interface allows the application component provider to make access control decisions based on the security context--the principal and role--associated with the caller of a method and to do programmatic sign on to an EIS. (See Section 6.4.5.1.2 on page 198.)
- Other development roles, such as the J2EE server provider, deployer, and system administrator, should satisfy an application's security requirements in the operational environment. These security requirements are specified in the deployment descriptor.
From a security perspective, the mechanism for getting a connection to a resource is referred to as EIS sign on. A user requests a connection to be established under its security context. This security context includes various attributes, such as role, access privileges, and authorization level for the user. All application-level invocations to the database using this connection are then provided through the security context associated with the connection.
If the EIS sign on mechanism involves authentication of the user, then an application component provider can authenticate the user in one of two ways.
- The component provider allows the deployer to set up the EIS sign on information and the container manages sign on. For example, the deployer sets the user name and password for establishing the database connection. The container then takes the responsibility of managing the database sign on. This is sometimes referred to as container-managed EIS sign on.
- The component provider implements sign on to the database from the component code. The component provides explicit security information for the user requesting the connection. This is referred to as application-managed EIS sign on.
We recommend that a component let the container manage EIS sign on. This removes the burden of managing security information for the sign on from the application component provider. It also enables J2EE servers to provide additional useful security services, such as single sign on across multiple EISs and principal mapping across security domains.
Container-managed EIS sign on has other advantages. It enables the application component provider to avoid hard-coding security details in the component code. A component with hard-coded security logic is less portable because its code must be changed if deployed on containers with different security policies and mechanisms.
6.4.5.1.1 Container-Managed Sign OnThis section illustrates how the application component provider delegates the responsibility of setting up and managing EIS sign on to the container. The deployer sets up the EIS sign on so that the user account for connecting to the database is always eStoreUser. The deployer also configures the user identification and authentication information--user name and password--that is needed to authenticate eStoreUser to the database.
Here is how to use the JDBC API for container-managed EIS sign on. Code Example 6.4 shows the component code for invoking the connection request method on the javax.sql.DataSource with no security parameters. As in the previous example, the component instance relies on the container to do the sign on to the database using the security information configured by the deployer. Code Example 6.5 contains the corresponding connection factory reference deployment descriptor entry, where the res-auth element specifies that sign on is performed by the container.
// Obtain the initial JNDI context
Context ctxt = new InitialContext();
// Perform JNDI lookup to obtain connection factory
javax.sql.DataSource ds = (javax.sql.DataSource) ctxt.lookup
("java:comp/env/jdbc/CatalogDB");
// Invoke factory to obtain a connection.
// The security information is not given; thus it will be
// configured by the deployer.
java.sql.Connection conn = ds.getConnection ();
| Code Example 6.4 Container-Managed Sign On with JDBC |
<resource-ref> <description>description</description> <res-ref-name>jdbc/CatalogDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
| Code Example 6.5 Connection Factory Reference Element |
With application-managed sign on, the application component provider performs a programmatic sign on to the database. The component passes explicit security information (user name, password) to the connection request method. Application-managed sign on can be accomplished using the JDBC API. The component passes the security information--the user's name and the password--to the connection request method of the javax.sql.DataSource. See Code Example 6.6.
// Obtain the initial JNDI context
Context ctxt = new InitialContext();
// Perform JNDI lookup to obtain factory
javax.sql.DataSource ds = (javax.sql.DataSource) ctxt.lookup
("java:comp/env/jdbc/CatalogDB");
// Get connection passing in the security information
java.sql.Connection conn = ds.getConnection
("eStoreUser", "password");
| Code Example 6.6 Application-Managed Sign On with the JDBC API |
6.4.5.2 Handling EIS Access Authorization
An application component provider relies on both the container and the EIS for authorizing access to EIS data and functions. The application component provider specifies security requirements for application components declaratively in a deployment descriptor. A set of security roles and method permissions can be used to authorize access to methods on a component. For example, an application component provider declaratively specifies the PurchaseManager role as the only security role that is granted permission to call the purchase method on a PurchaseOrder enterprise bean. The purchase method in turn drives its execution through an ERP logistics application by issuing a purchase requisition. In effect, this application has authorized only end-users with the PurchaseManager role to do a purchase requisition. This is the recommended authorization model.
An application component provider can also programmatically control access to enterprise information system data and functions based on the principal or role associated with the client who initiated the operation. For example, the EJB specification allows component code to invoke getCallerPrincipal and isCallerInRole to get the caller's security context. An application component provider can use these two methods to perform security checks that cannot be expressed declaratively in the deployment descriptor.
An application can also rely on an enterprise information system to do access control based on the security context under which a connection to the enterprise information system has been established. For example, if all users of an application connect to the database as dbUser, then a database administrator can set explicit permissions for dbUser in the database security domain. The database administrator can deny dbUser permission to execute certain stored procedures or to access certain tables.