Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Mastering Enterprise JavaBeans™ and the Java 2 Platform, Enterprise Edition - Roman E

..pdf
Скачиваний:
41
Добавлен:
24.05.2014
Размер:
6.28 Mб
Скачать

Understanding EJB 1.1 645

<!--

Here, we define our Address bean. Notice we use the "AddressEJB" ejbname. This is the same "AddressEJB" that the above ejb-link element uses. -->

<entity> <ejb-name>AddressEJB</ejb-name> <ejb-class>AddressBean</ejb-class> <home>AddressHome</home> <remote>Address</remote>

...

</entity>

</enterprise-beans>

...

Source D.2 Declaring an EJB Reference within an EJB 1.1 Deployment Descriptor (continued).

Address—and no other. This is useful information for the deployer, because the deployer now knows which class files Employee depends on, and what JNDI location needs to be bound. Similarly, the container’s tools can easily inspect the deployment descriptor and verify that the deployer has done his job.

Finally, note that while the above example declares the Address bean within our deployment descriptor, we didn’t have to do this. The Address bean could have been in its own ejb-jar file with its own deployment descriptor.

How to Look Up Resource Factories

A resource factory is a provider of resources, such as a JDBC driver or a JMS driver. You can use resource factories from within your beans to acquire resources such as database connections. For example, a typical bean-managed persistent entity bean uses the JDBC 1.0 DriverManager resource factory to acquire javax.sql.Connection database connection resources, used to read to and write from a database.

EJB 1.0 does not specify how to obtain references to resource factories, and this has led to nonportable code. For example, the following illustrates how to obtain a JDBC connection from within a bean using BEA WebLogic (for a full example, see Chapter 8):

Properties env = entityContext.getEnvironment();

String jdbcURL = (String) env.get(JDBC_URL);

javax.sql.Connection conn =

javax.sql.DriverManager.getConnection(jdbcURL, env);

Go back to the first page for a quick link to buy this book online!

646 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

This code has many problems and is highly nonportable, for the following reasons:

JDBC version problems. We’re using a JDBC 1.0 DriverManager class, but the target-deployed environment may support JDBC 1.0, or it may support JDBC 2.0’s DataSource class. As a bean provider, you may not be aware of the eventual deployment environment or application server being used, especially if you are selling beans on the market.

JDBC 1.0 does not support connection pooling. Every EJB container has the liberty to perform connection pooling differently with proprietary APIs. This makes your bean code non-portable across application servers.

Acquiring a reference to a resource factory is not portable. EJB 1.0 does not specify how resource factories such as JDBC drivers should be obtained or initialized.

EJB 1.1 and J2EE address each of these issues.

JDBC versions have been standardized. J2EE requires that all J2EE-compliant products support the JDBC 2.0 standard extension’s javax.sql.DataSource resource factory. This means if you’re developing with J2EE, you can rest assured that your JDBC code will run in any J2EE-compliant product.

Connection pooling is portable. JDBC 2.0 specifies standard interfaces for connection pooling, further enhancing your code portability. Connection pooling happens completely behind the scenes, and your bean code is oblivious to it.

Acquiring a reference to a resource factory is portable. EJB 1.1 mandates that you use JNDI to look up resource factories, which makes your bean code portable across application servers.

The following code illustrates how to look up a JDBC 2.0 DataSource via JNDI from within an EJB 1.1 bean method:

//obtain the initial JNDI context Context initCtx = new InitialContext();

//perform JNDI lookup to obtain resource factory javax.sql.DataSource ds = (javax.sql.DataSource)

initCtx.lookup("java:comp/env/jdbc/EmployeeDB");

Notice that we’re using java:comp/env/jdbc for the JNDI location of our JDBC 2.0 driver. This is the EJB 1.1 suggested location for your JDBC resources. You must specify your resource factory’s JNDI location in the deployment descriptor. When your bean is deployed, the deployer will bind a real resource factory to that JNDI location. The corresponding deployment descriptor is shown in Source D.3.

