Configuring Entity Beans
The life cycle of an entity bean is a hybrid of the life cycles of a stateless and a stateful session bean. WebLogic maintains a free pool of inactive entity EJB instances. Just like stateless session beans, you can set the initial and the maximum sizes for the EJB pool. If you do specify a value for the initial-beans-in-free-pool setting, WebLogic prepares the pool of EJB instances with this initial capacity when the server starts up. Each EJB instance is created using the newInstance( ) method, and the setEntityContext( ) method is invoked once it's added to the pool. At this point, each EJB instance has a reference to the EntityContext, which provides the entity EJB with access to various container-managed services. When an EJB instance is removed from the pool, the container invokes the unsetEntityContext( ) method. In general, an entity EJB instance will remain in this pooled state as long as clients continue to invoke methods on the home object, or when the EJB container invokes one of the query methods on the entity bean.
Just like stateful session beans, WebLogic also maintains a cache of active entity EJB instances. The max-beans-in-cache element in the weblogic-ejb-jar.xml descriptor determines the maximum size of the entity cache. When a client invokes a create( ) method on the entity bean, the EJB container dips into the free pool and automatically invokes the ejbCreate( ) method on the EJB instance obtained from the pool, before placing it in the entity cache. Typically, the ejbCreate( ) method inserts one or more rows in the database, initializes the state with the underlying data, and returns the primary key for the new EJB instance. Once the EJB instance has been placed in the entity cache, it calls the ejbPostCreate( ) method on the EJB instance.
The EJB container uses the ejbLoad( ) and ejbStore( ) methods to read and write the current state of persistent fields in an entity bean. Suppose a client invokes a method on an entity bean and a new transaction is initiated. A transaction context would exist either because the client has explicitly initiated a JTA transaction before the method call, or because the EJB container automatically has initiated a transaction before the method call. For instance, the EJB container would do this implicitly if the method has a transaction setting of, say, Required. The EJB container then invokes the ejbLoad( ) method on the EJB instance to ensure that it has the most recent version of EJB's persistent data. Upon successful completion of the transaction, the EJB container invokes the ejbStore( ) method so that it can save any changes to the persistent fields. Throughout this time, the entity bean remains in the active state, including when the container invokes one of the select methods on the entity bean.
The following XML stanza illustrates how you can configure the free pool and entity cache for an entity bean:
PatientEJB 15 5 15 900 org.foo.bar.PatientHome
When the entity cache reaches its maximum capacity, the EJB instances are scrubbed from the cache after idle-timeout-seconds of inactivity. In this case, passivation involves removing the EJB instance from the entity cache and returning it to the free pool. The EJB container invokes the ejbStore( ) method so that the current state of the EJB's persistent fields can be written to the database, and then calls the ejbPassivate( ) method to return the EJB instance to the pooled state. A passivated EJB instance can be activated later, in which case the EJB container will invoke the ejbActivate( ) method when it moves the EJB instance back into the entity cache.
10.4.1 Altering the Store Behavior
By default, WebLogic's EJB container calls the ejbStore( ) method on an EJB 1.1-compliant entity bean whenever a transaction is committed, regardless of whether its persistent fields were modified. However, WebLogic also allows you to limit the number of calls to the ejbStore( ) method by ensuring that it is invoked only when the persistent fields of the entity bean are dirty. You may use the is-modified-method-name element in the weblogic-ejb-jar.xml descriptor file to designate a method that the EJB container can use to determine whether the persistent fields of the EJB have been modified and should therefore be written to the database.
The following example shows how to use a dirty flag to track changes to the persistent fields of a bean-managed entity bean:
public class PatientBean implements EntityBean { /** serializable Java object that represents the home address of Patient */ private AddressVO homeAddress; // other persistent fields of Patient /** flag that determines if entity bean is dirty */ private boolean dirty; /** part of Patient's component interface */ public void setAddress(AddressVO theAddress) { this.homeAddress = theAddress; dirty = true; } /** part of Patient's component interface */ public AddressVO getAddress( ) { return this.homeAddress; } /** is-modified method */ public boolean isModified( ) { return dirty; } /** ejbStore method */ public void ejbStore( ) { //write changes to persistent fields to the db dirty = false; } /** ejbLoad method */ public void ejbLoad( ) { //refresh values of persistent fields from db dirty = false; } //provide implementation for other life-cycle methods }
The isModified( ) method returns true to indicate one or more persistent fields have been modified, and false otherwise. This allows the EJB container to decide whether it needs to invoke the ejbStore( ) method and write those changes to the database. The following XML stanza from the weblogic-ejb-jar.xml descriptor shows how you can declare the isModified( ) method:
PatientEJB 10 isModified org.foo.bar.PatientHome
Whenever the EJB container needs to write the EJB's persistent state to the database, it will first invoke the isModified( ) method to determine whether any changes have been made to the EJB's persistent fields.
|
Even though the isModified( ) method can improve the performance of your entity beans by avoiding unnecessary calls to the ejbStore( ) method, you need to properly track the "dirty" flag throughout the life cycle of the entity bean to ensure that no changes to any of the persistent fields are ever lost.
As we have seen, WebLogic calls the ejbStore( ) method just before the successful completion of the transaction. Generally, this is quite optimal because you limit the number of calls to the ejbStore( ) method during the transaction and avoid needless database updates. However, you may need to alter this default behavior under certain conditionsfor instance, if your database supports a READ_UNCOMMITTED isolation level, you can make the results of intermediate updates available to other transactions by writing the data out sooner.
To perform this early writing, you can force the EJB container to call the ejbStore( ) method after the completion of each method call, instead of at the end of the transaction. The delay-updates-until-end-of-tx element in the weblogic-ejb-jar.xml descriptor lets you determine when the ejbStore( ) method is invoked. By setting the element to false, you ensure that the EJB container saves any changes to the EJB's persistent fields after every method call, rather than when the transaction completes successfully. Even though the ejbStore( ) method is invoked after every method call, the database updates are committed only when the transaction completes successfully.
10.4.2 EJB Concurrency
WebLogic provides a number of features that control how WebLogic's EJB container manages concurrent access to entity beans. The concurrency-strategy element in the weblogic-ejb-jar.xml descriptor file lets you choose from one of the following options:
Exclusive
The EJB container places an exclusive lock on a cached entity EJB instance for the duration of the transaction. This means other clients are blocked from accessing the EJB's persistent fields until the current transaction completes. Clearly, exclusive locking doesn't provide optimal concurrency, especially because other clients are unable to even read the persistent fields while the EJB instance is involved in a transaction.
|
Database
By default, WebLogic allows concurrent access to entity beans and lets the database handle caching and locking issues. This option improves concurrent access to entity beans because now the database is responsible for obtaining the necessary locks on the EJB's persistent data, and for resolving deadlocks. WebLogic continues to maintain a cache of entity EJB instances; a separate EJB instance is allocated for each transaction.
Because the container doesn't have exclusive access to the underlying data, it is unable to cache the state of the EJB instance between transactions, so you cannot enable the cache-between-transactions setting. At the start of a transaction, the EJB container will instead invoke the ejbLoad( ) method to obtain the latest values for the persistent fields of the EJB.
Optimistic
When the optimistic concurrency is used, no locks are held by the EJB container or the database during the transaction. Instead, the EJB container ensures that none of the data that was updated during the transaction has changed before the transaction is committed. If any of the data has changed, the transaction is rolled back.
To enable these "smart" updates and data checks for validity, you need to specify additional settings in the weblogic-cmp-rdbms-jar.xml descriptor file. We shall cover these settings in Chapter 11.
ReadOnly
WebLogic supports an additional cache of read-only entity beans. A read-only EJB instance is activated for each transaction, allowing multiple clients and transactions to proceed in parallel.
Most importantly, this scheme also can ensure that the state of the read-only bean accurately reflects the underlying data. You can persuade the EJB container to periodically refresh the state of the persistent fields of the entity bean by specifying the read-timeout-seconds element in the weblogic-ejb-jar.xml descriptor file. This setting determines the frequency with which WebLogic refreshes the cached EJB instances:
PatientEJB 15 300 ReadOnly org.foo.bar.PatientHome
When an entity bean is invoked, WebLogic checks to see whether the cached data is older than read-timeout-seconds. If so, it invokes the ejbLoad( ) method on the entity bean. Otherwise, the client continues to use the cached values for the EJB's persistent fields. By default, cached EJB instances with read-only concurrency are reloaded every 600 seconds.
Read-only entity beans are bound by certain constraints as well:
- WebLogic never calls the ejbStore( ) method for a read-only entity bean because the EJB's persistent fields need not be written to the database.
- All methods of the entity bean must be idempotent i.e., repeated calls to the same method with identical arguments must have exactly the same effect as a single method call.
Let's now look at two important refinements to the read-only concurrency model.
10.4.2.1 Read-only multicast invalidation
WebLogic allows the client to determine when cached instances of a read-only entity bean need to be refreshed, instead of relying on a periodic refresh. This makes the read-only beans a very flexible option because often you will know when the underlying data has been invalidated. In these circumstances, the client needs to simply trigger a multicast message that invalidates all EJB instances on the server, and perhaps cached copies on other servers in the cluster as well. When an entity bean is configured with a read-only concurrency strategy, WebLogic's EJB compiler generates an implementation class for the EJB's home, which implements the weblogic.ejb.CachingHome interface. If the entity bean has local interfaces, the home object also implements the weblogic.ejb.CachingLocalHome interface.
These interfaces provide additional methods that allow the client to invalidate cached EJB instances on the local server and/or cached EJB instances on other cluster members:
package weblogic.ejb; public interface CachingHome extends javax.ejb.EJBHome { /** * invalidate cached EJB instances with supplied primary key */ public void invalidate(Object pk) throws RemoteException; /** * invalidate cached EJB instances with supplied primary keys */ public void invalidate(Collection pks) throws RemoteException; /** * invalidate cached EJB instances with supplied primary key * on local server only */ public void invalidateLocalServer(Object pk) throws RemoteException; /** * invalidate cached EJB instances with supplied primary keys * on local server only */ public void invalidateLocalServer(Collection pks) throws RemoteException; /** * invalidate all cached EJB instances */ public void invalidateAll( ) throws RemoteException; /** * invalidate all cached EJB instances on the local server only */ public void invalidateAllLocalServer( ) throws RemoteException; }
Using the CachingHome interface, you then can invoke one of its invalidate( ) methods and force WebLogic to invalidate cached instances of the read-only entity bean:
/* use JNDI lookup to obtain reference to EJB home object */ InitialContext ctx = new InitialContext( ); Object home = ctx.lookup("org.foo.bar.PatientHome") PatientHome ejbHome = (EJBHome) PortableRemoteObject.narrow(home, org.foo.bar. PatientHome.class); /* invalidate all cached EJB instances with the specified primary key */ ((weblogic.ejb.CachingHome) ejbHome).invalidate(pk);
A subsequent call to an invalidated EJB causes the container to invoke the ejbLoad( ) method on the EJB, thereby allowing the values of the persistent fields to be reloaded from the database. If your read-only entity bean supports these multicast invalidations, you also may choose to disable the periodic reloading of EJB instances by setting the value of read-timeout-seconds in the weblogic-ejb-jar.xml descriptor to 0.
10.4.2.2 Read-mostly pattern
Let's look at how to implement a read-mostly pattern, which enables you to model entities that are primarily read-only, but perhaps also updated infrequently. WebLogic lets you employ a combination of read-only and read-write entity beans to model entities that are updated occasionally. In this scenario, both the read-only and read-write entity beans are mapped to the same data. Use the read-only EJB for read access to the underlying data, and use the read-write EJB when you need to persist changes to the CMP fields. The read-only EJB still can refresh its in-memory state at intervals specified by the read-timeout-seconds setting. The read-write EJB invalidates its read-only counterpart whenever any changes to its persistent fields are saved to the database.
This invalidation can be triggered in two ways:
- After updating the read-write EJB, the client can invalidate the read-only EJB explicitly by invoking the invalidate( ) method on the CachingHome interface. If the updates occur during a client-initiated transaction, you should ensure the invalidation occurs after the transaction has completed successfully.
- You can configure WebLogic's EJB container so that it automatically handles the invalidation whenever the ejbStore( ) method is invoked on the read-write EJB. The invalidation-target element in the weblogic-ejb-jar.xml descriptor file allows you to specify the name of the read-only EJB that ought to be invalidated when updates to the read-write EJB are committed.
So, for an entity object that needs to implement the read-mostly pattern, you require a combination of entity beans:
- A read-only bean that encapsulates read access to the underlying data and also may be periodically refreshed
- A read-write bean that invalidates the read-only bean whenever changes to the persistent fields are committed
The following XML fragment shows the deployment settings that enable you to implement this scenario:
PatientReaderEJB 0 ReadOnly WebLogic_CMP_RDBMS 7.0 META-INF/weblogic-cmp-rdbms-jar.xml org.foo.bar.PatientReaderHome PatientEJB WebLogic_CMP_RDBMS 8.1 META-INF/weblogic-cmp-rdbms-jar.xml PatientReaderEJB org.foo.bar.PatientHome
By setting the read-timeout-seconds element to 0, you ensure the EJB data is loaded only when the EJB instance enters the cache and whenever the read-only entity bean is invalidated. Both of the entity beans involved in this configuration must use the same abstract persistence schema.
10.4.3 Entity Bean Caching
Earlier we looked at how to configure the entity cache for an entity EJB component its size, caching strategy, and the idle-timeout after which a cached EJB instance may be passivated. In this section, we examine how to configure WebLogic so that it caches the EJB's persistent data across (between) transactions. We also look at how entity beans can share an application-level cache at runtime.
10.4.3.1 Caching between transactions
WebLogic's read-only and read-mostly schemes attempt to optimize EJB usage by minimizing unnecessary writes. Besides these concurrency strategies, WebLogic also supports caching between transactions, which attempts to minimize unnecessary reads. By default, WebLogic supports short-term caching, in which the EJB's persistent data is cached only for the duration of the transaction. In other words, WebLogic does not read the values of the EJB's persistent fields again until the next transaction. It does this by invoking the ejbLoad( ) method on the entity bean once, at the start of the transaction. This ensures that the transaction always uses the most current version of the EJB's persistent data. This approach is best suited when multiple applications may have update access to the underlying data.
Long-term caching occurs when the EJB container retains the state of the EJB's persistent fields across transactions, between the EJB's use in one transaction and its use in the next transaction. You can enable long-term caching by setting the cache-between-transactions element in the weblogic-ejb-jar.xml descriptor file to true.
|
Read-only entity beans always perform long-term caching of data, and refresh their data only if they are invalidated or during a periodic refresh. For this reason, read-only beans ignore the value of the cache-between-transactions setting.
If you enable long-term caching for an EJB that supports Exclusive concurrency, you also must guarantee that the EJB container has exclusive update access to the underlying data. Otherwise, your EJBs run the risk of working with stale data. Hence, no application outside the EJB container must be capable of updating the EJB's persistent data. This can be achieved by deploying the entity bean to a single WebLogic instance, in which case the state of any cached EJB instance since the last transaction always will be accurate. If you deploy such a bean to a cluster of servers, any member of the cluster would be able to update the EJB's persistent data, which violates the assumptions of the Exclusive concurrency strategy. In this case, WebLogic will automatically disable any caching between transactions to prevent any inconsistencies in the runtime state of the EJB's persistent fields.
If you enable long-tem caching for entity beans with Optimistic concurrency, the EJB container can reuse the cached values of the EJB's persistent fields from the previous transaction. The container checks for optimistic conflicts only at the end of the transaction, thereby ensuring that all changes to the EJB data are consistent. Finally, if an entity bean supports Database concurrency, the value of the cache-between-transactions setting is ignored. WebLogic continues to maintain a cache of entity EJB instances, but does not cache the EJB's persistent data between transactions. At the start of each transaction, the EJB container invokes the ejbLoad( ) method to obtain the latest copy of its persistent data. Therefore, you should not enable long-term caching if the entity bean is configured for Database concurrency.
10.4.3.2 Application-level cache
Different entity beans that are part of the same EAR may share a single cache. WebLogic lets you create these application-scoped entity caches by providing an entity-cache element in the weblogic-application.xml descriptor file. Individual entity beans within the EAR then can reference this entity cache using the entity-cache-ref element in the weblogic-ejb-jar.xml descriptor. The following XML stanza shows how you can declare an entity cache for an enterprise application:
GlobalEntityCache 10 Exclusive
You can restrict the size of the entity cache in two ways:
- Using the max-cache-size subelement, you can specify a maximum limit on the amount of memory allocated for the entity cache, in terms of either bytes or megabytes.
- Using the max-beans-in-cache subelement, you can specify a maximum limit on the number of entity beans in the cache. If you set this to 0, there is no limit on the number of entity beans in the cache it is restricted only by the available system memory.
If you do not explicitly restrict the cache size, by default WebLogic limits the entity cache to 1000 EJB instances, at most. Use the caching-strategy subelement to configure how the EJB container handles cached EJB instances. You can specify one of the following values for the caching-strategy setting:
Exclusive
Here, the entity cache maintains a single EJB instance in memory for each primary key value. The EJB container locks the EJB instance under the exclusive locking policy, which means only a single transaction can use an EJB instance at any given time.
MultiVersion (default)
Here, the entity cache can hold multiple EJB instances in memory for a primary key value. The EJB container ensures that each transaction gets a different cached EJB instance of the entity bean.
By default, WebLogic resorts to application-level caching whenever an entity bean does not specify an entity cache in its weblogic-ejb-jar.xml descriptor file. WebLogic also provides two application-scoped caches for each enterprise application namely, ExclusiveCache and MultiVersionCache. An application deployed to WebLogic Server can declare entity caches with these names, and then override the settings for these default caches. However, you cannot modify the caching strategy for these two entity caches.
An EJB component that is bundled within the enterprise application then can use the entity-cache-ref element to refer to the application-level entity cache:
GlobalEntityCache Exclusive 20
The entity-cache-name subelement indicates the application-scoped entity cache that will cache EJB instances of this entity bean. The estimated-bean-size element specifies the average size of the entity EJB instances (in bytes). You need this setting when the application-level cache has restrictions on its memory size because it allows WebLogic to determine the maximum number of instances of this entity bean that may be cached.
The concurrency-strategy subelement defines the concurrency model used for cached instances of the entity bean. Its value must be compatible with the caching strategy configured for the application-level entity cache. An Exclusive cache supports only entity EJB instances with Exclusive concurrency. A MultiVersion entity cache is compatible with entity beans configured for either Database, ReadOnly, or Optimistic concurrency strategy.