Spring and EJB

With the advent of Spring, developers now have, for the first time, a truly lightweight alternative to EJB. Using Spring, you can take advantage of many of the features offered by EJB, such as declarative transaction management, object pooling, and simple ORM functionality. That said, we anticipate that EJB will continue to be used for application development for the foreseeable future. Although we do not look at reasons for or against using EJB in this book, our experience with Spring has been excellent, and we recommend that you use it instead of EJB whenever you can. For a more complete discussion of the pros and cons of both Spring and EJB, read

EJB Support in Spring

EJB support in Spring can be loosely grouped into two categories: access and implementation. The access support classes in Spring make it easier for you to access EJB resources. You have already seen an example of some of the base access support classes in the section on JNDI. In this section, we look at how Spring extends the basic JNDI support framework to ease EJB access and utilizes AOP support to provide proxy-based access to EJB resources.

The EJB implementation support in Spring provides abstract base classes that make it simpler for you to create three types of EJB: stateless session beans, stateful session beans, and message-driven beans. The basic premise behind these base classes is not so much to ease the burden of creating an EJB, but to enable you to access Spring-managed resources easily from within your beans and, more importantly, to aid you in factoring your business logic out of the EJB implementation and into a POJO used by the EJB. Don't worry if this sounds a little unclear at this point; we discuss this in detail in the next section alongside two examples that should make this crystal clear.

In this section, we build a simple web application that uses two EJB services. The first, a stateless session bean, implements the EchoService business interface and provides simple echoing capabilities. The second, a stateful session bean, implements the CounterService business interface and provides stateful services for counting.

These are trivial examples, but they help to demonstrate the recommended approach to building EJBs with Spring as well as different components involved in Spring's EJB support. As with the JNDI section, we do not go into great detail about the EJB spec itself, other than to discuss the various deployment descriptors that make up our sample. We do, however, peek inside the implementation of Spring's EJB support at the various components and how they affect your application. In particular, we look at how Spring locates the ApplicationContext for your EJBs and how JNDI resources are located using the JNDI infrastructure discussed earlier.

You may have noticed that we mentioned that Spring supports three kinds of EJB, but we are only going to be implementing two types in this chapter. The message-driven bean support classes follow a similar pattern to those for stateless and stateful session beans, and you will find an example of how to build a message-driven bean with Spring in Chapter 12.

Building EJBs with Spring

Spring provides three abstract classes to serve as base classes for your EJB bean classes: AbstractStatelessSessionBean, AbstractStatefulSessionBean, and AbstractMessageDrivenBean. When building an EJB using Spring, you still have to provide all the different interfaces and the home class, but when implementing your bean class, you derive from the appropriate Spring base class. The base classes provided by Spring allow your EJBs to access a Spring ApplicationContext, and thus allow them to access resources that are managed by Spring.

Before we jump into the details of building our EchoService and CounterService beans using Spring, we are going to look at how Spring goes about locating an ApplicationContext for your EJBs and the recommended approach for building an EJB when you are using Spring.

The Spring EJB Class Hierarchy

Spring provides a well-defined class hierarchy for the EJB support classes, as shown in Figure 13-2.

image from book
Figure 13-2: Spring EJB support classes

As you can see, the central base class, AbstractEnterpriseBean, exposes the beanFactoryLocator property to allow subclasses access to the BeanFactoryLocator instance being used. The BeanFactoryLocator interface is discussed in more detail in the next section, along with the loadBeanFactory() and unloadBeanFactory() methods. Notice that the AbstractStatelessSessionBean class has already implemented the ejbCreate(), ejbActivate(), and ejbPassivate() methods, whereas AbstractStatefulSessionBean has not. Spring has special requirements regarding bean passivation that we discuss in more detail later, in the section entitled "Building a Stateful Session Bean."

The BeanFactoryLocator Interface

One of the key features offered by the base EJB classes in Spring is the ability to access an ApplicationContext from which you can load Spring-managed resources. This functionality is not provided by the base classes themselves; rather, it is delegated to an implementation of the BeanFactoryLocator interface, like the one shown in Listing 13-9.

Listing 13-9: The BeanFactoryLocator Interface

image from book
public interface BeanFactoryLocator {        BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException;      }
image from book

