Chapter 5. EJBs on JBoss

The EJB Container Configuration and Architecture

The JBoss EJB container architecture employs 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.

5.1. 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. Figure 5.1, “The composition of an EJBHome proxy in JBoss.” shows the composition of an EJB home proxy and its relation to the EJB deployment.

The composition of an EJBHome proxy in JBoss.

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 standard 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:

    • the JMX ObjectName of the EJB container MBean the Proxy is associated with

    • the javax.ejb.EJBMetaData for the EJB

    • the JNDI name of the EJB home interface

    • the transport specific invoker (org.jboss.invocation.Invoker)

    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.

  7. 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. 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.

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

Example 5.1. The client-interceptors from the Standard Stateless SessionBean configuration.

<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 call-by-value="false">
                    org.jboss.invocation.InvokerInterceptor
                </interceptor>
                <interceptor call-by-value="true">
                    org.jboss.invocation.MarshallingInvokerInterceptor
                </interceptor>
            </home>
            <bean>
                <interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>
                <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
                <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>
                <interceptor call-by-value="false">
                    org.jboss.invocation.InvokerInterceptor
                </interceptor>
                <interceptor call-by-value="true">
                    org.jboss.invocation.MarshallingInvokerInterceptor
                </interceptor>
            </bean>
        </client-interceptors>
    </proxy-factory-config>
</invoker-proxy-binding>           
<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 client interceptor is:

  • org.jboss.proxy.ejb.HomeInterceptor: 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: 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: associates the current security context with the method invocation for use by other interceptors or the server.

  • org.jboss.proxy.TransactionInterceptor: associates any active transaction with the invocation method invocation for use by other interceptors.

  • org.jboss.invocation.InvokerInterceptor: 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 Example 5.1, “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.

    org.jboss.invocation.MarshallingInvokerInterceptor: extends the InvokerInterceptor to not optimize in-VM invocations. This is used to force call-by-value semantics for method calls.

5.1.1. 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 several 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

  • singleton-message-driven-bean: a JMS invoker for singleton message driven beans

  • message-inflow-driven-bean: a JMS invoker for message inflow driven beans

  • jms-message-inflow-driven-bean: a JMS inflow invoker for standard 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 Figure 5.2, “The invoker-proxy-binding schema”.

The invoker-proxy-binding schema

Figure 5.2. 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 unstructured collection of elements. Only a few of the elements 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.

The following proxy-factory-config is for an entity bean accessed over RMI.

<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 call-by-value="false">
                org.jboss.invocation.InvokerInterceptor
            </interceptor>
            <interceptor call-by-value="true">
                org.jboss.invocation.MarshallingInvokerInterceptor
            </interceptor>
        </home>
        <bean>
            <interceptor>org.jboss.proxy.ejb.EntityInterceptor</interceptor>
            <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
            <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>
            <interceptor call-by-value="false">
                org.jboss.invocation.InvokerInterceptor
            </interceptor>
            <interceptor call-by-value="true">
                org.jboss.invocation.MarshallingInvokerInterceptor
            </interceptor>
        </bean>
        <list-entity>
            <interceptor>org.jboss.proxy.ejb.ListEntityInterceptor</interceptor>
            <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
            <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>
            <interceptor call-by-value="false">
                org.jboss.invocation.InvokerInterceptor
            </interceptor>
            <interceptor call-by-value="true">
                org.jboss.invocation.MarshallingInvokerInterceptor
            </interceptor>
        </list-entity>
    </client-interceptors>
</proxy-factory-config>

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

  • 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.

  • poa: The portable object adapter usage. Valid values are per-servant and 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.

  • interface-repository-supported: This indicates whether or not a deployed EJB has its own CORBA interface repository.

The following shows a proxy-factory-config for EJBs accessed over IIOP.

