Ejb Con Spring

De Dos Ideas.
Saltar a: navegación, buscar

Spring Framework trae amplio soporte para el uso de EJB.

Introducción a EJB 3.0 de sesión
Visitá el taller donde encontrarás más información, ejemplos y prácticas sobre este tema.

Creación en EJB 2.x

Spring provee una forma simple y cómoda para crear EJBs de Stateless, Stateful y Message Driven Bean. Esto se hace heredando la implementación de los EJB de alguna de estas clases:

  • Abstract Stateless Session Bean
  • Abstract Stateful Session Bean
  • Abstract Message Driven Bean

Estas clases proveen varios beneficios:

  • proveen acceso a un factory de Spring ya inicializado
  • implementan ya varios métodos obligatorios de EJB

Las clases brindan un factory de Spring ya creado e inicializado, el cual se accede a través del método heredado getBeanFactory(). Este factory se configura a través de variables de entorno en el archivo ejb-jar.xml.

El método ejbCreate() se encuentra implementado (para poder inicializar el factory y otras tareas). Spring provee el método onEjbCreate() el cual implementaremos para inyectar "a mano" las dependencias de nuestro EJB.

Ejemplo de creación de un EJB Stateless

Supondremos que ya contamos con un objeto de negocio FlotaEspacialBO de la siguiente forma:

public class FlotaEspacialBoImpl implements FlotaEspacialBo {
    public Collection<Invasor> buscarInvasores(Long idFlota) {
        ... buscar los invasores de la flota indicada ...
    }
}

Expondremos este EJB (el cual lo tenemos ya declarado en Spring) usando un EJB Stateless. Para el EJB crearemos todas las interafaces necesarias (Home, Remote, Local, etc). La diferencia estará en el bean:

public class FlotaEspacialBean extends AbstractStatelessSessionBean {
    private FlotaEspacialBo flotaEspacialBo;
    public Collection<Invasor> buscarInvasores(Long idFlota) {
         return flotaEspcialBo.buscarInvasores(idFlota);
    }
    @Override
    protected void onEjbCreate() throws CreateException {
        flotaEspcialBo = (FlotaEspacialBo) getBeanFactory().getBean("business.FlotaEspacialBo");
    }
}

Como se ve, el bean hereda de !AbstractStatelessSessionBean, clase que ya provee varios métodos implementados para los EJB. Así, el EJB queda muy simple, invocando al objeto de negocio directamente para resolver la lógica. El método onEjbCreate() se encarga de inyectar las dependencias del EJB utilizando el método getBeanFactory() que viene heredado.

Falta entonces indicarle a Spring los archivos de configuración que deberá levantar el factory, lo cual va en el ejb-jar.xml

<ejb-jar>
   <enterprise-beans>
       <session>
           <ejb-name>FlotaEspacialBean</ejb-name>
           <home>com.dosideas.business.ejb.flota.FlotaEspacialRemoteHome</home>
           <remote>com.dosideas.business.ejb.flota.FlotaEspacialRemote</remote>
           <ejb-class>com.dosideas.business.ejb.flota.FlotaEspacialBean</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>application-ejb.xml,application-negocio.xml</env-entry-value>
           </env-entry>
       </session>
   </enterprise-beans>
</ejb-jar>

Los tag env-entry contienen la ubicación de los archivos de Spring para inicializar.

Creación en EJB 3.x

EJB 3 contiene muchas mejoras y simplificaciones al momento de crear EJBs. Integrar EJB3 y Spring es una tarea muy sencilla, que se resuelve con el uso de Anotaciones.

import javax.interceptor.Interceptors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor;
@Stateless
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class PaisSessionBeanBean implements PaisSessionBeanRemote {
   @Autowired
   private PaisBo paisBo;
   public Pais buscarPaisPorId(Long id) {
       return paisBo.buscarPaisPorId(id);
   }
}

La anotacion @Interceptors(SpringBeanAutowiringInterceptor.class) prepara un factory para ser usado en el EJB. A su vez, se encarga de inyectar todos los atributos marcados con @Autowired.

El interceptor busca un archivo beanRefContext.xml en el classpath, que contenga una instancia de un ApplicationContext, el cual referencia a los archivos de Spring a cargar. Por ejemplo:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
   <bean name="applicationContext-main" class="org.springframework.context.support.ClassPathXmlApplicationContext">
       <constructor-arg>
           <list>
               <value>application-business.xml</value>
               <value>application-dao.xml</value>
               <value>application-db.xml</value>
               <value>application-hibernate.xml</value>
           </list>
       </constructor-arg>
   </bean>
</beans>

Acceso de EJBs