A BeanFactoryLocator is used in circumstances when Spring has no control over the creation of a resource and thus cannot automatically configure it. In these circumstances, a BeanFactoryLocator allows a resource to locate the BeanFactory itself in an externally configurable way. Of course, in such cases, the resource can simply mandate where the BeanFactory configuration must be, but that means that you, as the application developer, have no control over the application. By using BeanFactoryLocator, you can fully control how your EJBs locate the BeanFactory or ApplicationContext they use for configuration.

Notice that the BeanFactoryLocator doesn't return a BeanFactory instance directly; instead it returns a BeanFactoryReference. A BeanFactoryReference is a lightweight wrapper around a BeanFactory or ApplicationContext that allows the resource using the BeanFactory to release its reference to the BeanFactory gracefully. The actual implementation of this interface is specific to both the BeanFactoryLocator implementation and the BeanFactory or ApplicationContext interface. We investigate this functionality a little more in Listing 13-21 when we look at stateful session beans that use their ability to release a BeanFactoryReference to enable bean passivation.

By default, all of the base EJB classes use the ContextJndiBeanFactoryLocator implementation of BeanFactoryLocator. Essentially, this class looks in a given JNDI location for a comma-separated list of configuration file names and creates an instance of ClassPathXmlApplicationContext using these configuration files. You can provide your own implementation of BeanFactoryLocator by setting the beanFactoryLocator property that is exposed by all three base EJB classes via the AbstractEnterpriseBean class. However, if you do so, be aware that each instance of your bean has its own instance of ContextJndiBeanFactoryLocator, and likewise, each instance of ContextJndiBeanFactoryLocator has its own instance of ClassPathXmlApplicationContext.

Although all the ApplicationContext instances created for your EJB instances are identically configured, the beans are not identical. Consider a Spring configuration that defines the echoService bean the EchoServiceEJB will use. If your application server creates 100 instances of your EJB, then 100 instances of ContextJndiBeanFactoryLocator are created, along with 100 instances of ClassPathXmlApplicationContext and 100 instances of the echoService bean. If this behavior is undesirable for your application, then Spring provides the SingletonBeanFactoryLocator and ContextSingletonBeanFactoryLocator classes that load singleton instances of BeanFactory and ApplicationContext, respectively. See the JavaDoc for these classes for more information.

The Spring Approach to EJBs

One of the biggest drawbacks of EJB is that it is very difficult to test EJB components separately from the EJB container, which makes unit testing EJB-implemented business logic a feat only attempted by the particularly masochistic. However, a workaround to this problem has been around in the Java world for a long while; it involves implementing your business logic in a POJO that implements the same business interface as your EJB bean class; you can then have the bean class delegate to the POJO. Using Spring makes this implementation much simpler and much more flexible, because you don't have to embed any logic inside the EJB as to how the POJO implementation is located and created. Figure 13-3 shows how we employ this approach when building the EchoService EJB.

image from book
Figure 13-3: Using a POJO implementation for an EJB

Here you can see that, as expected, the bean class, EchoServiceEJB, implements the EchoService interface, but also notice that EchoServiceEJB has a dependency on the EchoService interface and a private field of type EchoService.

Building a Stateless Session Bean

A stateless session bean is the easiest EJB to build with Spring; this is because it requires no special handling whatsoever and all the ejbXXX() methods are implemented by the AbstractStatelessSessionBean class.

HarropMachacek_4614C13 .fm Page 453 Thursday, December 16, 2004 3:47 PM

Listing 13-10: The EchoService Interface

image from book
package com.apress.prospring.ch13.ejb;      public interface EchoService {          public String echo(String message); }
image from book

Notice that the service interface is not EJB-specific, and indeed, it is not required when implementing the EJB. The traditional approach to EJB development is to define business methods in the EJB-specific local and remote interfaces. In this approach, the business methods are defined in a standard Java interface and the local and remote bean interfaces extend this standard interface. This service interface not only provides a standard interface for the local and remote interfaces to extend, but it also provides a common interface that both the EJB bean class and the POJO implementation class can implement. Although we recommend that your EJB bean class does not implement either the local or remote interface, there is no problem with the EJB bean implementing the service interface. By having all the components that make up the EJB share a common interface, it is easier to ensure that your local interface defines the methods you intend it to, that your bean class implements the methods expected by the local interface, and that the POJO implementation implements the methods required by the EJB bean class.