<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, the following elements apply:

  • MinimumSize: This specifies the minimum pool size for MDBs processing . This defaults to 1.

  • MaximumSize: This specifies the upper limit to the number of concurrent MDBs that will be allowed for the JMS destination. 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.

  • KeepAliveMillis: This specifies the keep alive time interval in milliseconds for sessions in the session pool. The default is 30000 (30 seconds).

  • MDBConfig: Configuration for the MDB JMS connection behavior. Among the elements supported are:

    • ReconnectIntervalSec: The time to wait (in seconds) before trying to recover the connection to the JMS server.

    • DeliveryActive: Whether or not the MDB is active at startup. The default is true.

    • 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.

Example 5.2, “A sample JMSContainerInvoker proxy-factory-config” gives a sample proxy-factory-config fragment taken from the standardjboss.xml descriptor.

Example 5.2. A sample JMSContainerInvoker proxy-factory-config

<proxy-factory-config>
    <JMSProviderAdapterJNDI>DefaultJMSProvider</JMSProviderAdapterJNDI>
    <ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI>
    <MinimumSize>1</MinimumSize>
    <MaximumSize>15</MaximumSize>
    <KeepAliveMillis>30000</KeepAliveMillis>
    <MaxMessages>1</MaxMessages>
    <MDBConfig>
        <ReconnectIntervalSec>10</ReconnectIntervalSec>
        <DLQConfig>
            <DestinationQueue>queue/DLQ</DestinationQueue>
            <MaxTimesRedelivered>10</MaxTimesRedelivered>
            <TimeToLive>0</TimeToLive>
        </DLQConfig>
    </MDBConfig>
</proxy-factory-config>

5.2. 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.

5.2.1. Detached Invokers - The Transport Middlemen

We looked at the detached invoker architecture in the context of exposing RMI compatible interfaces of MBean services earlier. 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 Figure 5.3, “The transport invoker server side architecture”.

The transport invoker server side architecture

Figure 5.3. The transport invoker server side architecture

For each type of home proxy there is a binding to an invoker and its associated transport protocol. A container may have multiple invocation protocols active simultaneously. In the jboss.xml file, an 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.

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 that enables compressed sockets for session beans can be found in the org.jboss.test.jrmp package of the testsuite. The following example illustrates the custom JRMPInvoker configuration and its mapping to a stateless session bean.

<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>

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.

<?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>     

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. The stateless-compression-invoker is linked to the custom JRMP invoker we just declared.

The following example, org.jboss.test.hello testsuite package, is an example of using the HttpInvoker to configure a stateless session bean to use the RMI/HTTP protocol.

<?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-invoker.sar/META-INF/jboss-service.xml descriptor, and its service descriptor fragment is show here:

<!-- The HTTP invoker service configuration -->
<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.

5.2.2. 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. The JRMPInvokerHA fully supports all of the attributes of the JRMPInvoker. 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.

5.2.3. The HA HttpInvoker - Clustered RMI/HTTP Transport

The RMI/HTTP layer allows for software load balancing of the invocations in a clustered environment. 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. Example 5.3, “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.

Example 5.3. 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 would be configured as shown below.

<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.

5.3. 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.

5.3.1. 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 verifier should be run. This validates that the EJBs in a deployment unit conform to the EJB 2.1 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.

  • CallByValue: a boolean flag that indicates call by value semantics should be used by default.

  • 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. 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.

5.3.1.1. Verifying EJB deployments

When the VerifyDeployments attribute of 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. It will also check 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. It allows you to use custom JavaDoc-like tags in the EJB bean implementation class to generate the related bean interfaces, deployment descriptors and related objects. See the XDoclet home page, http://sourceforge.net/projects/xdoclet for additional details.

5.3.1.2. 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:

  • Create subclass of org.jboss.ejb.Container depending on the type of the EJB: stateless, stateful, BMP entity, CMP entity, or message driven. 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.

  • Set all container configurable attributes from a merge of the jboss.xml and standardjboss.xml descriptors.

  • Create and add the container interceptors as configured for the container.

  • 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.

5.3.1.3. 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_4_0.dtd. The section DTD that relates to container configuration information is shown in Figure 5.4, “The jboss_4_0 DTD elements related to container configuration.”.

The jboss_4_0 DTD elements related to container configuration.

