CHAPTER 5 EJBs on JBoss - The EJB Container Configuration and Architecture

The JBoss 3.2.x EJB container architecture is a fourth generation design that emphasizes a modular plug-in approach. All key aspects of the EJB container may be replaced by custom versions of a plug-in and/or an interceptor by a developer. This approach allows for fine tuned customization of the EJB container behavior to optimally suite your needs. Most of the EJB container behavior is configurable through the EJB jar META-INF/jboss.xml descriptor and the default server-wide equivalent standardjboss.xml descriptor. We will look at various configuration capabilities throughout this chapter as we explore the container architecture.

The EJB Client Side View

We will begin our tour of the EJB container by looking at the client view of an EJB through the home and remote proxies. It is the responsibility of the container provider to generate the javax.ejb.EJBHome and javax.ejb.EJBObject for an EJB implementation. A client never references an EJB bean instance directly, but rather references the EJBHome which implements the bean home interface, and the EJBObject which implements the bean remote interface. See .

See The composition of an EJBHome proxy in JBoss.. shows the composition of an EJB home proxy and its relation to the EJB deployment.

 
FIGURE 5-1. The composition of an EJBHome proxy in JBoss.

The numbered items in the figure are:

  1. The EJBDeployer ( org.jboss.ejb.EJBDeployer ) is invoked to deploy an EJB jar. An EJBModule ( org.jboss.ejb.EJBModule ) is created to encapsulate the deployment metadata.
  2. The create phase of the EJBModule life cycle creates an EJBProxyFactory ( org.jboss.ejb.EJBProxyFactory ) that manages the creation of EJB home and remote interface proxies based on the EJBModule invoker-proxy-bindings metadata. There can be multiple proxy factories associated with an ejb and we will look at how this is defined shortly.
  3. The ProxyFactory constructs the logical proxies and binds the homes into JNDI. A logical proxy is composed of a dynamic Proxy ( java.lang.reflect.Proxy ), the home interfaces of the EJB that the proxy exposes, the ProxyHandler ( java.lang.reflect.InvocationHandler ) implementation in the form of the ClientContainer ( org.jboss.proxy.ClientContainer ), and the client side interceptors.
  4. The proxy created by the EJBProxyFactory is a JDK 1.3+ dynamic proxy. It is a serializable object that proxies the EJB home and remote interfaces as defined in the EJBModule metadata. The proxy translates requests made through the strongly typed EJB interfaces into a detyped invocation using the ClientContainer handler associated with the proxy. It is the dynamic proxy instance that is bound into JNDI as the EJB home interface that clients lookup. When a client does a lookup of an EJB home, the home proxy is transported into the client VM along with the ClientContainer and its interceptors. The use of dynamic proxies avoids the EJB specific compilation step required by many other EJB containers.
  5. The EJB home interface is declared in the ejb-jar.xml descriptor and available from the EJBModule metadata. A key property of dynamic proxies is that they are seen to implement the interfaces they expose. This is true in the sense of Java's strong type system. A proxy can be cast to any of the home interfaces and reflection on the proxy provides the full details of the interfaces it proxies.
  6. The proxy delegates calls made through any of its interfaces to the ClientContainer handler. The single method required of the handler is:

    public Object invoke(Object proxy, Method m, Object[] args)
    throws Throwable

    The EJBProxyFactory creates a ClientContainer and assigns this as the ProxyHandler . The ClientContainer 's state consists of an InvocationContext ( org.jboss.invocation.InvocationContext ) and a chain of interceptors ( org.jboss.proxy.Interceptor ). The InvocationContext contains:
  7. the JMX ObjectName of the EJB container MBean the Proxy is associated with
  8. the javax.ejb.EJBMetaData for the EJB
  9. the JNDI name of the EJB home interface
  10. the transport specific invoker ( org.jboss.invocation.Invoker )
  11. The interceptor chain consists of the functional units that make up the EJB home or remote interface behavior. This is a configurable aspect of an EJB as we will see when we discuss the jboss.xml descriptor, and the interceptor makeup is contained in the EJBModule metadata. Interceptors ( org.jboss.proxy.Interceptor ) handle the different EJB types, security, transactions and transport. You can add your own interceptors as well.

  12. The transport specific invoker associated with the proxy has an association to the server side detached invoker that handles the transport details of the EJB method invocation. The detached invoker is a JBoss server side component.

The configuration of the client side interceptors is done using the jboss.xml client-interceptors element. See The jboss.xml descriptor client side interceptor configuration elements.. shows the subset of the jboss.xml DTD for the client interceptors.When the ClientContainer invoke method is called it creates an untyped Invocation ( org.jboss.invocation.Invocation ) to encapsulate request. This is then passed through the interceptor chain. The last interceptor in the chain will be the transport handler that knows how to send the request to the server and obtain the reply, taking care of the transport specific details.

FIGURE 5-2. The jboss.xml descriptor client side interceptor configuration elements.

As an example of the client interceptor configuration usage, consider the default stateless session bean configuration found in the server/default/standardjboss.xml descriptor. See The client-interceptors from the "Standard Stateless SessionBean" configuration..shows the "stateless-rmi-invoker"client interceptors configuration referenced by the "Standard Stateless SessionBean".

The client-interceptors from the "Standard Stateless SessionBean" configuration.

<invoker-proxy-bindings>

<invoker-proxy-binding>

<name>stateless-rmi-invoker</name>

<invoker-mbean>jboss:service=invoker,type=jrmp</invoker-mbean>

<proxy-factory>org.jboss.proxy.ejb.ProxyFactory</proxy-factory>

<proxy-factory-config>

<client-interceptors>

<home>

<interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</home>

<bean>

<interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</bean>

</client-interceptors>

</proxy-factory-config>

</invoker-proxy-binding>

...

<invoker-proxy-bindings>

 

<container-configuration>

<container-name>Standard Stateless SessionBean</container-name>

<call-logging>false</call-logging>

<invoker-proxy-binding-name>stateless-rmi-invoker</invoker-proxy-binding-name>

...

</container-configuration>

This is the client interceptor configuration for stateless session beans that is used in the absence of an EJB jar META-INF/jboss.xml configuration that overrides these settings. The functionality provided by each interceptor is:

  • org.jboss.proxy.ejb.HomeInterceptor , this handles the getHomeHandle , getEJBMetaData , and remove methods of the EJBHome interface locally in the client VM. Any other methods are propagated to the next interceptor.
  • org.jboss.proxy.ejb.StatelessSessionInterceptor , this handles the toString , equals , hashCode , getHandle , getEJBHome and isIdentical methods of the EJBObject interface locally in the client VM. Any other methods are propagated to the next interceptor.
  • org.jboss.proxy.SecurityInterceptor , this associates the current security context with the method invocation for use by other interceptors or the server.
  • org.jboss.proxy.TransactionInterceptor , this associates any active transaction with the invocation method invocation for use by other interceptors.
  • org.jboss.invocation.InvokerInterceptor , this interceptor encapsulates the dispatch of the method invocation to the transport specific invoker. It knows if the client is executing in the same VM as the server and will optimally route the invocation to a by reference invoker in this situation. When the client is external to the server VM, this interceptor delegates the invocation to the transport invoker associated with the invocation context. In the case of the See The client-interceptors from the "Standard Stateless SessionBean" configuration.. configuration, this would be the invoker stub associated with the "jboss:service=invoker,type=jrmp", the JRMPInvoker service presented in The JRMPInvoker - RMI/JRMP Transport.

Specifying the EJB Proxy Configuration

To specify the EJB invocation transport and the client proxy interceptor stack, you need to define an invoker-proxy-binding in either the EJB jar META-INF/jboss.xml descriptor, or the server standardjboss.xml descriptor. There are serveral default invoker-proxy-bindings defined in the standardjboss.xml descriptor for the various default EJB container configurations and the standard RMI/JRMP and RMI/IIOP transport protocols. The current default proxy configurations are:

  • entity-rmi-invoker: a RMI/JRMP configuration for entity beans
  • clustered-entity-rmi-invoker: a RMI/JRMP configuration for clustered entity beans
  • stateless-rmi-invoker: a RMI/JRMP configuration for stateless session beans
  • clustered-stateless-rmi-invoker: a RMI/JRMP configuration for clustered stateless session beans
  • stateful-rmi-invoker: a RMI/JRMP configuration for clustered stateful session beans
  • clustered-stateful-rmi-invoker: a RMI/JRMP configuration for clustered stateful session beans
  • message-driven-bean: a JMS invoker for message driven beans
  • iiop: a RMI/IIOP for use with session and entity beans.

To introduce a new protocol binding, or customize the proxy factory, or the client side interceptor stack, requires defining a new invoker-proxy-binding. The full invoker-proxy-binding DTD fragment for the specification of the proxy configuration is given in See The invoker-proxy-binding schema..

 

FIGURE 5-3. The invoker-proxy-binding schema

The invoker-proxy-binding child elements are:

  • name: The name element gives a unique name for the invoker-proxy-binding. The name is used to reference the binding from the EJB container configuration when setting the default proxy binding as well as the EJB deployment level to specify addition proxy bindings. You will see how this is done when we look at the jboss.xml elements that control the server side EJB container configuration.
  • invoker-mbean: The invoker-mbean element gives the JMX ObjectName string of the detached invoker MBean service the proxy invoker will be associated with.
  • proxy-factory: The proxy-factory element specifies the fully qualified class name of the proxy factory, which must implement the org.jboss.ejb.EJBProxyFactory interface. The EJBProxyFactory handles the configuration of the proxy and the association of the protocol specific invoker and context. The current JBoss implementations of the EJBProxyFactory interface include:
  • org.jboss.proxy.ejb.ProxyFactory : The RMI/JRMP specific factory.
  • org.jboss.proxy.ejb.ProxyFactoryHA : The cluster RMI/JRMP specific factory.
  • org.jboss.ejb.plugins.jms.JMSContainerInvoker : The JMS specific factory.
  • org.jboss.proxy.ejb.IORFactory : The RMI/IIOP specific factory.
  • proxy-factory-config: The proxy-factory-config element specifies additional information for the proxy-factory implementation. Unfortunately, its currently an unstructed collection of elements. Only a fraction of the elements shown in See The invoker-proxy-binding schema. apply to each type of proxy factory. The child elements break down into the three invocation protocols: RMI/RJMP, RMI/IIOP and JMS.
  • For the RMI/JRMP specific proxy factories, org.jboss.proxy.ejb.ProxyFactory and org.jboss.proxy.ejb.ProxyFactoryHA , the following elements apply:

  • client-interceptors: The client-interceptors define the home, remote and optionally the multi-valued proxy interceptor stacks.
  • web-class-loader: The web class loader defines the instance of the org.jboss.web.WebClassLoader that should be associated with the proxy for dynamic class loading.
  • See A sample ProxyFactory proxy-factory-config. gives a sample proxy-factory-config taken from the standardjboss.xml descriptor.