The next step is to create the bean interface. In this example, we do not use the EJB in a remote container, so we stick to a local interface, as shown in Listing 13-11.

Listing 13-11: The Local Interface for the EchoService EJB

image from book
package com.apress.prospring.ch13.ejb;      import javax.ejb.EJBLocalObject;      public interface EchoServiceLocal extends EchoService, EJBLocalObject {      }
image from book

Notice that, as we discussed earlier, no business methods are defined in this interface. Instead, the EchoServiceLocal interface extends the service interface, EchoService.

Next, we need to create the EJB home interface. For this example, we are not going to be invoking the EJB remotely, so we stick to a simple local home interface, as shown in Listing 13-12.

Listing 13-12: Local Home Interface for EchoService EJB

image from book
package com.apress.prospring.ch13.ejb;      import javax.ejb.CreateException; import javax.ejb.EJBLocalHome;      public interface EchoServiceHome extends EJBLocalHome {          public EchoServiceLocal create() throws CreateException; }
image from book

That takes care of most of the boilerplate code required by the EJB specification. When you are building an EJB using traditional architecture, the next step is to create the bean class. However, we are going to factor the implementation of the EchoService into a POJO and have the EJB bean class delegate to this POJO implementation. Listing 13-13 shows the POJO implementation of the EchoService interface.

Listing 13-13: POJO Implementation of EchoService

image from book
package com.apress.prospring.ch13.ejb;      public class EchoServiceImpl implements EchoService {          public String echo(String message) {         return message;     } }
image from book

Here the implementation of EchoService is contained in a POJO that you can easily test outside of the EJB container (not that this implementation needs much testing!).

The final step in the implementation of the EchoService EJB is to create the bean class itself. This is where Spring comes into the equation. With the actual implementation of the EchoService interface contained in EchoServiceImpl, we can simply choose to create an instance of this implementation in EchoServiceEJB and be done with it.

However, what happens if we want to change the implementation? We need to recompile and redeploy the EJB. By using Spring, we can load the implementation class, and any dependencies, from the ApplicationContext. This means that you can take full advantage of all of Spring's features for the actual implementation class, including DI, AOP, and external configuration support. Listing 13-14 shows the implementation of the EchoService bean class.

Listing 13-14: The EchoServiceEJB Class

image from book
package com.apress.prospring.ch13.ejb;      import javax.ejb.CreateException;      import org.springframework.ejb.support.AbstractStatelessSessionBean;      public class EchoServiceEJB extends AbstractStatelessSessionBean implements         EchoService {          private static final String BEAN_NAME = "echoService";          private EchoService service;          public String echo(String message) {         return service.echo(message);     }          protected void onEjbCreate() throws CreateException {         service = (EchoService) getBeanFactory().getBean(BEAN_NAME);     } }
image from book

There are a few interesting points to note in this code. First, you should notice that EchoServiceEJB extends the Spring base class AbstractStatelessSessionBean and implements the EchoService interface. Second, all the methods on the EchoService interface are delegated to the wrapped implementation of EchoService. Third, this bean has no ejbXXX() methods— these are all implemented in AbstractStatelessSessionBean. And finally, notice the onEjbCreate() method. This is a hook method provided by AbstractStatelessSessionBean, and it is called during ejbCreate(). This method is perfect for obtaining any Spring-managed resources the EJB needs, in particular, the actual implementation of the service interface. As you can see, we retrieved the echoService bean from Spring and stored it in the service field. This bean is the implementation of EchoService to which the EchoServiceEJB delegates.

Remember from Figure 13-2 that the getBeanFactory() method is declared on the AbstractEnterpriseBean class that forms the base of all Spring EJB implementation support classes. By default, the AbstractEnterpriseBean class uses an instance of ContextJndiBeanFactoryLocator to locate and load an ApplicationContext to return from getBeanFactory(). ContextJndiBeanFactoryLocator works by looking in a particular JNDI location for a list of file names from which to load the ApplicationContext configuration. We can specify this list in the deployment descriptor for the EJB, as shown in Listing 13-15.