Figure 5.4. The jboss_4_0 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. The following is a sample of container-configuration from standardjboss.xml.

<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>
    <insert-after-ejb-post-create>false</insert-after-ejb-post-create>
    <call-ejb-store-on-clean>true</call-ejb-store-on-clean>
    <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>org.jboss.ejb.plugins.CallValidationInterceptor</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>

These two examples 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.

How an EJB deployment chooses its container configuration is based on the explicit or implict jboss/enterprise-beans/<type>/configuration-name element. The configuration-name element is a link to a container-configurations/container-configuration element in Figure 5.4, “The jboss_4_0 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 container-configuration that references the name of an existing standardjboss.xml container-configuration/container-name as the value for the container-configuration/extends attribute. The following example shows an example of defining a new Secured Stateless SessionBean configuration that is an extension of the Standard Stateless SessionBean configuration.

<?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

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 purely a matter of style.

Now that you know how to specify which container configuration an EJB is using and can define a deployment unit level override, we now will look at the container-configuration child elements 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.

5.3.1.3.1. 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.

5.3.1.3.2. 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.

5.3.1.3.3. 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.

5.3.1.3.4. The sync-on-commit-only element

This configures a performance optimization that will cause entity bean state to be synchronized with the database only at commit time. Normally the state of all the beans in a transaction would need to be synchronized when an finder method is called or when an remove method is called, for example.

5.3.1.3.5. insert-after-ejb-post-create

This is another entity bean optimization which cause the database insert command for a new entity bean to be delayed until the ejbPostCreate method is called. This allows normal CMP fields as well as CMR fields to be set in a single insert, instead of the default insert followed by an update, which allows removes the requirement for relation ship fields to allow null values.

5.3.1.3.6. call-ejb-store-on-clean

By the specification the container is required to call ejbStore method on an entity bean instance when transaction commits even if the instance was not modified in the transaction. Setting this to false will cause JBoss to only call ejbStore for dirty objects.

5.3.1.3.7. 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.

5.3.1.3.8. The instance-pool element

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.

5.3.1.3.9. The container-pool-conf element

The container-pool-conf is passed to the InstancePool implementation class given by the instance-pool element if it implements the XmlLoadable interface. All current JBoss InstancePool implementations derive from the org.jboss.ejb.plugins.AbstractInstancePool class which provides support for elements shown in Figure 5.5, “The container-pool-conf element DTD”.

The container-pool-conf element DTD

Figure 5.5. The container-pool-conf element DTD

  • MinimumSize: 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.

  • MaximumSize: The MaximumSize specifies the maximum number of pool instances that are allowed. The default use of MaximumSize 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.

  • strictMaximumSize: 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.

  • strictTimeout: 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.

5.3.1.3.10. The instance-cache element

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.

5.3.1.3.11. The container-cache-conf element

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 which 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, shown in Figure 5.6, “The container-cache-conf element DTD”.

The container-cache-conf element DTD

Figure 5.6. The container-cache-conf element DTD

  • 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.

5.3.1.3.12. 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.

5.3.1.3.13. 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.

WebClaossLoader has two methods meant to be overridden by subclasses: getKey() and getBytes(). The latter is a no-op in this implementation and should be overridden 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.

5.3.1.3.14. 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.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.QueuedPessimisticEJBLockNoADE: This behaves the same as the QueuedPessimisticEJBLock except that deadlock detection is disabled.

  • 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.

Locking and deadlock detection will be discussed in more detail in Section 5.4, “Entity Bean Locking and Deadlock Detection”.

5.3.1.3.15. The commit-option and optiond-refresh-rate elements

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

  • 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 commit option 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.

5.3.1.3.16. The security-domain element

The security-domain element specifies the JNDI name of the object that implements the org.jboss.security.AuthenticationManager and org.jboss.security.RealmMapping interfaces. It is more typical to specify the security-domain under the jboss root element so that all EJBs in a given deployment are secured in the same manner. However, it is possible to configure the security domain for each bean configuration. The details of the security manager interfaces and configuring the security layer are discussed in Chapter 8, Security on JBoss.