A sample ProxyFactory proxy-factory-config

<proxy-factory-config>

<client-interceptors>

<home>

<interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</home>

<bean>

<interceptor>org.jboss.proxy.ejb.EntityInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</bean>

<list-entity>

<interceptor>org.jboss.proxy.ejb.ListEntityInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</list-entity>

</client-interceptors>

</proxy-factory-config>

 

    For the RMI/IIOP specific proxy factory, org.jboss.proxy.ejb.IORFactory , the following elements apply:

  • poa: The portable object adapter usage, one of per-servent, shared
  • register-ejbs-in-jnp-context: A flag indicating if the EJBs should be register in JNDI.
  • jnp-context: The JNDI context in which to register ejbs.
  • web-class-loader: The web class loader defines the instance of the org.jboss.web.WebClassLoader that should be associated with the proxy for dynamic class loading.
  • See A sample IOFactory proxy-factory-config. gives a sample proxy-factory-config fragment taken from the standardjboss.xml descriptor.

A sample IOFactory proxy-factory-config

<proxy-factory-config>

<web-class-loader>org.jboss.iiop.WebCL</web-class-loader>

<poa>per-servant</poa>

<register-ejbs-in-jnp-context>true</register-ejbs-in-jnp-context>

<jnp-context>iiop</jnp-context>

</proxy-factory-config>

 

    For the JMS specific proxy factory, org.jboss.ejb.plugins.jms.JMSContainerInvoker , there is an MDBConfig

  • MaximumSize: This specifies the upper limit to the number of concurrent MDBs that will be allowed for the JMS destination associated with a given MDB deployment. This defaults to 15.
  • MaxMessages: This specifies the maxMessages parameter value for the createConnectionConsumer method of javax.jms.QueueConnection and javax.jms.TopicConnection interfaces, as well as the maxMessages parameter value for the createDurableConnectionConsumer method of javax.jms.TopicConnection . It is the maximum number of messages that can be assigned to a server session at one time. This defaults to 1. This value should not be modified from the default unless your JMS provider indicates this is supported.
  • MDBConfig:Configuration for the MDB JMS connection behavior. This include the reconnection interval and dead letter queue elements:
  • ReconnectIntervalSec: The time to wait (in seconds) before trying to recover the connection to the JMS server.
  • DLQConfig: Configuration for an MDB's dead letter queue, used when messages are redelivered too many times.
  • JMSProviderAdapterJNDI: The JNDI name of the JMS provider adapter in the java:/ namespace. This is mandatory for an MDB and must implement org.jboss.jms.jndi.JMSProviderAdapter .
  • ServerSessionPoolFactoryJNDI: The jndi name of the session pool in the java:/ namespace of the JMS provider's session pool factory. This is mandatory for an MDB and must implement org.jboss.jms.asf.ServerSessionPoolFactory .
  • See A sample JMSContainerInvoker proxy-factory-config. gives a sample proxy-factory-config fragment taken from the standardjboss.xml descriptor.

A sample JMSContainerInvoker proxy-factory-config

<proxy-factory-config>

<JMSProviderAdapterJNDI>DefaultJMSProvider</JMSProviderAdapterJNDI>

<ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI>

<MaximumSize>15</MaximumSize>

<MaxMessages>1</MaxMessages>

<MDBConfig>

<ReconnectIntervalSec>10</ReconnectIntervalSec>

<DLQConfig>

<DestinationQueue>queue/DLQ</DestinationQueue>

<MaxTimesRedelivered>10</MaxTimesRedelivered>

<TimeToLive>0</TimeToLive>

</DLQConfig>

</MDBConfig>

</proxy-factory-config>

The EJB Server Side View

Every EJB invocation must end up at a JBoss server hosted EJB container. In this section we will look at how invocations are transported to the JBoss server VM and find their way to the EJB container via the JMX bus.

Detached Invokers - The Transport Middlemen

We looked at the detached invoker architecture in the context of exposing RMI compatible interfaces of MBean services in Remote Access to Services, Detached Invokers. Here we will look at how detached invokers are used to expose the EJB container home and bean interfaces to clients. The generic view of the invoker architecture is presented in See The transport invoker server side architecture..

FIGURE 5-4. The transport invoker server side architecture

For each type of home proxy there is a binding to an invoker and its associated transport protocol. In 3.2.x, a container may have multiple invocation protocols active simultaneously. In 3.0.x a container could only have a single protocol associated with it, but this coule be any protocol. The jboss.xml DTD configuration fragments for the invoker configuration is given in See The jboss.xml descriptor invoker configuration elements... The invoker-proxy-binding-name maps to an invoker-proxy-binding/name element. At the container-configuration level this specifies the default invoker that will be used for EJBs deployed to the container. At the bean level, the invoker-bindings specify one or more invokers to use with the EJB container MBean.

FIGURE 5-5. The jboss.xml descriptor invoker configuration elements.

When one specifies multiple invokers for a given EJB deployment, the home proxy must be given a unique JNDI binding location. This is specified by the invoker/jndi-name element value. Another issue when multiple invokers exist for an EJB is how to handle remote homes or interfaces obtained when the EJB calls other beans. Any such interfaces need to use the same invoker used to call the outer EJB in order for the resulting remote homes and interfaces to be compatible with the proxy the client has initiated the call through. The invoker/ejb-ref elements allow one to map from a protocol independent ENC ejb-ref to the home proxy binding for ejb-ref target EJB home that matches the referencing invoker type.

An example of using a custom JRMPInvoker MBean can be found in the org.jboss.test.jrmp package of the testsuite. See A custom JRMPInvoker example that enables compressed sockets for session bean.. illustrates the custom JRMPInvoker configuration and its mapping to a stateless session bean.

A custom JRMPInvoker example that enables compressed sockets for session bean.

// The custom JRMPInvoker jboss-service.xml descriptor

<server>

<mbean code="org.jboss.invocation.jrmp.server.JRMPInvoker"

name="jboss:service=invoker,type=jrmp,socketType=CompressionSocketFactory">

<attribute name="RMIObjectPort">4445</attribute>

<attribute name="RMIClientSocketFactory">

org.jboss.test.jrmp.ejb.CompressionClientSocketFactory

</attribute>

<attribute name="RMIServerSocketFactory">

org.jboss.test.jrmp.ejb.CompressionServerSocketFactory

</attribute>

</mbean>

</server>

 

// The jboss.xml descriptor using the custom invoker

<?xml version="1.0"?>

<!DOCTYPE jboss PUBLIC

"-//JBoss//DTD JBOSS 3.2//EN"

"http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd">

 

<!-- The jboss.xml descriptor for the jrmp-comp.jar ejb unit -->

<jboss>

<enterprise-beans>

<session>

<ejb-name>StatelessSession</ejb-name>

<configuration-name>Standard Stateless SessionBean</configuration-name>

<invoker-bindings>

<invoker>

<invoker-proxy-binding-name>stateless-compression-invoker

</invoker-proxy-binding-name>

<jndi-name>jrmp-compressed/StatelessSession</jndi-name>

</invoker>

</invoker-bindings>

</session>

</enterprise-beans>

 

<invoker-proxy-bindings>

<invoker-proxy-binding>

<name>stateless-compression-invoker</name>

<invoker-mbean>

jboss:service=invoker,type=jrmp,socketType=CompressionSocketFactory

</invoker-mbean>

<proxy-factory>org.jboss.proxy.ejb.ProxyFactory</proxy-factory>

<proxy-factory-config>

<client-interceptors>

<home>

<interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</home>

<bean>

<interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</bean>

</client-interceptors>

</proxy-factory-config>

</invoker-proxy-binding>

</invoker-proxy-bindings>

</jboss>

Here the default JRMPInvoker has been customized to bind to port 4445 and to use custom socket factories that enable compression at the transport level. The StatelessSession EJB invoker-bindings settings specify that the "stateless-compression-invoker" will be used with the home interface bound under the JNDI name "jrmp-compressed/StatelessSession".

An example of using the HttpInvoker to configure a stateless session bean to use the RMI/HTTP protocol can be found in the org.jboss.test.hello testsuite package.See A sample jboss.xml descriptor for enabling RMI/HTTP for a stateless session bean.. illsutrates the custom settings.

A sample jboss.xml descriptor for enabling RMI/HTTP for a stateless session bean.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE jboss PUBLIC

"-//JBoss//DTD JBOSS 3.2//EN"

"http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd">

 

<jboss>

<enterprise-beans>

<session>

<ejb-name>HelloWorldViaHTTP</ejb-name>

<jndi-name>helloworld/HelloHTTP</jndi-name>

<invoker-bindings>

<invoker>

<invoker-proxy-binding-name>stateless-http-invoker</invoker-proxy-binding-name>

</invoker>

</invoker-bindings>

</session>

</enterprise-beans>

 

<invoker-proxy-bindings>

<!-- A custom invoker for RMI/HTTP -->

<invoker-proxy-binding>

<name>stateless-http-invoker</name>

<invoker-mbean>jboss:service=invoker,type=http</invoker-mbean>

<proxy-factory>org.jboss.proxy.ejb.ProxyFactory</proxy-factory>

<proxy-factory-config>

<client-interceptors>

<home>

<interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</home>

<bean>

<interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</bean>

</client-interceptors>

</proxy-factory-config>

</invoker-proxy-binding>

</invoker-proxy-bindings>

</jboss>

Here a custom invoker-proxy-binding named "stateless-http-invoker" is defined. It uses the HttpInvoker MBean as the detached invoker. The "jboss:service=invoker,type=http" name is the default name of the HttpInvoker MBean as found in the http-inovker.sar/META-INF/jboss-service.xml descriptor, and its service descriptor fragment is show here:

<!-- The HTTP invoker service configration

-->

<mbean code="org.jboss.invocation.http.server.HttpInvoker"

name="jboss:service=invoker,type=http">

<!-- Use a URL of the form http://<hostname>:8080/invoker/EJBInvokerServlet

where <hostname> is InetAddress.getHostname value on which the server

is running.

-->

<attribute name="InvokerURLPrefix">http://</attribute>

<attribute name="InvokerURLSuffix">:8080/invoker/EJBInvokerServlet</attribute>

<attribute name="UseHostName">true</attribute>

</mbean>

The client proxy posts the EJB invocation content to the EJBInvokerServlet URL specified in the HttpInvoker service configuration.