Listing 13-15: Configuring ContextJndiBeanFactoryLocator in Deployment Descriptor

image from book
<session>     <description>Echo Service Bean</description>     <ejb-name>EchoServiceEJB</ejb-name>     <local-home>com.apress.prospring.ch13.ejb.EchoServiceHome</local-home>     <local>com.apress.prospring.ch13.ejb.EchoServiceLocal</local>     <ejb-class>com.apress.prospring.ch13.ejb.EchoServiceEJB</ejb-class>     <session-type>Stateless</session-type>     <transaction-type>Container</transaction-type>     <env-entry>         <env-entry-name>ejb/BeanFactoryPath</env-entry-name>         <env-entry-type>java.lang.String</env-entry-type>         <env-entry-value>applicationContext.xml</env-entry-value>     </env-entry> </session>
image from book

The important part in this deployment descriptor is the <env-entry> tag that sets the value of the ejb/BeanFactoryPath JNDI location to applicationContext.xml. The JNDI path ejb/BeanFactoryPath is the default location where ContextJndiBeanFactoryLocator looks for the configuration file path. The configuration in Listing 13-15 is essentially telling the instance of ContextJndiBeanFactoryLocator associated with the EchoServiceEJB to create an ApplicationContext configured using the details in the applicationContext.xml file.

An important point to note is that this configuration file is specific to the EJB and is separate from any applicationContext.xml file you may have for the main application. When we package up the application, we place the applicationContext.xml file specific to the EJBs in the EJB JAR, and then we have the applicationContext.xml file for the web application in the WAR file.

To finish up with the deployment of the EchoServiceEJB, we also need the JBoss-specific deployment descriptor for the bean. This is shown in Listing 13-16.

Listing 13-16: JBoss Deployment Descriptor

image from book
<jboss>     <enterprise-beans>         <session>             <ejb-name>EchoServiceEJB</ejb-name>             <local-jndi-name>ejb/echoService</local-jndi-name>         </session>     </enterprise-beans> </jboss>
image from book

The important part in this deployment descriptor is the <local-jndi-name> tag, which tells JBoss the JNDI name to which to bind the EJB home. In this case, we are able to locate an instance of EchoServiceHome under ejb/echoService.

That's all for the stateless session bean. As you can see, the implementation steps aren't vastly different from the traditional approach to EJB implementation. However, with the approach taken here, you gain the ability to test your business logic easily and without any dependency on the EJB container. By using Spring, we have made the POJO-based implementation approach much more flexible, and we are easily able to avoid coupling the EJB bean to a particular POJO implementation.

Building a Stateful Session Bean

Building a stateful session bean is slightly more complex than building a stateless session bean because you now have to consider what happens to the ApplicationContext when the bean is passivated. Recall from Figure 13-2 that AbstractStatefulSessionBean does not implement ejbCreate(), ejbActivate(), and ejbPassivate(). The reason for this is that none of the default BeanFactory and ApplicationContext implementations Spring provides is serializable, and as a result, none can be passivated along with your stateful session bean.

To get around this, you have two options. The simplest option is to implement the ejbActivate() and ejbPassivate() to load and unload the ApplicationContext as appropriate, which allows the bean to be passivated without storing the ApplicationContext and reconstructing the ApplicationContext when the bean is activated again. The second option is to provide your own implementation of BeanFactoryLocator that creates a BeanFactory or an ApplicationContext that is serializable. When you use the first approach, be aware that when you use the ContextJndiBeanFactoryLocator, bean activation results in the ApplicationContext being loaded from scratch. If this is unacceptable overhead for your application, you can swap ContextJndiBeanFactoryLocator for ContextSingletonBeanFactoryLocator, as discussed earlier.

As before, to start building the bean, we start with the service interface, shown here in Listing 13-17.

Listing 13-17: The CounterService Interface

image from book
package com.apress.prospring.ch13.ejb;      public interface CounterService {          public int increment();          public int decrement(); }
image from book

Again, we created a basic service interface that will be implemented by the local interface, the bean class, and the implementation class.

The next step is to create the local interface, shown in Listing 13-18.

Listing 13-18: Local Interface for CounterService