5.3.1.3.17. cluster-config

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 cluster-config and related elements

Figure 5.7. The cluster-config and related elements

  • partition-name: 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, it 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.

  • home-load-balance-policy: 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.

  • bean-load-balance-policy: 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. For entity beans and stateful session beans, the default is org.jboss.ha.framework.interfaces.FirstAvailavble. For stateless session beans, org.jboss.ha.framework.interfaces.RoundRobin.

  • session-state-manager-jndi-name: 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.

5.3.1.3.18. The depends element

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.

5.3.2. 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.

5.3.2.1. 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 below.

Example 5.4. The org.jboss.ejb.ContainerPlugin interface

public interface ContainerPlugin
    extends Service, AllowedOperationsFlags
{
    /** 
     * 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);
}   

5.3.2.2. 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 below.

Example 5.5. 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.

5.3.2.3. 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.

Example 5.6.  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).

5.3.2.4. 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.

Example 5.7. 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.

5.3.2.5. 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

Example 5.8.  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;
}

5.3.2.6. The org.jboss.ejb.EntityPersistenceStore interface

As per the EJB 2.1 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. The EntityPersistanceStore interface is shown below.

Example 5.9.  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.

5.3.2.7. 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

The StatefulSessionPersistenceManager interface is shown below.

Example 5.10. 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.

5.4. Entity Bean Locking and Deadlock Detection

This section provides information on what entity bean locking is and how entity beans are accessed and locked within JBoss. 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.

5.4.1. Why JBoss Needs Locking

Locking is 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.

5.4.2. 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 option 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.

When a bean is marked for passivation, the bean is placed in a passivation queue. Each entity bean container has a passivation thread that periodically passivates beans that have been placed in the passivation 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. It is not put into the passivation queue and is not reused by an instance pool. Except for the passivation queue, there is no entity bean instance pooling.

5.4.3. 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. 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.

  • 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.

5.4.4. Pluggable Interceptors and Locking Policy

We saw that the basic entity bean lifecycle and behavior is defined by the container configuration defined in standardjboss.xml descriptor. Let's look at the container-interceptors definition for the Standard CMP 2.x EntityBean configuration.

<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>org.jboss.ejb.plugins.CallValidationInterceptor</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>

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 and C of the specification are taken care of here as well as the JBoss specific commit-option D.

5.4.5. Deadlock

Finding deadlock problems and resolving them is the topic of this section. We will describe what deadlocking MBeans, how you can detect it within your application, and how you can resolve deadlocks. Deadlock can occur when two or more threads have locks on shared resources. Figure 5.8, “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.

Deadlock definition example

Figure 5.8. 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.

5.4.5.1. Deadlock Detection

Fortunately, JBoss is able to perform deadlock detection. 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 Table 5.1, “An example blocked transaction table”.

Table 5.1. An example blocked transaction table

Blocking TXTx that holds needed lock
Tx1Tx2
Tx3Tx4
Tx4Tx1

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.

5.4.5.2. 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;
        }
    }
}

5.4.5.3. Viewing Lock Information

The EntityLockMonitor MBean service 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.

5.4.6. 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.

5.4.6.1. 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.

5.4.6.2. 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.

5.4.6.3. 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.

Example 5.11. 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>

5.4.6.4. 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. The following is an example of declaring all getter methods and the anotherReadOnlyMethod as read-only.

Example 5.12. 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> 

5.4.6.5. 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 below.

Example 5.13. 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>

5.4.7. 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 a SQL 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 explicitly implement the select for update invocation within the BMP's ejbLoad method.

5.4.8. Troubleshooting

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

5.4.8.1. Locking Behavior Not Working

Many JBoss users observe that locking does not seem to be working and see concurrent access to their beans, and thus dirty reads. Here are some common reasons for this:

  • If you have custom container-configurations, make sure you have updated these configurations.

  • 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.

5.4.8.2. IllegalStateException

An IllegalStateException with the message "removing bean lock and it has tx set!" 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.

5.4.8.3. 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 mistake is making connections across the internet or running a web-crawler within a transaction.

5.5. EJB Timer Configuration

The J2EE timer service allows for any EJB object to register for a timer callback either at a designated time in the future. Timer events can be used for auditing, reporting or other cleanup tasks that need to need to happen at some given time in the future. Timer events are intended to be persistent and should be executed even in the event of a server failure. Coding to EJB timers is a standard part of the J2EE specification, so we won't explore the programming model. We will, instead, look at the configuration of the timer service in JBoss so that you can understand how to make timers work best in your environment

The EJB timer service is configure by several related MBeans in the ejb-deployer.xml file. The primary MBean is the EJBTimerService MBean.

<mbean code="org.jboss.ejb.txtimer.EJBTimerServiceImpl" name="jboss.ejb:service=EJBTimerService">
    <attribute name="RetryPolicy">jboss.ejb:service=EJBTimerService,retryPolicy=fixedDelay</attribute>
    <attribute name="PersistencePolicy">jboss.ejb:service=EJBTimerService,persistencePolicy=database</attribute>
    <attribute name="TimerIdGeneratorClassName">org.jboss.ejb.txtimer.BigIntegerTimerIdGenerator</attribute>
    <attribute name="TimedObjectInvokerClassName">org.jboss.ejb.txtimer.TimedObjectInvokerImpl</attribute>
</mbean> 

The EJBTimerService has the following configurable attributes:

  • RetryPolicy: This is name of the MBean that implements the retry policy. The MBean must support the org.jboss.ejb.txtimer.RetryPolicy interface. JBoss provides one implementation, FixedDelayRetryPolicy, which will be described later.

  • PersistencePolicy: This is the name of the MBean that implements the the persistence strategy for saving timer events. The MBean must support the org.jboss.ejb.txtimer.PersistencePolicy interface. JBoss provides two implementations, NoopPersistencePolicy and DatabasePersistencePolicy, which will be described later.

  • TimerIdGeneratorClassName: This is the name of a class that provides the timer ID generator strategy. This class must implement the org.jboss.ejb.txtimer.TimerIdGenerator interface. JBoss provides the org.jboss.ejb.txtimer.BigIntegerTimerIdGenerator implementation.

  • TimedObjectInvokerClassname: This is the name of a class that provides the timer method invocation strategy. This class must implement the org.jboss.ejb.txtimer.TimedObjectInvoker interface. JBoss provides the org.jboss.ejb.txtimer.TimedObjectInvokerImpl implementation.

The retry policy MBean definition used is shown here:

<mbean code="org.jboss.ejb.txtimer.FixedDelayRetryPolicy"
       name="jboss.ejb:service=EJBTimerService,retryPolicy=fixedDelay">
    <attribute name="Delay">100</attribute>
</mbean>

The retry policy takes one configuration value:

  • Delay: This is the delay (ms) before retrying a failed timer execution. The default delay is 100ms.

If EJB timers do not need to be persisted, the NoopPersistence policy can be used. This MBean is commented out by default, but when enabled will look like this:

<mbean code="org.jboss.ejb.txtimer.NoopPersistencePolicy" 
       name="jboss.ejb:service=EJBTimerService,persistencePolicy=noop"/>

Most applications that use timers will want timers to be persisted. For that the DatabasePersitencePolicy MBean should be used.

<mbean code="org.jboss.ejb.txtimer.DatabasePersistencePolicy" 
       name="jboss.ejb:service=EJBTimerService,persistencePolicy=database">
    <!-- DataSource JNDI name -->
    <depends optional-attribute-name="DataSource">jboss.jca:service=DataSourceBinding,name=DefaultDS</depends>
    <!-- The plugin that handles database persistence -->
    <attribute name="DatabasePersistencePlugin">org.jboss.ejb.txtimer.GeneralPurposeDatabasePersistencePlugin</attribute>
</mbean>
  • DataSource: This is the MBean for the DataSource that timer data will be written to.

  • DatabasePersistencePlugin: This is the name of the class the implements the persistence strategy. This should be org.jboss.ejb.txtimer.GeneralPurposeDatabasePersistencePlugin.