Uno de los usos más interesantes es la posibilidad de dejar a Spring realizar el lookup del EJB e inyectar la interfaz de negocio directamente en nuestros objetos.

Así, delegamos a Spring la localización y creación del EJB.

Para esto, se utilizan 2 clases principales:

  • JndiTemplate, que contiene la información para realizar la búsqueda del EJB.
  • En el caso de EJB 2.x, usaremos la clase SimpleRemoteStatelessSessionProxyFactoryBean, que realizará la instanciación de nuestro EJB. Este Proxy será el que inyectaremos en nuestros otros beans.
  • En el caso de EJB 3.x, usaremos la clase JndiObjectFactoryBean, que realizará la búsqueda del EJB. Este proxy será el que inyectaremos en nuestros beans.

Ejemplo de acceso a EJB 2.x

Este ejemplo funciona con EJB 2.x.

El EJB

Supongamos que tenemos un EJB llamado PersonaServiceBean. Esta EJB está compuesto de las siguiente clases:

  • com.dosideas.business.ejb.persona.PersonaServiceRemote (interfaz de negocio remota)
  • com.dosideas.business.ejb.persona.PersonaServiceBean (implementación del EJB)

Archivo de configuración de Spring

En un archivo de Spring, podemos declarar una referencia a nuestro EJB, que luego podremos inyectar como un bean normal.

<?xml version="1.0" encoding="UTF-8"?>
<beans>
   <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"/>
   <bean id="ejb.PersonaServiceBean" 
         class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
       <property name="jndiName">
           <value>ejb/PersonaServiceBean</value>
       </property>
       <property name="jndiTemplate">
           <ref local="jndiTemplate"/>
       </property>
       <property name="businessInterface">
           <value>com.dosideas.business.ejb.persona.PersonaServiceRemote</value>
       </property>
   </bean>
</beans>

El bean con id ejb.PersonaServiceBean es un proxy que cumple con la interfaz indicada en el atributo "businessInterface". En el ejemplo, se declaró la interfaz de negocio remota del EJB. Este bean puede ya ser inyectado en cualquier otro lado y utilizado normalmente.

Spring realizará la creación del bean igual que con el resto de los objetos. En este caso, al iniciarse el factory de Spring se realizará el lookup e instanciación correspondiente del EJB. Es posible demorar esta acción con los medios tradicionales que provee Spring (lazy-init, etc.).

El atributo businessInterface

El atributo businessInterface indica la interfaz que implementará el Proxy de Spring. Un detalle interesante es que esta interfaz no tiene porqué ser la interfaz de negocio del EJB.

Así, podriamos crear una interfaz "nuestra", que cumpla con los métodos que están en la interfaz de negocio del EJB. De esta manera, nuestra aplicación queda independiente de cambios en la interfaz del EJB (y la posiblidad, además, de intercambiar libremente entre interfaces remotas y locales del EJB).

Ejemplo de acceso a EJB 3.x

Este ejemplo funciona con EJB 3.x.

El EJB

Supongamos que tenemos un EJB llamado PersonaServiceBean. Esta EJB está compuesto por la siguiente interfaz:

  • com.dosideas.business.ejb.persona.PersonaServiceRemote (interfaz de negocio remota)


Archivo de configuración de Spring

En un archivo de Spring, podemos declarar una referencia a nuestro EJB, que luego podremos inyectar como un bean normal.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
   <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate" />
   <bean id="ejb.PersonaServiceBean" class="org.springframework.jndi.JndiObjectFactoryBean">
       <property name="jndiName" value="ejb/PersonaServiceBean"/>
   </bean>
</beans>

Una forma alternativa es utilizando los tags propios para jee que trae Spring.

<jee:jndi-lookup id="ejb.PersonaServiceBean" jndi-name="ejb/PersonaServiceBean" cache="true" />

Reconexión automática de EJBs

Si por algún motivo se reinicia el servidor de EJBs, podría ser necesario reiniciar también a los clientes.

Podemos usar el atributo refresh-home-on-connect-failure para forzar una nueva búsqueda de nuestro cliente en el caso de un error de conexión. Para EJB 3.0, esto funciona a partir de Spring 2.5.5 (ya que las versiones anteriores contenían un bug).

<jee:remote-slsb id="miServicio" jndi-name="ejb/MiServicio"

   business-interface="com.dosideas.business.ejb.MiServicio"  
   cache-home="false" 
   lookup-home-on-startup="false"  
   home-interface="com.dosideas.business.ejb.MiServicioHome"  
   resource-ref="false" 
   refresh-home-on-connect-failure="true">
   <jee:environment>  
   </jee:environment>  

</jee:remote-slsb>

Ver también