image from book
package com.apress.prospring.ch13.ejb;      import javax.ejb.EJBLocalObject;      public interface CounterServiceLocal extends CounterService, EJBLocalObject {      } 
image from book

Again, the local interface itself contains no method definitions, and all the business methods are inherited from the CounterService interface.

Next up, we need to create the home interface, as shown in Listing 13-19.

Listing 13-19: The Local Home Interface for CounterService

image from book
package com.apress.prospring.ch13.ejb;      import javax.ejb.CreateException; import javax.ejb.EJBLocalHome;      public interface CounterServiceHome extends EJBLocalHome {          public CounterServiceLocal create() throws CreateException; }
image from book

Again, there is nothing special about this implementation; it is just the standard EJB approach.

The fourth component required for the CounterService EJB is the POJO implementation. This implementation is stateful and is marked as Serializable, so it can be passivated along with the CounterServiceEJB. Listing 13-20 shows the CounterServiceImpl class.

Listing 13-20: Basic CounterService Implementation

image from book
package com.apress.prospring.ch13.ejb;      import java.io.Serializable;      public class CounterServiceImpl implements CounterService, Serializable {          private int count = 0;          public int increment() {         return ++count;     }          public int decrement() {         return --count;     } }
image from book

Thus far, the implementation of the stateful session bean has been similar to the implementation of the stateless session bean. However, there are notable differences when implementing the bean class. Listing 13-21 shows the bean class for the CounterServiceEJB.

Listing 13-21: The CounterServiceEJB Class

image from book
package com.apress.prospring.ch13.ejb;      import java.rmi.RemoteException;      import javax.ejb.CreateException; import javax.ejb.EJBException;      import org.springframework.ejb.support.AbstractStatefulSessionBean;      public class CounterServiceEJB extends AbstractStatefulSessionBean implements         CounterService {          private CounterService service;          public int increment() {         return service.increment();     }          public int decrement() {         return service.decrement();     }          public void ejbCreate() throws CreateException {         load();         service = (CounterService) getBeanFactory().getBean("counterService");     }          public void ejbActivate() throws EJBException, RemoteException {         load();         service = (CounterService) getBeanFactory().getBean("counterService");     }          public void ejbPassivate() throws EJBException, RemoteException {         unload();     }          private void load() {         loadBeanFactory();     }          private void unload() {         unloadBeanFactory();         setBeanFactoryLocator(null);     } } 
image from book

The first part of the bean class implementation is similar to that of the EchoServiceEJB that we created earlier. However, the noticeable differences here are in the ejbCreate(), ejbActivate(), and ejbPassivate() methods. In ejbCreate(), we invoke the load() method, which in turn invokes loadBeanFactory() on AbstractEnterpriseBean. This causes the BeanFactoryLocator implementation to load the BeanFactory and makes it available via a call to getBeanFactory(). Finally the ejbCreate() method uses the BeanFactory to access the counterService bean and stores it in the service field.

The bean is now configured for use and will continue to function happily until the container chooses to passivate it. When this happens, the ejbPassivate() method is invoked and, in turn, the unload() method. The first step unload() takes is to invoke unloadBeanFactory(), which clears out the ApplicationContext loaded by the ContextJndiBeanFactoryLocator and sets the reference to null. As we mentioned earlier, the reason for this is that ClassPathXmlApplicationContext (the ApplicationContext implementation used by ContextJndiBeanFactoryLocator) is not serializable and as a result, it cannot be passivated along with the bean. Finally, unload() removes all references to the BeanFactoryLocator implementation because, like ClassPathXmlApplicationContext, ContextJndiBeanFactoryLocator is not serializable.

When the container is ready to reactivate a bean after passivation, it invokes ejbActivate() on the bean to let the bean know it can reestablish any state that could not be passivated. In this case, we simply invoke load() again to reload the ApplicationContext. Note that if you are using a custom BeanFactoryLocator implementation, then you need to reinstantiate it in ejbActivate() as well. Once the ApplicationContext is reloaded, we reload the counterService bean from the ApplicationContext.