The HA JRMPInvoker - Clustered RMI/JRMP Transport

The org.jboss.invocation.jrmp.server.JRMPInvokerHA service is an extension of the JRMPInvoker that is a cluster aware invoker. As of JBoss-3.0.3, the JRMPInvokerHA fully supports all of the attributes of the JRMPInovker . This means that customized bindings of the port, interface and socket transport are available to clustered RMI/JRMP as well. For additional information on the clustering architecture and the implementation of the HA RMI proxies see the JBoss Clustering docs.

The HA HttpInvoker - Clustered RMI/HTTP Transport

The RMI/HTTP layer added in JBoss-3.0.2 has been extended to allow for software load balancing of the invocations in a clustered environment in JBoss-3.0.3. An HA capable extension of the HTTP invoker has been added that borrows much of its functionality from the HA-RMI/JRMP clustering.

To enable HA-RMI/HTTP you need to configure the invokers for the EJB container. This is done through either a jboss.xml descriptor, or the standardjboss.xml descriptor. See A jboss.xml stateless session configuration for HA-RMI/HTTP. shows is an example of a stateless session configuration taken from the org.jboss.test.hello testsuite package.

A jboss.xml stateless session configuration for HA-RMI/HTTP

<jboss>

<enterprise-beans>

<session>

<ejb-name>HelloWorldViaClusteredHTTP</ejb-name>

<jndi-name>helloworld/HelloHA-HTTP</jndi-name>

<invoker-bindings>

<invoker>

<invoker-proxy-binding-name>stateless-httpHA-invoker</invoker-proxy-binding-name>

</invoker>

</invoker-bindings>

<clustered>true</clustered>

</session>

</enterprise-beans>

 

<invoker-proxy-bindings>

<invoker-proxy-binding>

<name>stateless-httpHA-invoker</name>

<invoker-mbean>jboss:service=invoker,type=httpHA</invoker-mbean>

<proxy-factory>org.jboss.proxy.ejb.ProxyFactoryHA</proxy-factory>

<proxy-factory-config>

<client-interceptors>

<home>

<interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</home>

<bean>

<interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</bean>

</client-interceptors>

</proxy-factory-config>

</invoker-proxy-binding>

</invoker-proxy-bindings>

</jboss>

The "stateless-httpHA-invoker" invoker-proxy-binding references the "jboss:service=invoker,type=httpHA" invoker service. This service is configured in the http-invoker.sar/META-INF/jboss-service.xml descriptor, and its default configuration from the sar descriptor is:

<mbean code="org.jboss.invocation.http.server.HttpInvokerHA"

name="jboss:service=invoker,type=httpHA">

<!-- Use a URL of the form

http://<hostname>:8080/invoker/EJBInvokerHAServlet

where <hostname> is InetAddress.getHostname value on which the server

is running.

-->

<attribute name="InvokerURLPrefix">http://</attribute>

<attribute name="InvokerURLSuffix">:8080/invoker/EJBInvokerHAServlet

</attribute>

<attribute name="UseHostName">true</attribute>

</mbean>

The URL used by the invoker proxy is the EJBInvokerHAServlet mapping as deployed on the cluster node. The HttpInvokerHA instances across the cluster form a collection of candidate http URLs that are made available to the client side proxy for failover and/or load balancing.

The EJB Container

An EJB container is the component that manages a particular class of EJB. In JBoss there is one instance of the org.jboss.ejb.Container created for each unique configuration of an EJB that is deployed. The actual object that is instantiated is a subclass of Container and the creation of the container instance is managed by the EJBDeployer MBean.

EJBDeployer MBean

The org.jboss.ejb.EJBDeployer MBean is responsible for the creation of EJB containers. Given an EJB-jar that is ready for deployment, the EJBDeployer will create and initialize the necessary EJB containers, one for each type of EJB. The configurable attributes of the EJBDeployer are:

  • VerifyDeployments: a boolean flag indicating if the EJB verifer should be run. This validates that the EJBs in a deployment unit conform to the EJB 2.0 specification. Setting this to true is useful for ensuring your deployments are valid.
  • VerifierVerbose: A boolean that controls the verboseness of any verification failures/warnings that result from the verification process.
  • StrictVerifier: A boolean that enables/disables strict verification. When strict verification is enable an EJB will deploy only if verifier reports no errors.
  • ValidateDTDs: a boolean flag that indicates if the ejb-jar.xml and jboss.xml descriptors should be validated against their declared DTDs. Setting this to true is useful for ensuring your deployment descriptors are valid.
  • MetricsEnabled: a boolean flag that controls whether container interceptors marked with an metricsEnabled=true attribute should be included in the configuration. This allows one to define a container interceptor configuration that includes metrics type interceptors that can be toggled on and off.
  • WebServiceName: The JMX ObjectName string of the web service MBean that provides support for the dynamic class loading of EJB classes.
  • TransactionManagerServiceName: The JMX ObjectName string of the JTA transaction manager service. This must have an attribute named "TransactionManager" that returns that javax.transaction.TransactionManager instance.

The deployer contains two central methods: deploy and undeploy . The deploy method takes a URL, which either points to an EJB-jar, or to a directory whose structure is the same as a valid EJB-jar (which is convenient for development purposes). Once a deployment has been made, it can be undeployed by calling undeploy on the same URL. A call to deploy with an already deployed URL will cause an undeploy , followed by deployment of the URL, such as a re-deploy. JBoss has support for full re-deployment of both implementation and interface classes, and will reload any changed classes. This will allow you to develop and update EJBs without ever stopping a running server.

During the deployment of the EJB jar the EJBDeployer and its associated classes perform three main functions, verify the EJBs, create a container for each unique EJB, initialize the container with the deployment configuration information. We will talk about each function in the following sections.

Verifying EJB deployments

When the VerifyDeployments attribute of the that the EJBDeployer is true, the deployer performs a verification of EJBs in the deployment. The verification checks that an EJB meets EJB specification compliance. This entails validating that the EJB deployment unit contains the required home and remote, local-home and local interfaces, and that the objects appearing in these interfaces are of the proper types, and that the required methods are present in the implementation class. This is a useful behavior that is enabled by default since there are a number of steps that an EJB developer and deployer must perform correctly to construct a proper EJB jar and, it is easy to make a mistake. The verification stage attempts to catch any errors and fail the deployment with an error that indicates what needs to be corrected.

Probably the most problematic aspect of writing EJBs is the fact that there is a disconnection between the bean implementation and its remote and home interfaces, as well as its deployment descriptor configuration. It is easy to have these separate elements get out of synch. One tool that helps eliminate this problem is XDoclet, an extension of the standard JavaDoc Doclet engine. It works off of custom JavaDoc tags in the EJB bean implementation class and creates the remote and home interfaces as well as the deployment descriptors. See the XDoclet home page here http://sourceforge.net/projects/xdoclet for additional details.

Deploying EJBs Into Containers

The most important role performed by the EJBDeployer is the creation of an EJB container and the deployment of the EJB into the container. The deployment phase consists of iterating over EJBs in an EJB jar, and extracting the bean classes and their metadata as described by the ejb-jar.xml and jboss.xml deployment descriptors. For each EJB in the EJB jar, the following steps are performed:

  1. Create subclass of org.jboss.ejb.Container depending on the type of the EJB, Stateless, Stateful, BMP Entity, CMP Entity, or MessageDriven. The container is assigned a unique ClassLoader from which it can load local resources. The uniqueness of the ClassLoader is also used to isolate the standard "java:comp" JNDI namespace from other J2EE components.
  2. Set all container configurable attributes from a merge of the jboss.xml and standardjboss.xml descriptors.
  3. Create and add the container interceptors as configured for the container.
  4. Associate the container with an application object. This application object represents a J2EE enterprise application and may contain multiple EJBs and web contexts.

If all EJBs are successfully deployed, the application is started which in turn starts all containers and makes the EJBs available to clients. If any EJB fails to deploy, a deployment exception is thrown and the deployment module is failed.

Container configuration information

JBoss externalizes most if not all of the setup of the EJB containers using an XML file that conforms to the jboss_3_2.dtd. The section of the jboss_3_2 DTD that relates to container configuration information is shown in See The jboss_3_2 DTD elements related to container configuration.. .

FIGURE 5-6. The jboss_3_2 DTD elements related to container configuration.

The container-configuration element and its subelements specify container configuration settings for a type of container as given by the container-name element. Each configuration specifies information such as the default invoker type, the container interceptor makeup, instance caches/pools and their sizes, persistence manager, security, and so on. Because this is a large amount of information that requires a detailed understanding of the JBoss container architecture, JBoss ships with a standard configuration for the four types of EJBs. This configuration file is called standardjboss.xml and it is located in the conf directory of any configuration file set that uses EJBs. See An example of a complex container-configuration element from the server/default/conf/standardjboss.xml file.. gives a sample of a configuration from the standardjboss.xml.

An example of a complex container-configuration element from the server/default/conf/standardjboss.xml file.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE jboss PUBLIC

"-//JBoss//DTD JBOSS 3.2//EN"

"http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd">

 

<jboss>

...

<container-configuration>

<container-name>Standard CMP 2.x EntityBean</container-name>

<call-logging>false</call-logging>

<invoker-proxy-binding-name>entity-rmi-invoker</invoker-proxy-binding-name>

<sync-on-commit-only>false</sync-on-commit-only>

<container-interceptors>

<interceptor>org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.TxInterceptorCMT</interceptor>

<interceptor metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityCreationInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityLockInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityInstanceInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityReentranceInterceptor</interceptor>

<interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntitySynchronizationInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.cmp.jdbc.JDBCRelationInterceptor</interceptor>

</container-interceptors>

<instance-pool>org.jboss.ejb.plugins.EntityInstancePool</instance-pool>

<instance-cache>org.jboss.ejb.plugins.InvalidableEntityInstanceCache</instance-cache>

<persistence-manager>org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager</persistence-manager>

<locking-policy>org.jboss.ejb.plugins.lock.QueuedPessimisticEJBLock</locking-policy>

<container-cache-conf>

<cache-policy>org.jboss.ejb.plugins.LRUEnterpriseContextCachePolicy</cache-policy>

<cache-policy-conf>

<min-capacity>50</min-capacity>

<max-capacity>1000000</max-capacity>

<overager-period>300</overager-period>

<max-bean-age>600</max-bean-age>

<resizer-period>400</resizer-period>

<max-cache-miss-period>60</max-cache-miss-period>

<min-cache-miss-period>1</min-cache-miss-period>

<cache-load-factor>0.75</cache-load-factor>

</cache-policy-conf>

</container-cache-conf>

<container-pool-conf>