Go back to the first page for a quick link to buy this book online!

Understanding EJB 1.1 647

...

<enterprise-beans>

<entity>

<ejb-name>EmployeeEJB</ejb-name> <ejb-class>EmployeeBean</ejb-class>

...

<!--

This element indicates a resource factory reference -->

<resource-ref>

<description>

This is a reference to a JDBC 2.0 driver used within the Employee bean.

</description>

<!--

The JNDI location that Employee uses to look up the JDBC driver. We declare it so the deployer knows to bind the JDBC driver in java:comp/env/jdbc/EmployeeDB.

--> <res-ref-name>jdbc/EmployeeDB</res-ref-name>

<!--

The resource factory class -->

<res-type>javax.sql.DataSource</res-type>

<!--

Security for accessing the resource factory. Can either be "Container" or "Application".

--> <res-auth>Container</res-auth>

</resource-ref>

</entity>

</enterprise-beans>

...

Source D.3 Declaring a Resource Factory Reference within an EJB 1.1 Deployment Descriptor.

Source D.3 is fairly self-explanatory, save for the res-auth entry. The following section has more details on this element.

Go back to the first page for a quick link to buy this book online!

648 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

Security with External Resources

When you acquire a connection to a database or other resource, that resource may require authorization. For example, you may need to specify a username and password when obtaining a JDBC connection. EJB 1.1 gives you two choices for authenticating yourself to a resource:

Perform the authentication yourself in the bean code. You should call the resource factory with the appropriate sign-on information, such as a login name and password. In this case, you should set the deployment descriptor’s res-auth element to Application.

Let the deployer handle authentication for you. The deployer specifies all sign-on information in the deployment descriptor. In this case, you should set the deployment descriptor’s res-auth element to Container.

The second choice is the most useful, especially when you are writing beans for resale or reuse by other companies, because only the deployer will know what sign-on credentials are needed to access a particular resource.

How to Look Up Environment Properties

Your bean’s environment properties are application-specific properties that your beans read in at runtime. In EJB 1.0, you read in your environment properties by querying your session context or entity context object, as shown below:

//1: Get the environment properties from the context Properties props = sessionContext.getProperties();

//2: Retrieve the desired environment property Integer myInteger = (Integer) props.get("myInteger");

Again, EJB 1.1 uses the unified JNDI API to look up deployed resources, including environment properties. The following code illustrates this.

//1: Acquire the initial context Context initCtx = new InitialContext();

//2: Use the initial context to look up

//the environment properties

Integer myInteger = (Integer) initCtx.lookup("java:comp/env/myInteger");

Notice that we look up environment properties under the JNDI name java:comp/ env. All EJB 1.1 environment properties must be in this naming context or in a subcontext of it. An example using a subcontext example is:

// 1: Acquire the initial context

Context initCtx = new InitialContext();

Go back to the first page for a quick link to buy this book online!

Understanding EJB 1.1 649

//2: Use the initial context to look up

//the environment properties

String companyName = (String)

initCtx.lookup("java:comp/env/EmployeeProps/companyName");

For a container to make your environment properties available under the correct JNDI names, you must specify your environment properties in an EJB 1.1 deployment descriptor. An example is shown in Source D.4.

...

<enterprise-beans>

<entity>

<ejb-name>EmployeeEJB</ejb-name> <ejb-class>EmployeeBean</ejb-class>

...

<!--

This element contains a single environment property. The property is only accessible from the EmployeeBean.

--> <env-entry>

<description>

The company name for this employee. </description>

<!--

The JNDI location that Employee uses to look up the environment property. We declare it so the container knows to bind the property in java:comp/env/EmployeeProps/companyName.

--> <env-entry-name>EmployeeProps/companyName</env-entry-name>

<!--

The type for this environment property -->

<env-entry-type>java.lang.String</env-entry-type>