You may well be wondering why we bother to reload the ApplicationContext in ejbCreate(). Given that the CounterServiceImpl class is serializable, it will be passivated along with the bean, so all we really need to do is close the BeanFactory once we obtain the counterService bean. However, remember that you can configure any CounterService implementation in the ApplicationContext, including one that is not serializable. If you can guarantee that all implementations are serializable then you can opt for this approach, perhaps supplementing it with a check in ejbCreate() to ensure that the implementation is actually serializable. Where you cannot guarantee that the implementation is serializable, you have to assume that it is not; therefore, you have to unload the BeanFactory at passivation and reload it on activation.

As with the stateless session bean, we need to define the location of the configuration for the ApplicationContext in the EJB deployment descriptor; this is shown in Listing 13-22.

Listing 13-22: Deployment Descriptor for Stateful Session Bean

image from book
<session>     <description>Counter Service Bena</description>     <ejb-name>CounterServiceEJB</ejb-name>     <local-home>        com.apress.prospring.ch13.ejb.CounterServiceHome</local-home>     <local>com.apress.prospring.ch13.ejb.CounterServiceLocal</local>     <ejb-class>com.apress.prospring.ch13.ejb.CounterServiceEJB</ejb-class>     <session-type>Stateful</session-type>     <transaction-type>Container</transaction-type>     <env-entry>     <env-entry-name>ejb/BeanFactoryPath</env-entry-name>     <env-entry-type>java.lang.String</env-entry-type>     <env-entry-value>applicationContext.xml</env-entry-value>     </env-entry> </session>
image from book

Accompanying this is the corresponding entry in the JBoss-specific deployment descriptor, shown in Listing 13-23.

Listing 13-23: JBoss Deployment Descriptor for CounterServiceEJB

image from book
<session>     <ejb-name>CounterServiceEJB</ejb-name>     <local-jndi-name>ejb/counterService</local-jndi-name> </session>
image from book

EJB Implementation Summary

Implementing EJBs with Spring is not drastically different from implementing EJBs using traditional approaches. However, Spring support makes it simple to factor out the implementation of your EJBs into a POJO class, thus reducing the barriers to testing and helping you deliver quality software.

As you saw, implementing a stateless session bean is pretty painless—you have no special requirements to bear in mind. When implementing stateful session beans, be aware of how the particular implementation of BeanFactoryLocator your bean is using affects the bean's ability to passivate successfully. If the BeanFactory returned by the BeanFactoryLocator is not serializable, then you need to remember to call loadBeanFactory() from ejbActivate() and unloadBeanFactory() from ejbPassivate(). Likewise, if the implementation of BeanFactoryLocator being used is not serializable, you need to set it to null in ejbPassivate() and, if you are not using the default implementation, re-create it in ejbActivate().

In some applications you can get around this requirement if you can ensure that any resources you obtain from the BeanFactory are serializable. If this is the case, you can simply store the resources in ejbCreate() and then unload the BeanFactory immediately, with no need to reload and unload because the bean is activated and passivated. If you need to access resources that aren't serializable, then consider using the ContextSingletonBeanFactoryLocator class to reduce the overhead of the ApplicationContext constantly being reloaded.

That concludes the implementation of the EJBs using Spring. Next, you see how to access these resources easily using Spring's EJB access support classes.

Accessing EJBs with Spring

Now that we have created our EJBs, we want, of course, to access them. Earlier in the chapter, you saw how to use Spring's JNDI support to simplify the lookup of JNDI resources. This comes in handy when we are trying to access the stateful CounterServiceEJB, because we can expose the home interface as a Spring-managed resource using JndiObjectFactoryBean. However, for the stateless EchoServiceEJB, we can go a step further. We use the LocalStatelessSessionProxyFactoryBean to create a proxy to the EchoServiceEJB and access it directly using the EchoService interface.

In this section, we are going to demonstrate how you can access both stateless and stateful session beans in a simpler manner using Spring's built-in features. We are going to jump ahead of ourselves slightly and show just the relevant methods from the example servlet. We show the full servlet code later in the section entitled "Testing EchoService and CounterService."

The JndiObjectLocator Infrastructure Component