<MaximumSize>100</MaximumSize>

</container-pool-conf>

<commit-option>B</commit-option>

</container-configuration>

...

</container-configurations>

</jboss>

See The jboss_3_2 DTD elements related to container configuration..and See An example of a complex container-configuration element from the server/default/conf/standardjboss.xml file.. demonstrate how extensive the container configuration options are. The container configuration information can be specified at two levels. The first is in the standardjboss.xml file contained in the configuration file set directory. The second is at the ejb-jar level. By placing a jboss.xml file in the ejb-jar META-INF directory, you can specify either overrides for container configurations in the standardjboss.xml file, or entirely new named container configurations. This provides great flexibility in the configuration of containers. As you have seen, all container configuration attributes have been externalized and as such are easily modifiable. Knowledgeable developers can even implement specialized container components, such as instance pools or caches, and easily integrate them with the standard container configurations to optimize behavior for a particular application or environment.

FIGURE 5-7. The jboss.xml descriptor EJB to container configuration mapping elements

How an EJB deployment chooses its container configuration is based on the explicit or implict jboss/enterprise-beans/<type>/configuration-name element. See The jboss.xml descriptor EJB to container configuration mapping elements. shows the jboss.xml DTD fragment that shows how an EJB can declare which container configuration it should use.

The configuration-name element is a link to a container-configurations/container-configuration element in See The jboss_3_2 DTD elements related to container configuration... It specifies which container configuration to use for the referring EJB. The link is from a configuration-name element to a container-name element. You are able to specify container configurations per class of EJB by including a container-configuration element in the EJB definition. Typically one does not define completely new container configurations, although this is supported. The typical usage of a jboss.xml level container-configuration is to override one or more aspects of a container-configuration coming from the standardjboss.xml descriptor. This is done by specifying conatiner-configuration that references the name of an existing standardjboss.xml container-configuration/container-name as the value for the container-configuration/extends attribute. See An example of overriding the standardjboss.xml container stateless session beans configuration to enable secured access.. shows an example of defining a new "Secured Stateless SessionBean" configuration that is an extension of the standard stateless session configuration whose name is "Standard Stateless SessionBean".

An example of overriding the standardjboss.xml container stateless session beans configuration to enable secured access.

<?xml version="1.0"?>

<jboss>

<enterprise-beans>

<session>

<ejb-name>EchoBean</ejb-name>

<configuration-name>Secured Stateless SessionBean</configuration-name>

...

</session>

</enterprise-beans>

<container-configurations>

<container-configuration extends="Standard Stateless SessionBean">

<container-name>Secured Stateless SessionBean</container-name>

<!-- Override the container security domain -->

<security-domain>java:/jaas/my-security-domain</security-domain>

</container-configuration>

</container-configurations>

</jboss>

If an EJB does not provide a container configuration specification in the deployment unit ejb-jar, the container factory chooses a container configuration from the standardjboss.xml descriptor based on the type of the EJB. So, in reality there is an implicit configuration-name element for every type of EJB, and the mappings from the EJB type to default container configuration name are as follows:

  • container-managed persistence entity version 2.0 = Standard CMP 2.x EntityBean
  • container-managed persistence entity version 1.1 = Standard CMP EntityBean
  • bean-managed persistence entity = Standard BMP EntityBean
  • stateless session = Standard Stateless SessionBean
  • stateful session = Standard Stateful SessionBean
  • message driven = Standard Message Driven Bean

So, it is not necessary to indicate which container configuration an EJB is using if you want to use the default based on the bean type. It probably provides for a more self-contained descriptor to include the configuration-name element, but this is a matter of style.

Now that you know how to specify which container configuration an EJB is using, and that you can define a deployment unit level override, the question is what are all of those container-configuration child elements? This question will be addressed element by element in the following sections. A number of the elements specify interface class implementations whose configuration is affected by other elements, so before starting in on the configuration elements you need to understand the org.jboss.metadata.XmlLoadable interface.

The XmlLoadable interface is a simple interface that consists of a single method. The interface definition is:

import org.w3c.dom.Element;

public interface XmlLoadable

{

public void importXml(Element element) throws Exception;

}

Classes implement this interface to allow their configuration to be specified via an XML document fragment. The root element of the document fragment is what would be passed to the importXml method. You will see a few examples of this as the container configuration elements are described in the following sections.

The container-name Element

The container-name element specifies a unique name for a given configuration. EJBs link to a particular container configuration by setting their configuration-name element to the value of the container-name for the container configuration.

The call-logging Element

The call-logging element expects a boolean (true or false) as its value to indicate whether or not the LogInterceptor should log method calls to a container. This is somewhat obsolete with the change to log4j, which provides a fine-grained logging API.

The invoker-proxy-binding-name Element

The invoker-proxy-binding-name element specifies the name of the default invoker to use. In the absence of a bean level invoker-bindings specification, the invoker-proxy-binding whose name matches the invoker-proxy-binding-name element value will be used to create home and remote proxies.

The container-interceptors Element

The container-interceptors element specifies one or more interceptor elements that are to be configured as the method interceptor chain for the container. The value of the interceptor element is a fully qualified class name of an org.jboss.ejb.Interceptor interface implementation. The container interceptors form a linked-list structure through which EJB method invocations pass. The first interceptor in the chain is invoked when the MBeanServer passes a method invocation to the container. The last interceptor invokes the business method on the bean. We will discuss the Interceptor interface latter in this chapter when we talk about the container plugin framework. Generally, care must be taken when changing an existing standard EJB interceptor configuration as the EJB contract regarding security, transactions, persistence, and thread safety derive from the interceptors.

The instance-pool and container-pool-conf Elements

 

FIGURE 5-8. The instance-pool and container-pool-conf elements

The instance-pool element specifies the fully qualified class name of an org.jboss.ejb.InstancePool interface implementation to use as the container InstancePool . We will discuss the InstancePool interface in detail latter in this chapter when we talk about the container plugin framework.

The container-pool-conf is passed to the InstancePool implementation class given by the instance-pool element if it implements XmlLoadable interface. All current JBoss InstancePool implementations derive from the org.jboss.ejb.plugins.AbstractInstancePool class and it provides support for the MinimumSize, MaximumSize, strictMaximumSize and strictTimeout container-pool-conf child elements. The MinimumSize element gives the minimum number of instances to keep in the pool, although JBoss does not currently seed an InstancePool to the MinimumSize value.

The MaximumSize specifies the maximum number of pool instances that are allowed. The default use of MaxiumSize may not be what you expect. The pool MaximumSize is the maximum number of EJB instances that are kept available, but additional instances can be created if the number of concurrent requests exceeds the MaximumSize value. If you want to limit the maximum concurrency of an EJB to the pool MaximumSize, you need to set the strictMaximumSize element to true. When strictMaximumSize is true, only MaximumSize EJB instances may be active. When there are MaximumSize active instances, any subsequent requests will be blocked until an instance is freed back to the pool. The default value for strictMaximumSize is false. How long a request blocks waiting for an instance pool object is controlled by the strictTimeout element. The strictTimeout defines the time in milliseconds to wait for an instance to be returned to the pool when there are MaximumSize active instances. A value less than or equal to 0 will mean not to wait at all. When a request times out waiting for an instance a java.rmi.ServerException is generated and the call aborted. This is parsed as a Long so the maximum possible wait time is 9,223,372,036,854,775,807 or about 292,471,208 years, and this is the default value.

The instance-cache and container-cache-conf Elements

 

FIGURE 5-9. The instance-cache and container-cache-conf and related elements

The instance-cache element specifies the fully qualified class name of the org.jboss.ejb.InstanceCache interface implementation. This element is only meaningful for entity and stateful session beans as these are the only EJB types that have an associated identity. We will discuss the InstanceCache interface in detail latter in this chapter when we talk about the container plugin framework.

The container-cache-conf element is passed to the InstanceCache implementation if it supports the XmlLoadable interface. All current JBoss InstanceCache implementations derive from the org.jboss.ejb.plugins.AbstractInstanceCache class and it provides support for the XmlLoadable interface and uses the cache-policy child element as the fully qualified class name of an org.jboss.util.CachePolicy implementation that is used as the instance cache store. The cache-policy-conf child element is passed to the CachePolicy implementation if it supports the XmlLoadable interface. If it does not, the cache-policy-conf will silently be ignored.

There are two JBoss implementations of CachePolicy used by the standardjboss.xml configuration that support the current array of cache-policy-conf child elements. The classes are org.jboss.ejb.plugins.LRUEnterpriseContextCachePolicy and org.jboss.ejb.plugins.LRUStatefulContextCachePolicy . The LRUEnterpriseContextCachePolicy is used by entity bean containers while the LRUStatefulContextCachePolicy is used by stateful session bean containers. Both cache policies support the following cache-policy-conf child elements:

  • min-capacity, specifies the minimum capacity of this cache
  • max-capacity, specifies the maximum capacity of the cache, which cannot be less than min-capacity.
  • overager-period, specifies the period in seconds between runs of the overager task. The purpose of the overager task is to see if the cache contains beans with an age greater than the max-bean-age element value. Any beans meeting this criterion will be passivated.
  • max-bean-age, specifies the maximum period of inactivity in seconds a bean can have before it will be passivated by the overager process.
  • resizer-period, specifies the period in seconds between runs of the resizer task. The purpose of the resizer task is to contract or expand the cache capacity based on the remaining three element values in the following way. When the resizer task executes it checks the current period between cache misses, and if the period is less than the min-cache-miss-period value the cache is expanded up to the max-capacity value using the cache-load-factor. If instead the period between cache misses is greater than the max-cache-miss-period value the cache is contracted using the cache-load-factor.
  • max-cache-miss-period, specifies the time period in seconds in which a cache miss should signal that the cache capacity be contracted. It is equivalent to the minimum miss rate that will be tolerated before the cache is contracted.
  • min-cache-miss-period, specifies the time period in seconds in which a cache miss should signal that the cache capacity be expanded. It is equivalent to the maximum miss rate that will be tolerated before the cache is expanded.
  • cache-load-factor, specifies the factor by which the cache capacity is contracted and expanded. The factor should be less than 1. When the cache is contracted the capacity is reduced so that the current ratio of beans to cache capacity is equal to the cache-load-factor value. When the cache is expanded the new capacity is determined as current-capacity * 1/cache-load-factor. The actual expansion factor may be as high as 2 based on an internal algorithm based on the number of cache misses. The higher the cache miss rate the closer the true expansion factor will be to 2.