<!--

The environment property value -->

<env-entry-value>MyCompany</env-entry-value> </env-entry>

</entity>

</enterprise-beans>

...

Source D.4 Declaring Environment Properties within an EJB 1.1 Deployment Descriptor.

Go back to the first page for a quick link to buy this book online!

650 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

How to Look Up the JTA UserTransaction Interface

The Java Transaction API (JTA) UserTransaction interface is used to explicitly issue begin, commit, and abort statements in your code, rather than allowing the container to do it for you. In EJB 1.0, session beans could gain access to the Java Transaction API (JTA) via the SessionContext interface as follows:

public class MySessionBean implements javax.ejb.SessionBean {

SessionContext ctx;

...

public void someMethod() {

UserTransaction utx = ctx.getUserTransaction();

utx.begin();

// perform logic

utx.commit();

}

...

}

In EJB 1.1, the equivalent way to perform this is via JNDI:

public class MySessionBean implements javax.ejb.SessionBean {

SessionContext ctx;

...

public void someMethod() {

Context initCtx = new InitialContext();

UserTransaction utx = (UserTransaction)

initCtx.lookup("java:comp/UserTransaction");

utx.begin();

// perform logic

utx.commit();

}

...

}

For the above code to work, your EJB 1.1 container is required to bind a

UserTransaction interface in java:comp/UserTransaction. Client code that wants to use the JTA (such as purchasing servlet we wrote in Chapter 15) can look up the JTA in a similar fashion.

Go back to the first page for a quick link to buy this book online!

Understanding EJB 1.1 651

Bean References Done Right

As we saw in Chapter 13, many EJB 1.0 containers do not correctly maintain a bean’s state during passivation/activation or during persistent operations. This has been massively improved in EJB 1.1. The EJB 1.1 specification mandates that containers maintain references correctly, assuming your bean’s state adheres to rules that we describe below.

Passivation and Activation Improvements

If you’ll recall from Chapter 5, a container can passivate your bean at any time, swapping its state out to disk to save system resources. Later on, the container can acitvate your bean so that it can service method calls once again.

In EJB 1.0, many containers did not correctly maintain your bean’s state for you during passivation/activation. Often times, your bean’s internal fields would be left blank, or exceptions would be thrown during the passivation/activation process. In EJB 1.1, your container must maintain your bean’s state, assuming your state fits into the following categories:

■■Serializable types

■■EJB object references

■■Home object references

■■Session context references

■■Environment naming contexts used for JNDI lookup

For example, let’s say you have the following stateful session bean code:

public class MySessionBean implements javax.ejb.SessionBean

{

//State variables private Long myLong;

private MySessionBeanRemoteInterface ejbObject; private MySessionBeanHomeInterface homeObject; private javax.ejb.SessionContext mySessionContext; private javax.naming.Context envContext;

//EJB-required methods (fill in as necessary) public void setSessionContext(SessionContext ctx) {} public void ejbCreate() {}

public void ejbPassivate() {} public void ejbActivate() {} public void ejbRemove() {}

Go back to the first page for a quick link to buy this book online!

652 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

// Business methods

...

}

The container must retain the values of the above member variables across passivation and activation operations. And because vendors must pass Sun Microsystems’ J2EE test suite, Sun has a mechanism of enforcing these rules. This is wonderful news for bean developers.

Persistence Improvements

In Chapter 9, we saw how to write entity beans whose persistent operations are completely handled by the container (called container-managed persistence). With container-managed persistence, your container is responsible for saving and loading your bean’s in-memory state fields to an underlying storage.

Unfortunately, many EJB 1.0 containers have a very restricted set of types that it will persist. For example, if you have an entity bean A that contains a reference to an entity bean B’s EJB object, many containers will not persist that EJB object reference properly. This is horrible because any complex EJB deployment will have many interesting relationships between entity beans.

EJB 1.1 improves this situation immensely. An EJB 1.1-based container must be able to persist the following:

■■Serializable types

■■EJB object references

■■Home object references

For example, let’s say you have the following entity bean code:

public class MyEntityBean implements javax.ejb.EntityBean

{

//Container-Managed fields public String myString;

public MyOtherEntityBeanRemoteInterface ejbObject; public MyOtherEntityBeanHomeInterface homeObject;

//EJB-required methods (fill in as necessary) public void setEntityContext(EntityContext ctx) {} public void unsetEntityContext() {}

public void ejbCreate() {} public void ejbPassivate() {} public void ejbActivate() {} public void ejbRemove() {} public void ejbStore() {} public void ejbLoad() {}

Go back to the first page for a quick link to buy this book online!

Understanding EJB 1.1 653

// Business methods

...

}

The container must be able to persist each of the above container-managed field types to storage. Again, this is great news for you because you now have a flexible arsenal of types you can safely define as your container-managed fields. This also means you can have systems of entity beans referring to other entity beans in a complex hierarchy, and your container will maintain persistent relationships for you.

Containers are not responsible for persisting entity context references or environment naming contexts used for JNDI lookups. You would never want to store these persistently as container-managed fields because they contain runtime EJBspecific information, and they do not represent persistent business data.

Transactions Clarified and Enhanced

Transactions have undergone significant changes in EJB 1.1. Let’s take a look at these changes.

Entity Beans Must Use Container-Managed Transactions

As we saw in Chapter 10, there are two ways for enterprise beans to perform transactions: declaratively or programmatically. With programmatic transactions, you must program to a transaction API to begin, commit, and abort transactions. With declarative transactions, the container calls a transaction service for you based upon your deployment descriptor settings, saving you the hassle of dealing with transaction APIs.

With EJB 1.1, only session beans can perform transactions programmatically. Entity beans must use declarative transactions.

Changes in Declarative Transactions

In Chapter 10, we learned about transactional isolation levels—used to control transactional concurrency. Transactional isolation can range from no isolation at all (the maximum concurrency with the least isolation) through full serializable isolation (lock-step operations with the highest isolation).

In EJB 1.0, there were two ways to use isolation levels:

If your bean is managing transactions, you specify isolation levels with your resource manager API (such as JDBC).

Go back to the first page for a quick link to buy this book online!

654 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

If your container is managing transactions, you declare your isolation levels in your deployment descriptor. The container then fulfills your isolation requirements at runtime.

In EJB 1.1, things have changed quite a bit.

If your bean is managing transactions, you specify isolation levels with your resource manager API (such as JDBC).

If your container is managing transactions, there is no way to specify isolation levels in the deployment descriptor. You need to either use resource manager APIs (such as JDBC), or rely on your container’s tools to specify isolation. Thus, isolation has been completely removed from the deployment descriptor.

In this new change, EJB 1.1 has added a bit of flexibility to isolation. If you’re using different resource managers within a single transaction, each resource manager can have a different isolation level, yet all run together under a single transaction. Note that any particular resource manager running under a transaction usually requires a single isolation level for the duration of that transaction. Note that there are some drawbacks to this new model as well, as described in the following sidebar.

Finally, note that although you cannot specify transaction isolation in the EJB 1.1 deployment descriptor, you can specify transaction attributes on bean methods. For example, you can mandate that a bean always runs in a transaction, or that a bean never runs in a transaction. Source D.5 demonstrates this.

<assembly-descriptor>

<!--

This demonstrates setting a transaction attribute on every method on the bean class.

--> <container-transaction>

<method> <ejb-name>Employee</ejb-name> <method-name>*</method-name>

</method>

<!--

Transaction attribute. Can be "NotSupported", "Supports", "Required", "RequiresNew", "Mandatory", or "Never".

-->

Source D.5 Declaring Transaction Attributes within an EJB 1.1 Deployment Descriptor (continues).

Go back to the first page for a quick link to buy this book online!