Before we jump into the juicy details of EJB access, we want to talk about the JndiObjectLocator class in Spring. Earlier in the chapter, we showed you how you can use JndiObjectFactoryBean to look up a resource in JNDI automatically and make it available via DI. In this section, we show you how to use LocalStatelessSessionProxyFactoryBean to look up an EJB home interface in JNDI and automatically build a proxy to the EJB resource associated with that home interface. Both JndiObjectFactoryBean and LocalStatelessSessionProxyFactoryBean share a common ancestor in JndiObjectLocator. This class, along with its parent, JndiLocatorSupport, provides the common logic for obtaining JNDI objects. What does this mean for you? Simply that configuring a LocalStatelessSessionProxyFactoryBean is just like configuring a JndiObjectFactoryBean because most of the properties are exposed on the common base classes. You will see this in practice in the next two sections.

Accessing a Stateless Session Bean via a Proxy

The easiest way to access a stateless session bean in a Spring application is to use a proxy. Spring provides two proxy FactoryBean classes for stateless session beans: LocalStatelessSessionProxyFactoryBean for local beans and SimpleRemoteStatelessSessionProxyFactoryBean for remote beans.

Because the EchoServiceEJB is only exposed through a local interface, we use LocalStatelessSessionProxyFactoryBean in this example, but the configuration is identical for both classes. All you need to do to create the proxy is configure the appropriate FactoryBean in your application configuration file.

Listing 13-24 shows the configuration for the echoService bean, which is a proxy to the EchoServiceEJB.

Listing 13-24: Configuring a Stateless Session Bean Proxy

image from book
<bean        >         <property name="jndiName">             <value>ejb/echoService</value>         </property>         <property name="resourceRef">             <value>true</value>         </property>         <property name="businessInterface">             <value>com.apress.prospring.ch13.ejb.EchoService</value>         </property> </bean> 
image from book

The first thing you should notice here is that two of the properties, jndiName and resourceRef, are identical to those used in JndiObjectFactoryBean. This is because, as we described, LocalStatelessSessionProxyFactoryBean and JndiObjectFactoryBean share the same JNDI infrastructure base classes. The third property specified in the configuration, businessInterface, tells the LocalStatelessSessionProxyFactoryBean which interface the proxy should implement. In general, this is the business interface used for the EJB. You can use another interface, perhaps to limit the methods exposed, but be sure that the methods in your interface match methods in the local interface of the EJB. Remember that because LocalStatelessSessionProxyFactoryBean is a FactoryBean, Spring does not return the bean instance itself; instead, it returns the result of FactoryBean.getObject(), which in this case is the proxy to the EJB.

In the simple example servlet that we built, we placed all code related to the stateless EchoServiceEJB in the doStatelessExample() method (see Listing 13-25).

Listing 13-25: Working with the EJB Proxy