The LRUStatefulContextCachePolicy also supports the remaining child elements:

  • remover-period, specifies the period in seconds between runs of the remover task. The remover task removes passivated beans that have not been accessed in more than max-bean-life seconds. This task prevents stateful session beans that were not removed by users from filling up the passivation store.
  • max-bean-life, specifies the maximum period of inactivity in seconds that a bean can exist before being removed from the passivation store.

An alternative cache policy implementation is the org.jboss.ejb.plugins.NoPassivationCachePolicy class, which simply never passivates instances. It uses an in-memory HashMap implementation that never discards instances unless they are explicitly removed. This class does not support any of the cache-policy-conf configuration elements.

The persistence-manager Element

The persistence-manager element value specifies the fully qualified class name of the persistence manager implementation. The type of the implementation depends on the type of EJB. For stateful session beans it must be an implementation of the org.jboss.ejb.StatefulSessionPersistenceManager interface. For BMP entity beans it must be an implementation of the org.jboss.ejb.EntityPersistenceManager interface, while for CMP entity beans it must be an implementation of the org.jboss.ejb.EntityPersistenceStore interface.

The web-class-loader Element

The web-class-loader element specifies a subclass of org.jboss.web.WebClassLoader that is used in conjunction with the WebService mbean to allow dynamic loading of resources and classes from deployed ears, ejb jars and wars. A WebClassLoader is associated with a Container and must have an org.jboss.mx.loading.UnifiedClassLoader as its parent. It overrides the getURLs() method to return a different set of URLs for remote loading than what is used for local loading.

WebClassLoader has two methods meant to be overriden by subclasses: getKey() and getBytes() . The latter is a no-op in this implementation and should be overriden by subclasses with bytecode generation ability, such as the classloader used by the iiop module.

WebClassLoader subclasses must have a constructor with the same signature as the WebClassLoader(ObjectName containerName, UnifiedClassLoader parent) constructor.

The locking-policy Element

The locking-policy element gives the fully qualified class name of the EJB lock implementation to use. This class must implement the org.jboss.ejb.BeanLock interface. The current JBoss versions include:

  • org.jboss.ejb.plugins.lock.MethodOnlyEJBLock , an implementation that does not perform any pessimistic transactional locking. It does provide locking for single-threaded non-reentrant beans. This locking policy is no longer supported as of 3.0.4.
  • org.jboss.ejb.plugins.lock.QueuedPessimisticEJBLock , an implementation that holds threads awaiting the transactional lock to be freed in a fair FIFO queue. Non-transactional threads are also put into this wait queue as well. This class pops the next waiting transaction from the queue and notifies only those threads waiting associated with that transaction. The QueuedPessimisticEJBLock is the current default used by the standard configurations.
  • org.jboss.ejb.plugins.lock.SimpleReadWriteEJBLock , This lock allows multiple read locks concurrently. Once a writer has requested the lock, future read-lock requests whose transactions do not already have the read lock will block until all writers are done -- then all the waiting readers will concurrently go (depending on the reentrant setting / methodLock). A reader who promotes gets first crack at the write lock -- ahead of other waiting writers. If there is already a reader that is promoting, we throw an inconsistent read exception. Of course, writers have to wait for all read-locks to release before taking the write lock.
  • org.jboss.ejb.plugins.lock.NoLock , an anti-locking policy used with the "Instance Per Transaction *" container configurations.
  • org.jboss.ejb.plugins.lock.JDBCOptimisticLock , an alternate optimistic locking policy for use with the "Instance Per Transaction CMP 2.x EntityBean" container configuration.

We will talk in more detail about the locking policy usage in section Entity Bean Locking and Deadlock Detection

The commit-option and optiond-refresh-rate Element

The commit-option value specifies the EJB entity bean persistent storage commit option. It must be one of A, B, C or D. The meaning of the option values is:

  • A, the container caches the beans state between transactions. This option assumes that the container is the only user accessing the persistent store. This assumption allows the container to synchronize the in-memory state from the persistent storage only when absolutely necessary. This occurs before the first business method executes on a found bean or after the bean is passivated and reactivated to serve another business method. This behavior is independent of whether the business method executes inside a transaction context.
  • B, the container caches the bean state between transactions. However, unlike option A the container does not assume exclusive access to the persistent store. Therefore, the container will synchronize the in-memory state at the beginning of each transaction. Thus, business methods executing in a transaction context don't see much benefit from the container caching the bean, whereas business methods executing outside a transaction context (transaction attributes Never, NotSupported or Supports) access the cached (and potentially invalid) state of the bean.
  • C, the container does not cache bean instances. The in-memory state must be synchronized on every transaction start. For business methods executing outside a transaction the synchronization is still performed, but the ejbLoad executes in the same transaction context as that of the caller.
  • D, is a JBoss specific feature which is not described in the EJB specification. It is a lazy read scheme where bean state is cached between transactions as with option A, but the state is periodically resynchronized with that of the persistent store. The default time between reloads is 30 seconds, but may configured using the optiond-refresh-rate element.
The security-domain Element

Inside the EJB org.jboss.ejb.Container classThe security-domain element specifies the JNDI name of the object that implements the org.jboss.security.AuthenticationManager and org.jboss.security.RealmMapping interfaces. Usually one specifies the security-domain globally under the jboss root element so that all EJBs in a given deployment are secured. The details of the security manager interfaces and configuring the security layer are discussed in Security on JBoss - J2EE Security Configuration and Architecture.

cluster-config

 

FIGURE 5-10. The cluster-config and related elements

The cluster-config element allows to specify cluster specific settings for all EJBs that use the container configuration. Specficiation of the cluster configuration may be done at the container configuration level or at the individual EJB deployment level.

The partition-name element indicates where to find the org.jboss.ha.framework.interfaces.HAPartition interface to be used by the container to exchange clustering information. This is not the full JNDI name under which HAPartition is bound. Rather, should correspond to the PartitionName attribute of the ClusterPartitionMBean service that is managing the desired cluster. The actual JNDI name of the HAPartition binding will be formed by appending"/HASessionState/" to the partition-name value. The default value is "DefaultPartition".

The home-load-balance-policy element indicates the java class name to be used to load balance calls made on the home proxy. The class must implement the org.jboss.ha.framework.interface.LoadBalancePolicy interface. The default policy is org.jboss.ha.framework.interfaces.RoundRobin .

The bean-load-balance-policy element indicates the java class name to be used to load balance calls in the bean proxy. The class must implement the org.jboss.ha.framework.interface.LoadBalancePolicy interface. The defaults are:

  • For entity beans and stateful session beands, org.jboss.ha.framework.interfaces.FirstAvailable
  • For stateless session beans, org.jboss.ha.framework.interfaces.RoundRobin

The session-state-manager-jndi-name element indicates the name of the org.jboss.ha.framework.interfaces.HASessionState to be used by the container as a backend for state session management in the cluster. Unlike the partition-name element, this is a JNDI name under which the HASessionState implementation is bound. The default location used is "/HASessionState/Default".

depends
 

The depends element gives a JMX ObjectName of a service on which the container or ejb depends. Specification of explicit dependencies on other services avoids having to rely on the deployment order being after the required services are started.

Container Plug-in Framework

The JBoss EJB container uses a framework pattern that allows one to change implementations of various aspects of the container behavior. The container itself does not perform any significant work other than connecting the various behavioral components together. Implementations of the behavioral components are referred to as plugins, because you can plug in a new implementation by changing a container configuration. Examples of plug-in behavior you may want to change include persistence management, object pooling, object caching, container invokers and interceptors. There are four subclasses of the org.jboss.ejb.Container class, each one implementing a particular bean type:

  • org.jboss.ejb.EntityContainer handles javax.ejb.EntityBean types
  • org.jboss.ejb.StatelessSessionContainer handles Stateless javax.ejb.SessionBean types
  • org.jboss.ejb.StatefulSessionContainer handles Stateful javax.ejb.SessionBean types

org.jboss.ejb.MessageDrivenContainer handles javax.ejb.MessageDrivenBean types

The EJB containers delegate much of their behavior to components known as container plug-ins. The interfaces that make up the container plugin points include the following:

  • org.jboss.ejb.ContainerPlugin
  • org.jboss.ejb.ContainerInvoker
  • org.jboss.ejb.Interceptor
  • org.jboss.ejb.InstancePool
  • org.jboss.ejb.InstanceCache
  • org.jboss.ejb.EntityPersistanceManager
  • org.jboss.ejb.EntityPersistanceStore
  • org.jboss.ejb.StatefulSessionPersistenceManager

The container's main responsibility is to manage its plug-ins. This means ensuring that the plug-ins have all the information they need to implement their functionality.

org.jboss.ejb.ContainerPlugin

The ContainerPlugin interface is the parent interface of all container plug-in interfaces. It provides a callback that allows a container to provide each of its plug-ins a pointer to the container the plug-in is working on behalf of. The ContainerPlugin interface is given in See The org.jboss.ejb.ContainerPlugin interface..

The org.jboss.ejb.ContainerPlugin interface

public interface ContainerPlugin extends org.jboss.system.Service

{

/** This callback is set by the container so that the plugin

* may access its container

*

* @param con the container which owns the plugin

*/

public void setContainer(Container con);

}

org.jboss.ejb.Interceptor

The Interceptor interface enables one to build a chain of method interceptors through which each EJB method invocation must pass. The Interceptor interface is given in See The org.jboss.ejb.Interceptor interface..

The org.jboss.ejb.Interceptor interface

import org.jboss.invocation.Invocation;

public interface Interceptor extends ContainerPlugin

{

public void setNext(Interceptor interceptor);

public Interceptor getNext();

public Object invokeHome(Invocation mi) throws Exception;

public Object invoke(Invocation mi) throws Exception;

}

All interceptors defined in the container configuration are created and added to the container interceptor chain by the EJBDeployer. The last interceptor is not added by the deployer but rather by the container itself because this is the interceptor that interacts with the EJB bean implementation.

The order of the interceptor in the chain is important. The idea behind ordering is that interceptors that are not tied to a particular EnterpriseContext instance are positioned before interceptors that interact with caches and pools.

Implementers of the Interceptor interface form a linked-list like structure through which the Invocation object is passed. The first interceptor in the chain is invoked when an invoker passes a Invocation to the container via the JMX bus. The last interceptor invokes the business method on the bean. There are usually on the order of five interceptors in a chain depending on the bean type and container configuration. Interceptor semantic complexity ranges from simple to complex. An example of a simple interceptor would be LoggingInterceptor, while a complex example is EntitySynchronizationInterceptor.

One of the main advantages of an interceptor pattern is flexibility in the arrangement of interceptors. Another advantage is the clear functional distinction between different interceptors. For example, logic for transaction and security is cleanly separated between the TXInterceptor and SecurityInterceptor respectively.

