lunes, 13 de septiembre de 2010

Spring Remoting - Conexiones en Runtime

Resumen:
Usando Spring, crearemos una conexión por RMI en runtime, sin necesidad de especificarlo en el XML.


Breve introducción a Spring Remoting

Spring nos da soporte para hacer remoting  usando diferentes protocolos : RMI, HttpInvoker, Hessian/Burlap, etc.

Básicamente, para cada protocolo nos provee de una serie  Exporters y FactoryBeans que nos facilitan el trabajo.

Siguiendo el ejemplo que aparece en la documentación, supongamos que en nuestra apliación tenemos las siguientes clases:

AccountService
public interface AccountService {
    public void insertAccount(Account account);
    public List getAccounts(String name);
}

AccountServiceImpl
// the implementation doing nothing at the moment
public class AccountServiceImpl implements AccountService {
    public void insertAccount(Account acc) {
        // do something...
    }
  
    public List getAccounts(String name) {
        // do something...
    }
}

Para hacer este código accesible, por ejemplo a través de RMI,  añadimos en nuestro fichero de configuración de spring un bean de la clase org.springframework.remoting.rmi.RmiServiceExporter
<!-- Our AccountService bean -->
<bean id="accountService" class="example.AccountServiceImpl" />

<!-- Exporting AccountService -->
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <property name="serviceName" value="AccountService"/>
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
    <!-- defaults to 1099 -->
    <property name="registryPort" value="1199"/>
</bean>


En este punto ya hemos expuesto nuestro accountService a través de RMI y por tanto podremos acceder de manera remota desde una aplicación cliente.

Ahora, para que nuestra aplicación cliente pueda ejecutar los métodos de accountService, hay que definir en su fichero de configuración un Proxy Factory Bean que será el encargado de crear el proxy que se conectará a nuestro servicio.

Para ello, definimos un bean org.springframework.remoting.rmi.RmiProxyFactoryBean

<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://my_server:1199/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

En este punto, desde nuestra aplicación cliente podremos invocar los métodos de nuestro servicio como si fueran locales:
  // client application
  ...
  List <Account> accounts = accountService.getAccounts(someValue);
  ...

¿ Cuál es el problema ?

Tal y como lo hemos definido, cuando nuestra aplicación cliente arranca :

  1. Debe conocer a priori la url del servicio (rmi://my_server:1199/AccountService)
  2. El servicio al que se quiere conectar debe estar levantado 
Pero ....
¿ Qué ocurre si la url se proporciona en tiempo de ejecución ?
¿ Qué ocurre cuando el servicio no está activo ? ¿ no podemos arrancar nuestra aplicación cliente ?


Solución :

Una vez conozcamos la URL y nos asegurmeos de que el servicio está levantado, crearemos el Proxy en tiempo de ejecución.

Para ello, simularemos el comportamiento que tienen los beans en tiempo de inicialización.

Observando la clase RmiProxyFactoryBean vemos que implementa las interfaces:



Si Spring fuera a instanciar un bean del tipo RmiProxyFactoryBean que hubiéramos definido en el fichero de configuración,  durante la Fase de Inicialización del Contexto de Aplicación,  llamaría automáticamente al método afterPropertiesSet(), para configurar el bean y posteriormente lammaría al método getObject() para obtener una instancia, en este caso del proxy.

Simulamos pues este comportamiento :

   String url = "rmi://my_server:1199/AccountService";

   // instanciamos el FactoryBean y le establecemos las properties
   RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
   factory.setServiceUrl(url);
   factory.setServiceInterface(example.AccountService.class);
   
   // simulamos el comportamiento que tendría en tiempo de inicialización 
   factory.afterPropertiesSet();
   AccountService service = (AccountService) factory.getObject();

   // accedemos de la misma forma
   List <Account> accounts = accountService.getAccounts(someValue);

No hay comentarios:

Publicar un comentario