image from book
private void doStatelessExample(ApplicationContext ctx, PrintWriter writer) {         // access the EJB proxy         EchoService service = (EchoService) ctx.getBean("echoService");         writer.write(service.echo("Foo"));     }
image from book

Here you can see that the EchoServiceEJB is accessed via the proxy, as though it were just a standard Spring bean. Using this approach makes it simple to swap out EJBs for standard POJOs and vice versa without affecting any dependency components.

Simplifying Stateful Session Bean Access

When accessing stateful session beans, be aware that currently, no proxy classes are available for you to use. However, you can still simplify your code somewhat by using the JNDI support in Spring.

The first step is to configure a bean in your ApplicationContext to access the EJB home interface, as shown in Listing 13-26.

Listing 13-26: Accessing EJB Home Using Spring

image from book
<bean               >         <property name="jndiName">             <value>ejb/counterService</value>         </property>         <property name="resourceRef">             <value>true</value>         </property> </bean>
image from book

Here we simply use the JndiObjectFactoryBean to perform the lookup of the home interface automatically. In the code, we can avoid performing this lookup manually and instead access the home interface using Spring. Listing 13-27 shows the example code for the stateful session bean.

Listing 13-27: Using the CounterServiceEJB

image from book
private void doStatefulExample(ApplicationContext ctx, PrintWriter writer,             HttpSession session) {         CounterService service = (CounterService) session                 .getAttribute("counterService");              if (service == null) {                  try {                 CounterServiceHome home = (CounterServiceHome) ctx                         .getBean("counterServiceHome");                      service = (CounterService) home.create();                      session.setAttribute("counterService", service);             } catch (CreateException ex) {                 ex.printStackTrace(writer);                 return;             }         }              writer.write("Counter: " + service.increment());     }
image from book

Notice that we are able to avoid performing the JNDI lookup. Instead, we rely on Spring to do that for us. Because we are storing the handle to the stateful session bean in the HttpSession, subsequent requests to this servlet use the same instance and thus, we see the counter on the page increase.

EJB Access Summary

When using EJBs in your Spring-based applications, you have a lot of options for simplifying how you access them. When using stateful session beans, you can use Spring's JNDI support to simplify the EJB home interface lookup. For stateless session beans, you can go a few steps further and create a proxy to the EJB; this allows you to treat it as a POJO bean and frees you and your application from EJB-specific details.

Testing EchoService and CounterService

All that remains for this example is to finish off the example servlet and package up the application.

Finishing the EjbTestServlet

You have already seen the bulk of the code for the EjbTestServlet class in the doStatelessExample() and doStatefulExample() methods shown earlier. All that is left is the doGet() method, which loads the ApplicationContext and invokes both doStatelessExample() and doStatefulExample(). The code for doGet() is shown in Listing 13-28.

Listing 13-28: The doGet() Method

image from book
protected void doGet(HttpServletRequest request,             HttpServletResponse response) throws ServletException, IOException {              PrintWriter writer = response.getWriter();              response.setContentType("text/html");              writer.write("<html><head><title>EJB Samples</title></head>");              writer.write("<body>");              writer.write("<h1>Echo Service (Stateless Session Bean)</h1>");              doStatelessExample(ctx, writer);              writer.write("<h1>Counter Service (Stateful Session Bean)</h1>");              doStatefulExample(ctx, writer, request.getSession());              writer.write("</body></html>");          }
image from book

This code is fairly basic, so we do not need to go into great detail explaining it; the only point of note is that the ApplicationContext is loaded in the init() method as it was in Listing 13-2.

Packaging the Sample Application

We are not going to go into great detail regarding the packaging of the sample application, but we do want to point out that it actually has two applicationContext.xml files to configure Spring. The first, shown in Listing 13-29, goes inside the EJB JAR and is used by the EJBs for configuration.

Listing 13-29: ApplicationContext Configuration for EJBs

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>     <bean             />          <bean             /> </beans> 
image from book

Here you can see that the POJO implementations of the EchoService and CounterService interfaces are configured and made available for access by the EchoServiceEJB and CounterServiceEJB classes. The second applicationContext.xml file sits in the WEB-INF directory of the web applications WAR file. This file, shown in Listing 13-30, contains the configuration for the EchoServiceEJB proxy and the CounterServiceEJB home interface lookup.

Listing 13-30: Configuring the Sample Application

image from book
<beans>         <bean                 symbol">¿                                      LocalStatelessSessionProxyFactoryBean">         <property name="jndiName">             <value>ejb/echoService</value>         </property>         <property name="resourceRef">             <value>true</value>         </property>         <property name="businessInterface">             <value>com.apress.prospring.ch13.ejb.EchoService</value>         </property>     </bean>     <bean             >         <property name="jndiName">             <value>ejb/counterService</value>         </property>         <property name="resourceRef">             <value>true</value>         </property>     </bean> </beans>
image from book

If you have problems packaging your application, check out the build script included with the code download; it shows how we go about assembling the sample application.

Running the Sample Application

Running the sample application is easy—just drop the EAR file created by the build script into the deploy directory in JBoss and you are on your way. The first time the screen is displayed, it looks something like Figure 13-4.

image from book
Figure 13-4: First run of the sample application

Here you can see that the foo message is echoed back to the screen by the EchoServiceEJB and that the CounterServiceEJB is currently displaying the count as one. Refreshing this screen results in the same message from EchoServiceEJB, but the counter displayed by CounterServiceEJB steadily increases.

EJB Summary

In this section, we looked at Spring's support for EJB, specifically focusing on support for stateless and stateful session beans. Using Spring's base classes, you can easily create EJBs that are simply wrappers around dynamically loaded business logic. Using this approach makes for easier testing and looser coupling in your application. When accessing stateless session beans, Spring provides proxy support, freeing you of the coding burden of accessing EJB resources and decreasing the coupling in your application.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net