If any of the interceptors fail, the call is terminated at that point. This is a fail-quickly type of semantic. For example, if a secured EJB is accessed without proper permissions, the call will fail as the SecurityInterceptor before any transactions are started or instances caches are updated.

org.jboss.ejb.InstancePool

An InstancePool is used to manage the EJB instances that are not associated with any identity. The pools actually manage subclasses of the org.jboss.ejb.EnterpriseContext objects that aggregate unassociated bean instances and related data. See The org.jboss.ejb.InstancePool interface. gives the InstancePool interface.

The org.jboss.ejb.InstancePool interface

public interface InstancePool extends ContainerPlugin

{

/** Get an instance without identity. Can be used

* by finders and create-methods, or stateless beans

*

* @return Context /w instance

* @exception RemoteException

*/

public EnterpriseContext get() throws Exception;

/** Return an anonymous instance after invocation.

*

* @param ctx

*/

public void free(EnterpriseContext ctx);

/** Discard an anonymous instance after invocation.

* This is called if the instance should not be reused,

* perhaps due to some exception being thrown from it.

*

* @param ctx

*/

public void discard(EnterpriseContext ctx);

/**

* Return the size of the pool.

*

* @return the size of the pool.

*/

public int getCurrentSize();

/**

* Get the maximum size of the pool.

*

* @return the size of the pool.

*/

public int getMaxSize();

}

Depending on the configuration, a container may choose to have a certain size of the pool contain recycled instances, or it may choose to instantiate and initialize an instance on demand.

The pool is used by the InstanceCache implementation to acquire free instances for activation, and it is used by interceptors to acquire instances to be used for Home interface methods (create and finder calls).

org.jboss.ebj.InstanceCache

The container InstanceCache implementation handles all EJB-instances that are in an active state, meaning bean instances that have an identity attached to them. Only entity and stateful session beans are cached, as these are the only bean types that have state between method invocations. The cache key of an entity bean is the bean primary key. The cache key for a stateful session bean is the session id. See The org.jboss.ejb.InstanceCache interface. gives the InstanceCache interface.

The org.jboss.ejb.InstanceCache interface

public interface InstanceCache extends ContainerPlugin

{

/**

* Gets a bean instance from this cache given the identity.

* This method may involve activation if the instance is not
* in the cache.

* Implementation should have O(1) complexity.

* This method is never called for stateless session beans.

*

* @param id the primary key of the bean

* @return the EnterpriseContext related to the given id

* @exception RemoteException in case of illegal calls

* (concurrent / reentrant), NoSuchObjectException if

* the bean cannot be found.

* @see #release

*/

public EnterpriseContext get(Object id)

throws RemoteException, NoSuchObjectException;

/**

* Inserts an active bean instance after creation or activation.

* Implementation should guarantee proper locking and O(1) complexity.

*

* @param ctx the EnterpriseContext to insert in the cache

* @see #remove

*/

public void insert(EnterpriseContext ctx);

/**

* Releases the given bean instance from this cache.

* This method may passivate the bean to get it out of the cache.

* Implementation should return almost immediately leaving the

* passivation to be executed by another thread.

*

* @param ctx the EnterpriseContext to release

* @see #get

*/

public void release(EnterpriseContext ctx);

/** Removes a bean instance from this cache given the identity.

* Implementation should have O(1) complexity and guarantee

* proper locking.

*

* @param id the pimary key of the bean

* @see #insert

*/

public void remove(Object id);

/** Checks whether an instance corresponding to a particular

* id is active

*

* @param id the pimary key of the bean

* @see #insert

*/

public boolean isActive(Object id);

}

In addition to managing the list of active instances, the InstanceCache is also responsible for activating and passivating instances. If an instance with a given identity is requested, and it is not currently active, the InstanceCache must use the InstancePool to acquire a free instance, followed by the persistence manager to activate the instance. Similarly, if the InstanceCache decides to passivate an active instance, it must call the persistence manager to passivate it and release the instance to the InstancePool.

org.jboss.ejb.EntityPersistenceManager

The EntityPersistenceManager is responsible for the persistence of EntityBeans. This includes the following:

  • Creating an EJB instance in a storage
  • Loading the state of a given primary key into an EJB instance
  • Storing the state of a given EJB instance
  • Removing an EJB instance from storage
  • Activating the state of an EJB instance
  • Passivating the state of an EJB instance

See The org.jboss.ejb.EntityPersistenceManager interface. gives the EntityPersistenceManager interface.

The org.jboss.ejb.EntityPersistenceManager interface

public interface EntityPersistenceManager extends ContainerPlugin

