Introducción En esta nueva práctica vamos a seguir explorarando nuevos “modo de vida”. Ya hemos visto que este concepto es fundamental para entender el funcionamiento de una aplicación distribuida. Curso de Middleware. Práctica 4. 1 de 11 Extendiendo los modos de vida Hasta el momento hemos vistos dos modelos de vida de los objetos remotos: SingleCall y Singleton. En ambos casos, el servidor crea el objeto cuando el cliente realiza una petición. En el caso del SingleCall el servidor elimina el objeto cuando termina la llamada y en el caso de Singleton el objeto sigue activo durante un tiempo que puede ser configurable. En este ejemplo vamos a trabajar con nuevos modos de vida de un objeto. El modo “SingleCall” es un modo de vida que recuerda mucho la filosofía de HTTP y de las páginas web. Se abre una conexión, se solicita un recurso y se cierra la conexión. Al menos en su forma más habitual. Se suele utilizar cuando se requiere un servicio que no mantiene un estado previo. Por ejemplo, para lanzar una impresión en un servidor de impresoras. El modo “Singleton” en cambio se utiliza cuando es necesario mantener un estado en el servidor y que este estado sea común y único entre todos los clientes. El ejemplo más representativo es el servidor de nombres que hemos desarrollado durante este curso. Pero a veces es necesario mantener un estado y que este estado sea propio de cada cliente. Un ejemplo de ello se produce en la lista de la compra en los comercios online. Cada usuario tiene su propia lista de compra y no esperamos que un usuario vea lo que está comprando otro. En estos casos, los modos de vida anteriores no suelen ser recomendables. Queremos que sea el programa cliente quien tenga dicha responsabilidad de crear y mantener el objeto remoto. En ese caso hablamos de objetos activados por el cliente (Client-Activated Object o CAO). Antes de entrar en ese concepto vamos a ver cómo se puede configurar Remoting sin necesidad de ficheros. Para ello vamos a reemplazar el fichero de configuración por una configuración por código. La configuración por fichero es muy cómoda si la configuración es conocida de antemano ya que nos evita tener que recompilar el código. Pero habrá veces que no es posible conocer los parámetros de la configuración hasta el momento de la ejecución (por ejemplo, si le pedimos al usuario los datos de conexión). En esos casos, la configuración tendremos que hacerla por programa. El código del servidor sería algo del estilo: Curso de Middleware. Práctica 4. 2 de 11 //Servidor.cs using System; using System.Runtime.Remoting; //Atencion a estos using: using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Calculo; public class Servidor { public static void Main (string[] args) { //RemotingConfiguration.Configure("Servidor.config"); HttpChannel chnl = new HttpChannel(1234); ChannelServices.RegisterChannel(chnl); RemotingConfiguration.RegisterWellKnownServiceType( typeof(Calculo.Calculadora), "Calculadora.remota", WellKnownObjectMode.Singleton); Console.WriteLine("Atendiendo las peticiones..."); Console.WriteLine("Pulse Enter para salir..."); Console.ReadLine(); } } Si comparamos el fichero de configuración con el código utilizado es evidente la correspondencia entre uno y el otro. Podemos observar que hemos reemplazado la llamada a “Configure” por una llamadas a “RegisterChannel” y a “RegisterWellKnownServiceType “. La primera llamada es necesaria hacerla por cada uno de los canales que tengamos abiertos en nuestro servidor. La segunda es necesaria para cada tipo de objeto que queramos publicar. En nuestro ejemplo hemos definido el objeto como Singleton. Notad también que hemos incluido los nombres de espacio (namespace) “System.Runtime.Remoting.Channels” y “System.Runtime.Remoting.Channels.Http“. Esto es necesario para tener visibilidad sobre las funciones mencionadas. Dependiendo de la configuración de tu máquina es probable que, para compilar el ejemplo, tengas que incluir la librería System.Runtime.Remoting.dll en el proceso de Curso de Middleware. Práctica 4. 3 de 11 compilación. El cliente también podemos configurarlo mediante programa. Por ejemplo: //Cliente.cs using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Calculo; public class Cliente { public static void Main (string[] args) { //RemotingConfiguration.Configure("Cliente.config"); HttpChannel chnl = new HttpChannel(); ChannelServices.RegisterChannel(chnl); RemotingConfiguration.RegisterWellKnownClientType( typeof(Calculo.Calculadora), "http://localhost:1234/Calculadora.remota" ); Console.WriteLine("Creando la calculadora"); Calculadora calc = new Calculadora(); ...... Console.WriteLine("Pulse Enter para salir..."); Console.ReadLine(); } } Para el cliente podemos hacer los mismos comentarios que hemos hecho para el servidor. Dejamos al alumno analizar el código y estudiar la correspondencia con el fichero de configuración. Curso de Middleware. Práctica 4. 4 de 11 Modo de vida de objeto publicado Hasta el momento hemos vistos dos modelos de vida de los objetos remotos: SingleCall y Singleton. En ambos casos, el servidor crea el objeto cuando el cliente realiza una petición. En el caso del SingleCall el servidor elimina el objeto cuando termina la llamada y en el caso de Singleton el objeto sigue activo durante un tiempo que puede ser configurable. En este ejemplo vamos a trabajar con dos nuevos modos de vida de un objeto. Hay ocasiones en los que el servidor tiene que crear el objeto antes de cualquier llamada de los clientes. En este caso, el servidor primero crear los objetos y después los “publica”. Veamos un ejemplo: using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Calculo; public class Servidor { public static void Main (string[] args) { //RemotingConfiguration.Configure("Servidor.config"); HttpChannel chnl = new HttpChannel(1234); ChannelServices.RegisterChannel(chnl); Calculadora calc = new Calculadora(); RemotingServices.Marshal (calc,"Calculadora.remota"); Console.WriteLine("Atendiendo las peticiones..."); Console.WriteLine("Pulse Enter para salir..."); Console.ReadLine(); } } El resto del código, incluyendo el correspondiente al cliente, sigue siendo el mismo. Curso de Middleware. Práctica 4. 5 de 11 Modo de vida de objeto activado por cliente Este tipo de objetos se llaman objetos publicados y junto con los objetos de llamada simple o los singleton forman el grupo de objetos activados por el servidor (ServerActivated Object o también conocidos por SAO). Se llaman de esta forma el hecho de que cuando un cliente crea un objeto remoto, realmente lo que hace es crear un proxy al objeto sin comunicarse con el servidor. Únicamente cuando el cliente utiliza alguno de los servicios del objeto se pone en contacto con el servidor. En ese momento el servidor decide si crear o reutilizar alguno de los objetos que tiene a tal efecto. Esto es, la responsabilidad de la vida del objeto recae sobre el servidor. Cuando queremos que sea el cliente quien tenga dicha responsabilidad hablamos de objetos activados por el cliente (Client-Activated Object o CAO). Veamos un ejemplo de como se usan. Primero el servidor: using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Calculo; public class Servidor { public static void Main (string[] args) { HttpChannel chnl = new HttpChannel(1234); ChannelServices.RegisterChannel(chnl); RemotingConfiguration.ApplicationName = "UnServidorDelCurso"; RemotingConfiguration.RegisterActivatedServiceType( typeof(Calculo.Calculadora)); Console.WriteLine("Atendiendo las peticiones..."); Console.WriteLine("Pulse Enter para salir..."); Console.ReadLine(); } } Ahora la configuración del servidor es ligeramente diferente. Lo que publicamos es el nombre del servidor en lugar del nombre de los recursos que atiende dicho servidor. En nuestro ejemplo, hemos asignado un nombre de “UnServidorDelCurso”. Curso de Middleware. Práctica 4. 6 de 11 El código del cliente sería parecido a: using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Calculo; public class Cliente { public static void Main (string[] args) { HttpChannel chnl = new HttpChannel(); ChannelServices.RegisterChannel(chnl); RemotingConfiguration.RegisterActivatedClientType( typeof(Calculo.Calculadora), "http://localhost:1234/UnServidorDelCurso"); Console.WriteLine("Creando muchas calculadoras"); Calculadora calc1 = new Calculadora(); Calculadora calc2 = new Calculadora(); Calculadora calc3 = new Calculadora(); ....... } } En este ejemplo, el cliente sigue actuando como de costumbre. Para ver la diferencia en el comportamiento os proponemos incluir algunas trazas en el cliente con puntos de parada controladas (con lectura del teclado por ejemplo). Observad la secuencia de llamadas y de creación de objetos. Curso de Middleware. Práctica 4. 7 de 11 Ejercicios adicionales Os proponemos varios ejercicios que nos ayuden a entender el comportamiento de estas características: 1.- Modifica el código de la calculadora de tal forma que puedas determinar cuantos objetos se crean y si éstos se comparten entre los clientes. Te recomiendo que te ayudes de algunas trazas y de variables de instancia. Prueba con los cuatro modos de vida que hemos visto. 2.- Define en la calculadora un constructor que tome algún valor. La idea es que el constructor por defecto sin parámetros no sea público. Prueba con los cuatro modos de vida y comprueba si puedes trabajar con todos ellos. 3.- Prueba a crear varias clases del estilo de la calculadora y modifica el código del cliente y del servidor para que trabajen con varios objetos de varios tipos de forma simultanea. La idea de este ejercicio es que compruebes que puedes atender dentro de un mismo servidor a varios objetos remotos. 4.- Algunos de los ejercicios anteriores podrás hacerlos con una configuración mediante fichero. Intenta hacerlos usando las dos configuraciones: por fichero y por programa. 5.- Nos interesa conocer el momento de la liberación del objeto en memoria. Es decir, queremos conocer el momento en el que el servidor destruye el objeto remoto. En los lenguajes tipo Java o C# no existen destructores como pueden existir en C++, pero podemos ayudarnos del patrón “IDisposable”. Busca información sobre el mismo y aplicarlo a la clase Calculadora. La idea es conocer cuando se libera los objetos. Por ello, pon una traza en el método “Dispose” correspondiente. Prueba los cuatro modos de vida. ¿Puedes controlar la muerte y la liberación del objeto remoto en los cuatro modos de vida? Es un buen momento para que tu profesor os cuente conceptos de teoría. No avances en la práctica hasta asegurarte que has entendido correctamente los objetivos de la misma!!!!. Hemos completado nuestra visión de los modos de vida que nos permite .Net Remoting. El concepto de modo de vida es importante y vamos a seguir profundizando en algunos puntos de interés: • Los objetos remotos están siendo utilizados por hebras (threads) gestionadas por la capa de comunicaciones. ¿Es importante conocer este punto? ¿Habría problemas de concurrencia? ¿Por qué? ¿Este problema aparece en todos los modos de vida? • Si nos encontráramos con un problema de concurrencia ¿Cómo lo evitamos? ¿A nivel del objeto remoto? ¿En el cliente? ¿En ambos? • Si volvemos nuestra vista a otros middleware (JavaEE) quizás podamos razonar sobre los modos de vida que éstos ofrecen. Revisa de Curso de Middleware. Práctica 4. 8 de 11 nuevo los modos de vida que conoces y compáralos con los que ofrece JavaEE. ¿Hasta qué punto son similares? Si tienes la oportunidad de trabajar con Corba, haz el mismo ejercicio. • Algunos middleware no soportan todos los modos de vida que conoces. ¿Cómo haríamos un modo de vida utilizando otro?. La idea es, por ejemplo, que seamos capaces de simular un sigleton usando single call. O más interesante aún. ¿Cómo simular un CAO si el middleware solo nos ofrece SingleCall o Singleton? ¿Podría darse el caso? • A lo largo del curso veremos los WebServices, pero para aquellos de vosotros que ya conozcáis algo de ellos ¿Qué modo de vida ofrecen? ¿Cómo puedo cambiarlos? ¿Es posible? Curso de Middleware. Práctica 4. 9 de 11 Resumen A lo largo de esta práctica hemos aprendido a configurar el middleware por fichero de configuración y mediante llamadas al sistema. Hemos aprendido dos nuevos modos de vida, con sus ventajas e inconvenientes. Además hemos visto otros conceptos relacionados tales como ciclo de vida, concurrencia, etc. Curso de Middleware. Práctica 4. 10 de 11 Conceptos introducidos En esta práctica hemos introducido/repasado los siguientes conceptos: • Modo Client-Activated Object • Modo publicado • Ciclo de vida (nacimiento y muerte del objeto) • Ventajas e inconvenientes de cada modo de vida • Servlets • Web Services • Concurrencia y áreas sincronizadas en el acceso a los objetos remotos • Curso de Middleware. Práctica 4. 11 de 11