{

/**

* Returns a new instance of the bean class or a subclass of the bean class.

*

* @return the new instance

*/

Object createBeanClassInstance() throws Exception;

/**

* This method is called whenever an entity is to be created. The

* persistence manager is responsible for calling the ejbCreate method

* on the instance and to handle the results properly wrt the persistent

* store.

*

* @param m the create method in the home interface that was

* called

* @param args any create parameters

* @param instance the instance being used for this create call

*/

void createEntity(Method m,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called whenever an entity is to be created. The

* persistence manager is responsible for calling the ejbPostCreate method

* on the instance and to handle the results properly wrt the persistent

* store.

*

* @param m the create method in the home interface that was

* called

* @param args any create parameters

* @param instance the instance being used for this create call

*/

void postCreateEntity(Method m,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called when single entities are to be found. The

* persistence manager must find out whether the wanted instance is

* available in the persistence store, and if so it shall use the

* ContainerInvoker plugin to create an EJBObject to the instance, which

* is to be returned as result.

*

* @param finderMethod the find method in the home interface that was

* called

* @param args any finder parameters

* @param instance the instance to use for the finder call

* @return an EJBObject representing the found entity

*/

Object findEntity(Method finderMethod,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called when collections of entities are to be found. The

* persistence manager must find out whether the wanted instances are

* available in the persistence store, and if so it shall use the

* ContainerInvoker plugin to create EJBObjects to the instances, which are

* to be returned as result.

*

* @param finderMethod the find method in the home interface that was

* called

* @param args any finder parameters

* @param instance the instance to use for the finder call

* @return an EJBObject collection representing the found

* entities

*/

Collection findEntities(Method finderMethod,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called when an entity shall be activated. The persistence

* manager must call the ejbActivate method on the instance.

*

* @param instance the instance to use for the activation

*

* @throws RemoteException thrown if some system exception occurs

*/

void activateEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is called whenever an entity shall be load from the

* underlying storage. The persistence manager must load the state from

* the underlying storage and then call ejbLoad on the supplied instance.

*

* @param instance the instance to synchronize

*

* @throws RemoteException thrown if some system exception occurs

*/

void loadEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is used to determine if an entity should be stored.

*

* @param instance the instance to check

* @return true, if the entity has been modified

* @throws Exception thrown if some system exception occurs

*/

boolean isModified(EntityEnterpriseContext instance) throws Exception;

/**

* This method is called whenever an entity shall be stored to the

* underlying storage. The persistence manager must call ejbStore on the

* supplied instance and then store the state to the underlying storage.

*

* @param instance the instance to synchronize

*

* @throws RemoteException thrown if some system exception occurs

*/

void storeEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is called when an entity shall be passivate. The persistence

* manager must call the ejbPassivate method on the instance.

*

* @param instance the instance to passivate

*

* @throws RemoteException thrown if some system exception occurs

*/

void passivateEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is called when an entity shall be removed from the

* underlying storage. The persistence manager must call ejbRemove on the

* instance and then remove its state from the underlying storage.

*

* @param instance the instance to remove

*

* @throws RemoteException thrown if some system exception occurs

* @throws RemoveException thrown if the instance could not be removed

*/

void removeEntity(EntityEnterpriseContext instance)

throws RemoteException, RemoveException;

}

As per the EJB 2.0 specification, JBoss supports two entity bean persistence semantics: Container Managed Persistence (CMP) and Bean Managed Persistence (BMP). The CMP implementation uses an implementation of the org.jboss.ejb.EntityPersistanceStore interface. By default this is the org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager which is the entry point for the CMP2 persistence engine. See The org.jboss.ejb.EntityPersistanceStore interface. gives the EntityPersistanceStore interface.

The org.jboss.ejb.EntityPersistanceStore interface

public interface EntityPersistenceStore extends ContainerPlugin

{

/**

* Returns a new instance of the bean class or a subclass of the bean class.

*

* @return the new instance

*

* @throws Exception

*/

Object createBeanClassInstance() throws Exception;

/**

* Initializes the instance context.

*

* <p>This method is called before createEntity, and should

* reset the value of all cmpFields to 0 or null.

*

* @param ctx

*

* @throws RemoteException

*/

void initEntity(EntityEnterpriseContext ctx);

/**

* This method is called whenever an entity is to be created.

* The persistence manager is responsible for handling the results properly

* wrt the persistent store.

*

* @param m the create method in the home interface that was

* called

* @param args any create parameters

* @param instance the instance being used for this create call

* @return The primary key computed by CMP PM or null for BMP

*

* @throws Exception

*/

Object createEntity(Method m,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called when single entities are to be found. The

* persistence manager must find out whether the wanted instance is

* available in the persistence store, if so it returns the primary key of

* the object.

*

* @param finderMethod the find method in the home interface that was

* called

* @param args any finder parameters

* @param instance the instance to use for the finder call

* @return a primary key representing the found entity

*

* @throws RemoteException thrown if some system exception occurs

* @throws FinderException thrown if some heuristic problem occurs

*/

Object findEntity(Method finderMethod,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called when collections of entities are to be found. The

* persistence manager must find out whether the wanted instances are

* available in the persistence store, and if so it must return a

* collection of primaryKeys.

*

* @param finderMethod the find method in the home interface that was

* called

* @param args any finder parameters

* @param instance the instance to use for the finder call

* @return an primary key collection representing the found

* entities

*

* @throws RemoteException thrown if some system exception occurs

* @throws FinderException thrown if some heuristic problem occurs

*/

Collection findEntities(Method finderMethod,

Object[] args,

EntityEnterpriseContext instance)

throws Exception;

/**

* This method is called when an entity shall be activated.

*

* <p>With the PersistenceManager factorization most EJB calls should not

* exists However this calls permits us to introduce optimizations in

* the persistence store. Particularly the context has a

* "PersistenceContext" that a PersistenceStore can use (JAWS does for

* smart updates) and this is as good a callback as any other to set it

* up.

* @param instance the instance to use for the activation

*

* @throws RemoteException thrown if some system exception occurs

*/

void activateEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is called whenever an entity shall be load from the

* underlying storage. The persistence manager must load the state from

* the underlying storage and then call ejbLoad on the supplied instance.

*

* @param instance the instance to synchronize

*

* @throws RemoteException thrown if some system exception occurs

*/

void loadEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is used to determine if an entity should be stored.

*

* @param instance the instance to check

* @return true, if the entity has been modified

* @throws Exception thrown if some system exception occurs

*/

boolean isModified(EntityEnterpriseContext instance) throws Exception;

/**

* This method is called whenever an entity shall be stored to the

* underlying storage. The persistence manager must call ejbStore on the

* supplied instance and then store the state to the underlying storage.

*

* @param instance the instance to synchronize

*

* @throws RemoteException thrown if some system exception occurs

*/

void storeEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is called when an entity shall be passivate. The persistence

* manager must call the ejbPassivate method on the instance.

*

* <p>See the activate discussion for the reason for exposing EJB callback

* calls to the store.

*

* @param instance the instance to passivate

*

* @throws RemoteException thrown if some system exception occurs

*/

void passivateEntity(EntityEnterpriseContext instance)

throws RemoteException;

/**

* This method is called when an entity shall be removed from the

* underlying storage. The persistence manager must call ejbRemove on the

* instance and then remove its state from the underlying storage.

*

* @param instance the instance to remove

*

* @throws RemoteException thrown if some system exception occurs

* @throws RemoveException thrown if the instance could not be removed

*/

void removeEntity(EntityEnterpriseContext instance)

throws RemoteException, RemoveException;

}

The default BMP implementation of the EntityPersistenceManager interface is org.jboss.ejb.plugins.BMPPersistenceManager. The BMP persistence manager is fairly simple since all persistence logic is in the entity bean itself. The only duty of the persistence manager is to perform container callbacks.

org.jboss.ejb.StatefulSessionPersistenceManager

The StatefulSessionPersistenceManager is responsible for the persistence of stateful SessionBeans. This includes the following:

  • Creating stateful sessions in a storage
  • Activating stateful sessions from a storage
  • Passivating stateful sessions to a storage
  • Removing stateful sessions from a storage

See The org.jboss.ejb.StatefulSessionPersistenceManager interface. gives the StatefulSessionPersistenceManager interface.

The org.jboss.ejb.StatefulSessionPersistenceManager interface

public interface StatefulSessionPersistenceManager extends
ContainerPlugin

{

public void createSession(Method m, Object[] args,

StatefulSessionEnterpriseContext ctx)

throws Exception;

public void activateSession(StatefulSessionEnterpriseContext ctx)

throws RemoteException;

public void passivateSession(StatefulSessionEnterpriseContext ctx)

throws RemoteException;

public void removeSession(StatefulSessionEnterpriseContext ctx)

throws RemoteException, RemoveException;

public void removePassivated(Object key);

}

The default implementation of the StatefulSessionPersistenceManager interface is org.jboss.ejb.plugins.StatefulSessionFilePersistenceManager. As its name implies, StatefulSessionFilePersistenceManager utilizes the file system to persist stateful session beans. More specifically, the persistence manager serializes beans in a flat file whose name is composed of the bean name and session id with a .ser extension. The persistence manager restores a bean's state during activation and respectively stores its state during passivation from the bean's .ser file.

Entity Bean Locking and Deadlock Detection

Bill Burke, Scott Stark

This section provides information on what entity bean locking is and how entity beans are accessed and locked within JBoss 3.x. It also describes the problems you may encounter as you use entity beans within your system and how to combat these issues. Deadlocking is formally defined and examined. And, finally, we walk you through how to fine tune your system in terms of entity bean locking.

Why JBoss Needs Locking

Locking is all about protecting the integrity of your data. Sometimes you need to be sure that only one user can update critical data at one time. Sometimes, access to sensitive objects in your system need to be serialized so that data is not corrupted by concurrent reads and writes. Databases traditionally provide this sort of functionality with transactional scopes and table and row locking facilities.

Entity beans are a great way to provide an object-oriented interface to relational data. Beyond that, they can improve performance by taking the load off of the database through caching and delaying updates until absolutely needed so that the database efficiency can be maximized. But, with caching, data integrity is a problem, so some form of application server level locking is needed for entity beans to provide the transaction isolation properties that you are used to with traditional databases.

Entity Bean Lifecycle

With the default configuration of JBoss there is only one active instance of a given Entity Bean in memory at one time. This applies for every cache configuration and every type of commit-option. The lifecycle for this instance is different for every commit-option though.

  • For commit-option `A', this instance is cached and used between transactions.
  • For commit-optoin `B', this instance is cached and used between transactions, but is marked as `dirty' at the end of a transaction. This means that at the start of a new transaction ejbLoad must be called.
  • For commit-option `C', this instance is marked as `dirty', released from the cache, and marked for passivation at the end of a transaction.
  • For commit-option `D', a background refresh thread periodically calls ejbLoad on stale beans within the cache. Otherwise, this option works in the same way as `A'.

Marked for passivation means that the bean is placed within a queue. Each Entity Bean container has a Passivation Thread that periodically passivates beans that have been placed in the queue. A bean is pulled out of the passivation queue and reused if the application requests access to a bean of the same primary key.

On an exception or transaction rollback, the entity bean instance is thrown out of cache entirely and is not even put in the passivation queue and is not reused by an instance pool. Except for the passivation queue, there is no Entity Bean instance pooling. This created too many problems and bugs in the system so it was removed.

Default Locking Behavior

Entity Bean locking is totally decoupled from the Entity Bean instance. The logic for locking is totally isolated and managed in a separate lock object. This allows for funkier implementations of the Entity Bean lifecycle that is described later in this section.

Because there is only one allowed instance of a given Entity Bean active at one time, JBoss employs two types of locks to ensure data integrity and to conform to the EJB spec.

Method Lock

The method lock ensures that only one thread of execution at a time can invoke on a given Entity Bean. This is required by the EJB spec. But, this single-threadedness, can be overridden by marking the bean reentrant in its deployment descriptor.

Transaction Lock

A transaction lock ensures that only one transaction at a time has access to a give Entity Bean. This ensures the ACID properties of transactions at the application server level. Since, by default, there is only one active instance of any given Entity Bean at one time, JBoss must protect this instance from dirty reads and dirty writes. So, the default Entity Bean locking behavior will lock an Entity Bean within a transaction until it completes. This means that if any method at all is invoked on an Entity Bean within a transaction, no other transaction can have access to this bean until the holding transaction commits or is rolled back.

Pluggable Interceptors and Locking Policy

We saw that the basic Entity Bean lifecyle and behavior is defined by the container configuration defined in standardjboss.xml descriptor. See The "Standard CMP 2.x EntityBean" interceptor definition. shows the container-interceptors defintion for the "Standard CMP 2.x EntityBean" configuration.

The "Standard CMP 2.x EntityBean" interceptor definition

<container-configuration>

<container-name>Standard CMP 2.x EntityBean</container-name>

...

<container-interceptors>

<interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.TxInterceptorCMT</interceptor>

<interceptor>org.jboss.ejb.plugins.MetricsInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityCreationInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityLockInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityInstanceInterceptor</interceptor>

<interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntitySynchronizationInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.cmp.jdbc.JDBCRelationInterceptor</interceptor>

</container-interceptors>

The interceptors shown above define most of the behavior of the Entity Bean. Below is an explanation of the interceptors that are relevant to this section.

EntityLockInterceptor. This interceptor's role is to schedule any locks that must be acquired before the invocation is allowed to proceed. This interceptor is very lightweight and delegates all locking behavior to a pluggable locking policy.

EntityInstanceInterceptor. The job of this interceptor is to find the Entity Bean within the cache or create a new one. This interceptor also ensures that there is only one active instance of a bean in memory at one time.

EntitySynchronizationInterceptor. The role of this interceptor is to synchronize the state of the cache with the underlying storage. It does this with the ejbLoad and ejbStore semantics of the EJB specification. In the presence of a transaction this is triggered by transaction demarcation. It registers a callback with the underlying transaction monitor through the JTA interfaces. If there is no transaction the policy is to store state upon returning from invocation. The synchronization polices A,B,C of the specification are taken care of here as well as the JBoss specific commit-option `D'.

Deadlock

Finding deadlock problems and resolving them is the topic of this section. We will describe what deadlocking means, how you can detect it within your application, and how you can resolve deadlocks. Deadlock can occur when when two or more threads have locks on shared resources. For example See Deadlock definition example. illustrates a simple deadlock scenario. Here, Thread 1 has the lock for Bean A, and Thread 2 has the lock for Bean B. At a later time, Thread 1 tries to lock Bean B and blocks because Thread 2 has it. Likewise, as Thread 2 tries to lock A it also blocks because Thread 1 has the lock. At this point both threads are deadlocked waiting for access to the resource already locked by the other thread.

 

FIGURE 5-11. Deadlock definition example

The default locking policy of JBoss is to lock an Entity bean when an invocation occurs in the context of a transaction until the transaction completes. Because of this, it is very easy to encounter deadlock if you have long running transactions that access many entity beans, or if you are not careful about ordering the access to them. Various techniques and advanced configurations can be used to avoid deadlocking problems. They are discussed later in this section.

Dedlock Detection

Fortunately, with JBoss 2.4.5 and greater, a deadlock detection algoritm has been introduced in the code base. JBoss holds a global internal graph of waiting transactions and what transactions they are blocking on. Whenever a thread determines that it cannot acquire an Entity Bean lock, it figures out what transaction currently holds the lock on the bean and add itself to the blocked transaction graph. An example of what the graph may look like is given in See An example blocked transaction table..

An example blocked transaction table

 

Blocking TX

Tx that holds needed lock

Tx1

Tx2

Tx3

Tx4

Tx4

Tx1

Before the thread actually blocks it tries to detect whether there is deadlock problem. It does this by traversing the block transaction graph. As it traverses the graph, it keeps track of what transactions are blocked. If it sees a blocked node more than once in the graph, then it knows there is deadlock and will throw an ApplicationDeadlockException. This exception will cause a transaction rollback which will cause all locks that transaction holds to be released. The algorithm for the deadlock dection is found in the BeanLockSupport deadlockDetection method. The code for this method is shown in See The org.jboss.ejb.plugins.lock.BeanLockSupport deadlockDetection method..

The org.jboss.ejb.plugins.lock.BeanLockSupport deadlockDetection method

// This following is for deadlock detection

protected static HashMap waiting = new HashMap();

public void deadlockDetection(Transaction miTx) throws Exception

{

HashSet set = new HashSet();

set.add(miTx);

Object checkTx = this.tx;

synchronized(waiting)

{

while (checkTx != null)

{

Object waitingFor = waiting.get(checkTx);

if (waitingFor != null)

{

if (set.contains(waitingFor))

{

log.error("Application deadlock detected: " + miTx + " has deadlock conditions");

throw new ApplicationDeadlockException("application deadlock detected");

}

set.add(waitingFor);

}

checkTx = waitingFor;

}

}

}

Catching ApplicationDeadlockException

Since JBoss can detect application deadlock, you should write your application so that it can retry a transaction if the invocation fails because of the ApplicationDeadlockException. Unfortunately, this exception can be deeply embedded within a RemoteException, so you have to search for it in your catch block. For example:

try

{

...

}

catch (RemoteException ex)

{

Throwable cause = null;

RemoteException rex = ex;

while (rex.detail != null)

{

cause = rex.detail;

if (cause instanceof ApplicationDeadlockException)

{

// ... We have deadlock, force a retry of the transaction.

break;

}

if (cause instanceof RemoteException)

{

rex = (RemoteException)cause;

}

}

}

Viewing Lock Information

A new org.jboss.monitor.EntityLockMonitor MBean service was added in JBoss 3.0.4 that allows one to view basic locking statistics as well as printing out the state of the transaction locking table. To enable this monitor uncomment its configuration in the conf/jboss-service.xml:

<mbean code="org.jboss.monitor.EntityLockMonitor"

name="jboss.monitor:name=EntityLockMonitor"/>

 

The EntityLockMonitor has no configurable attributes. It does have the following read-only attributes:

  • MedianWaitTime: The median value of all times threads had to wait to acquire a lock.
  • AverageContenders: The ratio of the total number of contentions to the sum of all threads that had to wait for a lock.
  • TotalContentions: The total number of threads that had to wait to acquire the transaction lock. This happens when a thread attempts to acquire a lock that is associated with another transaction
  • MaxContenders: The maximum number of threads that were waiting to acquire the transaction lock.

It also has the following operations:

  • clearMonitor: This operation resets the lock monitor state by zeroing all counters.
  • printLockMonitor: This operation prints out a table of all ejb locks that lists the ejbName of the bean, the total time spent waiting for the lock, the count of times the lock was waited on and the number of transactions that timed out waiting for the lock.

Advanced Configurations and Optimizations

The default locking behavior of entity beans can cause deadlock. Since access to an Entity Bean locks the bean into the transaction, this also can present a huge performance/throughput problem for your application. This section walks through various techniques and configurations that you can use to optimize performance and reduce the possibility of deadlock.

Short-lived Transactions

Make your transactions as short-lived and fine-grained as possible. The shorter the transaction you have, the less likelihood you will have concurrent access collisions and your application throughput will go up.

Ordered Access

Ordering the access to your entity beans can help lessen the likelihood of deadlock. This means making sure that the entity beans in your system are always accessed in the same exact order. In most cases, user applications are just too complicated to use this approach and more advanced configurations are needed.

Read-Only Beans

Entity beans can be marked as read-only. When a bean is marked as read-only, it never takes part in a transaction. This means that it is never transactionally locked. Using commit-option `D' with this option is sometimes very useful when your read-only bean's data is sometimes updated by an external source.

To mark a bean as read-only, use the <read-only> flag in the jboss.xml deployment descriptor

Marking an entity bean read-only using jboss.xml

<jboss>

<enterprise-beans>

<entity>

<ejb-name>MyEntityBean</ejb-name>

<jndi-name>MyEntityHomeRemote</jndi-name>

<read-only>True</read-only>

</entity>

</enterprise-beans>

</jboss>

Explicitly Defining Read-Only Methods

After reading and understanding the default locking behavior of entity beans, you're probably wondering, "Why lock the bean if its not modifying the data?" JBoss allows you to define what methods on your Entity Bean are read-only so that it will not lock the bean within the transaction if only these types of methods are called. You can define these read-only methods within a jboss.xml deployment descriptor. Wildcards are allowed for method names. See Defining entity bean methods as read-only. shows an example of declaring all getter methods and the anotherReadOnlyMethod as read-only.

Defining entity bean methods as read-only

<jboss>

<enterprise-beans>

<entity>

<ejb-name>nextgen.EnterpriseEntity</ejb-name>

<jndi-name>nextgen.EnterpriseEntity</jndi-name>

<method-attributes>

<method>

<method-name>get*</method-name>

<read-only>true</read-only>

</method>

<method>

<method-name>anotherReadOnlyMethod</method-name>

<read-only>true</read-only>

</method>

</method-attributes>

</entity>

</enterprise-beans>

</jboss>

Instance Per Transaction Policy

The Instance Per Transaction policy is an advanced configuration that can totally wipe away deadlock and throughput problems caused by JBoss's default locking policy. The default Entity Bean locking policy is to only allow one active instance of a bean. The Instance Per Transaction policy breaks this requirement by allocating a new instance of a bean per transaction and dropping this instance at the end of the transaction. Because each transaction has its own copy of the bean, there is no need for transaction based locking.

This option does sound great but does have some drawbacks right now. First, the transactional isolation behavior of this option is equivalent to READ_COMMITTED. This can create repeatable reads when they are not desired. In other words, a transaction could have a copy of a stale bean. Second, this configuration option currently requires commit-option `B' or `C' which can be a performance drain since an ejbLoad must happen at the beginning of the transaction. But, if your application currently requires commit-option `B' or `C' anyways, then this is the way to go. The JBoss developers are currently exploring ways to allow commit-option `A' as well (which would allow the use of caching for this option).

JBoss has container configurations named "Instance Per Transaction CMP 2.x EntityBean" and "Instance Per Transaction BMP EntityBean" defined in the standardjboss.xml that implement this locking policy. To use this configuration, you just have to reference the name of the container configuration to use with your bean in the jboss.xml deployment descriptor as show in See An example of using the Instance Per Transaction policy...

An example of using the Instance Per Transaction policy.

<jboss>

<enterprise-beans>

<entity>

<ejb-name>MyCMP2Bean</ejb-name>

<jndi-name>MyCMP2</jndi-name>

<configuration-name>Instance Per Transaction CMP 2.x EntityBean</configuration-name>

</entity>

<entity>

<ejb-name>MyBMPBean</ejb-name>

<jndi-name>MyBMP</jndi-name>

<configuration-name>Instance Per Transaction BMP EntityBean</configuration-name>

</entity>

</enterprise-beans>

</jboss>

If you're using JBoss 3.0.0, you'll have to setup the configuration yourself within the jboss.xml descriptor. The highlighted code shown in See The Instance Per Transaction configuration . is what is new in the configuration.

The Instance Per Transaction configuration

<jboss>

<container-configurations>

<container-configuration>

<container-name>Instance Per Transaction CMP 2.x EntityBean</container-name>

<call-logging>false</call-logging>

<container-invoker>org.jboss.proxy.ejb.ProxyFactory</container-invoker>

<container-interceptors>

<interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.TxInterceptorCMT</interceptor>

<interceptor metricsEnabled = "true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityCreationInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityLockInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityMultiInstanceInterceptor</interceptor>

<interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.EntityMultiInstanceSynchronizationInterceptor</interceptor>

<interceptor>org.jboss.ejb.plugins.cmp.jdbc.JDBCRelationInterceptor</interceptor>

</container-interceptors>

<client-interceptors>

<home>

<interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</home>

<bean>

<interceptor>org.jboss.proxy.ejb.EntityInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</bean>

<list-entity>

<interceptor>org.jboss.proxy.ejb.ListEntityInterceptor</interceptor>

<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>

<interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>

<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>

</list-entity>

</client-interceptors>

<instance-pool>org.jboss.ejb.plugins.EntityInstancePool</instance-pool>

<instance-cache>org.jboss.ejb.plugins.EntityInstanceCache</instance-cache>

<persistence-manager>org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager</persistence-manager>

<transaction-manager>org.jboss.tm.TxManager</transaction-manager>

<locking-policy>org.jboss.ejb.plugins.lock.MethodOnlyEJBLock</locking-policy>

<container-cache-conf>

<cache-policy>org.jboss.ejb.plugins.LRUEnterpriseContextCachePolicy</cache-policy>

<cache-policy-conf>

<min-capacity>50</min-capacity>

<max-capacity>1000000</max-capacity>

<overager-period>300</overager-period>

<max-bean-age>600</max-bean-age>

<resizer-period>400</resizer-period>

<max-cache-miss-period>60</max-cache-miss-period>

<min-cache-miss-period>1</min-cache-miss-period>

<cache-load-factor>0.75</cache-load-factor>

</cache-policy-conf>

</container-cache-conf>

<container-pool-conf>

<MaximumSize>100</MaximumSize>

</container-pool-conf>

<commit-option>B</commit-option>

<container-configuration>

Running Within a Cluster

Currently there is no distributed locking capability for entity beans within the cluster. This functionality has been delegated to the database and must be supported by the application developer. For clustered entity beans, it is suggested to use commit-option `B' or `C' in combination with a row locking mechanism. For CMP, there is a <row-locking> configuration option. This option will use "SELECT ... FOR UPDATE" when the bean is loaded from the database. With commit-option `B' or `C', this implements a transactional lock that can be used across the cluster. For BMP, you must explicity implement the "SELECT ... FOR UPDATE" invocation within the BMP's ejbLoad method.

Troubleshooting

This section will describe some common locking problems and their solution.

Locking Behavior Not Working

There are many emails on the the JBoss User email list which sometimes state that the locking is not working and they are having concurrent access to their beans, and thus dirty reads. Here are some common reasons for this:

  • If you are upgrading from JBoss 2.4.0 or lower and you have custom <container-configurations>, make sure you have updated these configurations because 2.4.0 and lower does not have the EntityLockInterceptor.
  • Make absolutely sure that you have implemented equals and hashCode correctly from custom/complex primary key classes.
  • Make absolutely sure that your custom/complex primary key classes serialize correctly. One common mistake is assuming that member variable initializations will be executed when a primary key is unmarshalled.

IllegalStateException

Sometimes, people get:

java.lang.IllegalStateException: removing bean lock and it has tx set!

This usually means that you have not implemented equals and/or hashCode correctly for your custom/complex primary key class, or that your primary key class is not implemented correctly for serialization.

Hangs and Transaction Timeouts

One long outstanding bug of JBoss is that on a transaction timeout, that transaction is only "marked" for a rollback and not actually rolled back. This responsibility is delegated to the invocation thread. This can cause major problems if the invocation thread hangs indefinitely since things like Entity Bean locks will never be released. The solution to this problem is not a good one. You really just need to avoid doing stuff within a transaction that could hang indefinitely. One common no-no is making connections across the internet or running a web-crawler within a transaction.


© 2002-2004 JBoss Inc. All rights reserved.