introducción - cediarepositorio.cedia.org.ec/bitstream/123456789/966/27/curso_2014_5.pdf ·...

27
Introducción Uno de los objetivos en el desarrollo de aplicaciones distribuidas es separar claramente el código que el servidor ofrece y el que el cliente utiliza. Las razones son múltiples y van desde razones de diseño típicas de orientación a objetos hasta razones de mercado (no nos interesa que los clientes tengan nuestro código). Para resolver este problema es necesario que el servidor publique un “contrato” que pueda utilizar el cliente. Históricamente, este contrato se ha definido utilizando un lenguaje propio llamado IDL (Lenguaje para la Descripción del Interfaz). Con este lenguaje, el fabricante del servidor escribía el contrato que podía suministrar. El cliente utilizaba este contrato para generar parte de su código. Si observamos nuestras prácticas, la librería que tenía la Calculadora (Calculo.dll) era común tanto al cliente como al servidor. Se compilaba y enlazaba en ambas aplicaciones. Con lo que hemos visto hasta la fecha podríamos preguntarnos si es posible separar al cliente del servidor y sobre todo si son necesarios o no dichos “contratos” en .Net Remoting. Responder a estas preguntas es el objetivo de nuestra práctica. A lo largo de ella vamos a explorar varios mecanismos para la separación del código entre el cliente y el servidor. Curso de Middleware. Práctica 5. 1 de 27

Upload: others

Post on 30-Jan-2020

8 views

Category:

Documents


0 download

TRANSCRIPT

IntroducciónUno de los objetivos en el desarrollo de aplicaciones distribuidas es separar claramente el código que el servidor ofrece y el que el cliente utiliza. Las razones son múltiples y van desde razones de diseño típicas de orientación a objetos hasta razones de mercado (no nos interesa que los clientes tengan nuestro código). Para resolver este problema es necesario que el servidor publique un “contrato” que pueda utilizar el cliente. Históricamente, este contrato se ha definido utilizando un lenguaje propio llamado IDL (Lenguaje para la Descripción del Interfaz). Con este lenguaje, el fabricante del servidor escribía el contrato que podía suministrar. El cliente utilizaba este contrato para generar parte de su código.

Si observamos nuestras prácticas, la librería que tenía la Calculadora (Calculo.dll) era común tanto al cliente como al servidor. Se compilaba y enlazaba en ambas aplicaciones. Con lo que hemos visto hasta la fecha podríamos preguntarnos si es posible separar al cliente del servidor y sobre todo si son necesarios o no dichos “contratos” en .Net Remoting. Responder a estas preguntas es el objetivo de nuestra práctica. A lo largo de ella vamos a explorar varios mecanismos para la separación del código entre el cliente y el servidor.

Curso de Middleware. Práctica 5. 1 de 27

Breve HistoriaAntes de meternos con las prácticas, me gustaría contaros una breve historia de la ingeniería del software. Y es que la ingeniería del software ha influido mucho en los paradigmas y diseño de los middleware.

Al principio de los tiempos (más o menos por los años 1960) se produce un primer salto en la evolución. Uno de hecho muy importante: los programadores pasan de utilizar el ensamblador a usar lenguajes de alto nivel que deben ser compilados. Ejemplos representativos son COBOL o Fortran. Pronto se descubre que no todo es tan maravilloso como parece y se detectan problemas. El código está poco o nada estructurado, lo que convierte un programa en un monstruo difícil de mantener. Yo he llegado a ver un único programa de unas 15 páginas en Fortran (con sus GOTO)!!.

No tarda en aparece una nueva generación. Por los años finales de los 60 o principios de los 70 nacen ya lenguajes de programación que refuerzan los conceptos de estructura (C o Pascal). De todas formas, la estructura es aún muy básica. La unidad de estructura es la función o procedimiento. Se empieza a pensar seriamente sobre la reutilización del código. En esta fecha tan temprana, lo único que podemos reutilizar es la función. Los sistemas distribuidos generan tecnologías acordes con esta filosofía y nace el RPC (llamada a procedimientos remotos). El principal problema que se detecta es que la lógica del código está fuertemente unida a los datos. Es necesario encapsular el código con sus datos.

Y nace la orientación a objetos. En este nuevo paso, los programadores consiguen una nueva unidad de reutilización: la clase. Estamos sobre los años 1980 y aparecen nuevos lenguajes: Smalltalk y más tarde C++. Con el concepto de clase, los programadores pueden encapsular el código con sus datos, aislando los problemas. El mundo se ve como una jerarquía de clases que, de una forma ideal, representa nuestro mundo. Evidentemente, los sistemas distribuidos tienen que evolucionar y adaptarse a estos nuevos conceptos. El middlware evoluciona hacia la orientación a objetos, las primeras alternativas son CORBA y le siguen otras como .Net Remoting. En paralelo (en algunos casos es previo) se exploran otras alternativas como los middlware orientados al mensaje, con sistemas de colas tipo MQSeries.

Pero los problemas siguen estando. Sigue siendo difícil la reutilización de código y la integración de sistemas. El concepto de clase viene acompañado de los conceptos de herencia y jerarquía. Y estos conceptos son problemáticos para los sistemas distribuidos. No siempre se puede distribuir el código sin hacer visibles todos los niveles de la jerarquía y clases más básicas. Además, es complicado hacer que un programa escrito en Ada pueda ser utilizado por un programador de C++. Pero es que otros muchos de los problemas de distribución de código siguen sin solucionar, por ejemplo el versionado.

Era necesario evolucionar hacia nuevos conceptos. Esta evolución ha sido menos abrupta que las anteriores y desemboca en el concepto de programación orientada al componente (Component-Oriented Programming). Los primeros intentos se producen sobre los años 1990. La idea es que los programadores acuerdan un cierto contrato que debe ser neutro al lenguaje y a la arquitectura interna del sistema. Se hacen relevantes los conceptos como IDL e interface. La unidad de reutilización es el interface. El programador puede hacer y distribuir tantas implementaciones como desee (incluso en

Curso de Middleware. Práctica 5. 2 de 27

diferentes lenguajes de programación), siempre y cuando respete el contrato (interface). Cuando un cliente llama a un componente, se utiliza un protocolo neutro al lenguaje. Es decir, llamamos a los miembros del componente utilizando un protocolo determinado, incluso si en ambos lados de la llamada utilizamos el mismo lenguaje de programación. Ejemplos de estos sistemas son COM, DCOM, JavaBeans, etc.

Espero que a estas alturas del curso, sepamos identificar muchos de estos conceptos en las prácticas que ya habéis realizado. Si tienes alguna dificultad, no dudes en comentarlo con vuestro profesor.

Pero la historia no se queda aquí. Tras el esfuerzo que genera la idea de componente, los sistemas siguen teniendo problemas de reutilización de código. Y es que los ingenieros del software se encuentran una y otra vez con el mismo problema. Para reutilizar debemos separar (desacoplar) el código, pero la unión del código es lo que nos permite integrar aplicaciones y por lo tanto generar valor. Es un “no puedo vivir contigo pero tampoco sin ti”.

Y de esta forma, el sistema evoluciona hacia las aplicaciones orientadas a servicios. De nuevo se hace un esfuerzo para reducir las dependencias y aislar las unidades de reutilización. Es el momento en el que el middleware toma una nueva variante: los servicios web; pero eso lo dejamos para otro ejercicio.

El futuro está aún por escribir, pero hay una tendencia clara en la que el middleware desaparece y es el propio lenguaje el que ofrece la capacidad de distribuir la aplicación. Por ahora tenemos lenguajes paralelos ya establecidos. El propio C# ya incorpora alguna de esas características pero hay muchos más. Si tenéis tiempo mirad las propuestas de lenguajes cómo X10. El siguiente paso en toda esta evolución posiblemente sea que el lenguaje se haga responsable de toda la funcionalidad que suministra actualmente el middleware. Ya hay algunos experimentos en este sentido. Por ejemplo, usando Scala y Spark podemos procesar datos de forma distribuida (usando el modelo map/reduce). Existe una fuerte investigación en lenguajes funcionales distribuidos (por ejemplo Haskell distributido).

Pero volvamos a nuestra práctica. El objetivo de toda esta historia era ilustrar que los sistemas distribuidos actuales suelen estar desacoplados. Y para ello es importante identificar y aislar el código que queremos distribuir y reutilizar. Hasta la fecha hemos compartido el código de nuestra calculadora tanto en el cliente como en el servidor. No parece nada positivo, visto todo lo anterior, no?. Intentemos poner algún remedio...

Curso de Middleware. Práctica 5. 3 de 27

Utilización de InterfaceUno de los problemas que estabamos describiendo es que necesitamos de un mecanismo que nos permita describir lo que hace una clase sin necesidad de dar el código de la misma. Antes de ver el concepto de interface, os propongo hacer una pequeña prueba. Arranca el servidor de nuestra primera práctica con la calculadora distribuida. Asegúrate que está arrancada sobre un canal http (en el fichero de configuración aparece el protocolo). Ahora, con un navegador intenta abrir la URL añadiendo al final “?wsdl” :

http://tu maquina:numero de puerto/tu servicio?wsdl

por ejemplo:

http://localhost:1234/Calculadora.remota?wsdl

Observa que cuando utilizamos “?wsdl” como parámetro de la url, se obtiene una descripción en XML de los objetos remotos que están siendo atendidos en esa url. Intenta analizar lo que ves. Cambia algo en tu calculadora y observa como la página refleja ese cambio. Este ejercicio lo volveremos a ver cuando estudiemos SOAP (Simple Object Access Protocol ). De hecho lo que sale por nuestro navegador es la descripción en WSDL (Web Services Description Language), un estándar del W3C.

Esa descripción se corresponde con el contrato de nuestra calculadora. Es una forma de describir los servicios que ofrece nuestro objeto remoto. En este caso, estamos viendo WSDL que es una forma neutral basada en XML de describir el funcionamiento de una clase. Pero no es el único. Otra alternativa es el IDL (“Lenguaje de Definición de Interfaces”) muy utilizado en Corba y en algunos sistemas basados en Rmi. Un ejemplo de IDL sería:

module HelloApp

{

interface Hello

{

string sayHello();

oneway void shutdown();

};

};

Una tercera alternativa es utilizar el propio lenguaje de programación. Muchos lenguajes tienen un mecanismo llamado “interface” que nos permite describir dicho contrato.

Se dice que la clase que realmente desarrolla el código “implementa” el interface. Os proponemos como ejercicio diseñar un interface que se corresponda con los servicios que ofrecía nuestra clase de calculadora. Por ejemplo:

Curso de Middleware. Práctica 5. 4 de 27

// ICalculadora.cs

using System;

namespace Calculo

{

public interface ICalculadora

{

double Suma(double izq, double dch);

double Producto(double izq, double dch);

....

}

}

Y nuestra calculadora ahora se ve modificada de forma que implemente el interface:

// Calculadora.cs

using System;

using Calculo;

namespace CalculoConImplementacion

{

public class Calculadora : MarshalByRefObject, ICalculadora

{

....

}

}

Nuestra intención es que el cliente sólo vea el interface, mientras que el servidor pueda trabajar con el código completo, es decir con la clase Calculadora. Para estar seguros que no usamos por error la clase Calculadora desde nuestro cliente hemos cambiado el nombre del paquete. El servidor únicamente tendremos que cambiarlo para que utilice el espacio de nombres “CalculoConImplementación”. Posiblemente tengas que cambiar los ficheros de configuración correspondientes. Si no sabes cómo hacerlo, no dudes en preguntar a tu profesor.

El siguiente paso es modificar el código del cliente. Observad que no podemos utilizar el operador “new” como venía siendo nuestra costumbre. Dicho operador únicamente se puede utilizar con aquellas clases que son instanciables. Es decir, no podemos usarlo con interfaces o con clases abstractas. Por esta razón nos vemos obligados a utilizar en su lugar la función “Activator.GetObject”. El código del cliente quedará como sigue:

Curso de Middleware. Práctica 5. 5 de 27

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using Calculo;

public class Cliente

{

public static void Main (string[] args)

{

Console.WriteLine("Creando la calculadora");

ICalculadora calc = (ICalculadora) Activator.GetObject(

typeof(Calculo.ICalculadora),

"http://localhost:1234/Calculadora.remota");

Console.WriteLine("Prueba de suma 5+3=" +

calc.Suma(5.0,3.0).ToString());

Console.WriteLine("Prueba de producto 5*3=" +

calc.Producto(5.0,3.0));

}

}

Finalmente tenemos que modificar el proceso de compilación de forma que la librería común (Calculo.dll) sólo incluya el resultado de compilar ICalculadora. Lo que queremos crear es un combinado o solución que tenga cuatro proyectos. Es decir, :

● Un proyecto de tipo librería que tenga únicamente el fichero de código ICalculadora.cs. Nuestra librería podría llamarse “Calculo.dll”.

● Un proyecto de tipo librería que tenga la implementación del interface, es decir, tiene el fichero de código “Calculadora.cs”. La librería podría llamarse “CalculoImpl.dll”. Este proyecto depende del anterior (edita las referencias a incluir)

● Un proyecto de tipo ejecutable con el código del cliente. Contiene el fichero de código “Cliente.cs” y depende de la librería “Calculo.dll”. El ejecutable podría llamarse “Cliente.exe”.

● Un proyecto de tipo ejecutable con el código del servidor (“Servidor.cs”) que genera “Servidor.exe”. Este proyecto utiliza o depende de las librerías “Calculo.dll” y “CalculoImpl.dll”

Observa que el cliente utiliza o depende de una única librería y en esa librería NO hay código ejecutable, únicamente el interface (“ICalculadora”). El servidor en cambio depende de las dos librerías (la que tiene el interface y la que tiene la implementación).

Curso de Middleware. Práctica 5. 6 de 27

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

Antes de seguir, volvemos con nuestras preguntas habituales:

• ¿Sabes la diferencia entre clase (normal), clase abstracta e interface?. ¿Sabes cuando elegir uno u otro?

• ¿Por qué no podemos hacer un “new” de una clase abstracta? ¿Por qué tampoco lo podemos hacer un interface?

• En cambio si hacemos un new de una clase concreta, podemos asignarla a una variable declarada usando el interface. ¿Tiene sentido? Aunque pudiera ser legal, ¿Es normal hacer estas cosas?. Por ejemplo:

ImyInterface variable = new ConcreteClass();

• Los lenguajes de programación tipo Java o C# prohíben la herencia múltiple, pero dejan que una clase implemente varios interfaces. Intenta explicar las razones de tal decisión.

• Compara el interface de C# con el WSDL del servicio. ¿Puedes ver ventajas o inconvenientes?.

• El interface lo escribe el programador (lo mismo que el IDL), pero ¿Quién escribe el WSDL?. y sobre todo, ¿Cómo se escribe? ¿Podríamos tener una solución tan transparente en C++?

• ¿El concepto de especificación (por ejemplo el“.h” de C++) es similar al concepto de interface en Java o C#?

• Cuando hemos introducido el concepto de interface hemos dejado de utilizar el operador “new”. ¿Sabemos las razones?. ¿Qué nos implica?

• Sabemos que el objeto “calc” es de tipo “ICalculadora” y por lo tanto parece que no tiene “código” (no tiene implementación). Pero se utiliza habitualmente (por ejemplo con “calc.Suma(..)”). ¿Sabrías decir qué es lo que se ejecuta?. ¿Es posible que estemos “ejecutando” un interface?.

Con este ejemplo, hemos podido comprobar cómo utilizar los interfaces para separar el código entre el cliente y el servidor. A continuación veremos que también podemos utilizar clases abstractas.

Curso de Middleware. Práctica 5. 7 de 27

Clases AbstractasCuando, por alguna razón, no es posible suministrar un interface, una alternativa es crear una clase abstracta y hacer que la clase remota derive de la misma. En este caso, la forma de proceder es muy similar a la del interface por lo que apenas se darán breves pinceladas de cómo se harían.

Una clase abstracta es una clase que no desarrolla todas las funciones que la componen, dejando algunas para que sean implementadas por las clases que hereden de ellas. En C# la clase abstracta de la Calculadora sería:

using System;

namespace Calculo

{

public abstract class CalculadoraAbstract : MarshalByRefObject

{

public abstract double Suma(double izq, double dch);

public abstract double Producto(double izq, double dch);

}

}

Dado que en C# no existe herencia múltiple, la clase Calculadora no podría heredar a la vez de “CalculadoraAbstract” y de “MarshalByRefObject”. Para evitar ese problema hemos hecho que la calculadora abstracta ya derive de “MarshalByRefObject”.

Una clase abstracta tiene el mismo problema que el interface a la hora de crear nuestro objeto remoto con el operador “new” y nos veremos obligados a usar de nuevo la función “GetObject”.

El resto de la práctica se deja como ejercicio al alumno ya que apenas hay diferencias con el método del interface.

Sigamos recorriendo las posibles alternativas para separar el cliente y el servidor. A veces no es posible utilizar las técnicas anteriores. En esos casos, tendremos que recurrir a la posibilidad descrita en el siguiente ejercicio.

Curso de Middleware. Práctica 5. 8 de 27

Usando una clase.No nos dejemos engañar por el título de este ejercicio. No se trata de volver a hacer lo que ya vimos en los primeros ejercicios. Lo que propone esta técnica es utilizar una clase “vacía” en lugar de un interface o de una clase abstracta. Por “vacía” lo que queremos decir es que debemos crear una clase que, aunque completa, no incorpore el código que implementa el cuerpo de las funciones. Mejor lo vemos con un ejemplo:

using System;

namespace Calculo

{

public class Calculadora: MarshalByRefObject

{

public virtual double Suma (double izq, double dch)

{

throw new NotImplementedException ();

}

public virtual double Producto (double izq, double

dch)

{

throw new NotImplementedException ();

}

}

}

Esta clase es la que haremos pública y será la que pueda utilizar el cliente. En cambio, el servidor deberá utilizar una versión completa de la calculadora. Por ejemplo:

Curso de Middleware. Práctica 5. 9 de 27

using System;

namespace PaqueteServidor

{

public class CalculadoraCompleta : Calculo.Calculadora

{

public CalculadoraCompleta()

{

Console.WriteLine("Estoy en el constructor");

}

public override double Suma(double izq, double dch)

{

Console.WriteLine("Estoy sumando");

return izq+dch;

}

public override double Producto(double izq, double

dch)

{

Console.WriteLine("Estoy multiplicando");

return izq*dch;

}

}

}

Como en los ejercicios anteriores, hemos cambiado el nombre de espacios para no generar dudas en la utilización del cliente. Observad que las funciones las hemos declarado “virtual” en la calculadora vacía y “override” en la calculadora completa. Es la forma que tiene C# de asegurar que el programador no cometa errores accidentales. Le obliga a declarar las funciones que quiere sobreescribir. Si tienes dudas sobre los conceptos de sobrecarga (overload) y sobreescritura (override) pregunta a tu profesor. El segundo es muy relevante para los middleware.

Los cambios en el servidor son mínimos:

Curso de Middleware. Práctica 5. 10 de 27

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

...

using Calculo;

using PaqueteServidor;

public class Servidor

{

public static void Main (string[] args)

{

...

RemotingConfiguration.RegisterWellKnownServiceType(

typeof(PaqueteServidor.CalculadoraCompleta),

"Calculadora.remota",

WellKnownObjectMode.Singleton);

...

}

}

La diferencia en nuestro cliente es que ahora ya podemos volver a usar el operador “new” puesto que ahora tenemos una clase instanciable (aunque aparentemente poco útil):

Curso de Middleware. Práctica 5. 11 de 27

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using Calculo;

public class Cliente

{

public static void Main (string[] args)

{

RemotingConfiguration.RegisterWellKnownClientType(

typeof(Calculo.Calculadora),

"tcp://localhost:1234/Calculadora.remota" );

Console.WriteLine("Creando la calculadora");

Calculadora calc = new Calculadora();

Console.WriteLine("Prueba de suma 5+3=" +

calc.Suma(5.0,3.0));

Console.WriteLine("Prueba de producto 5*3=" +

calc.Producto(5.0,3.0));

}

}

Esta última técnica puede parecernos un tanto “extraña”. Lo primero que debería llamarnos la atención es que no se levanta la excepción “NotImplementedException”. Al fin y al cabo, es la calculadora que hemos creado y es lo que hace la función a la que hemos llamado, no? Por otra parte, puede parecernos poco útil crear una clase vacía cuando ya hemos visto las alternativas del interface o la clase abstracta.

Hemos incluido esta alternativa principalmente para hacernos reflexionar sobre como funciona el operador “new” y entender .Net Remoting.

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

Pero antes de seguir, vamos a pararnos un segundo:

• ¿Qué ventaja o inconvenientes puedes ver sobre los tres casos de separación que hemos visto? Me refiero a la utilización del interface, de una clase abstracta y de una clase “vacía”. Si sabes Corba, ¿Se pueden utilizar estas alternativas? ¿Puedes justificar la respuesta?

• De este último ejemplo, parece que el sistema no instancia una

Curso de Middleware. Práctica 5. 12 de 27

calculadora en local o si lo hace, no la utiliza. Antes de seguir, intenta dar una explicación. Incluso, haz un esquema (tipo UML o uno de tu cosecha) sobre tu explicación.

• En la introducción hemos visto que los programadores intentan separar el contrato (“interface”) de la implementación. ¿Crees que se ha conseguido con las alternativas anteriores?

• Si desarrollamos un servicio/componente que queremos poner a disposición al mundo entero, ¿que alternativa tienes para distribuir el “contrato”? Es decir, ¿cómo le dirías al mundo entero que tu calculadora sabe sumar? ¿Es WSDL una solución? ¿Qué sería mejor WSDL, interface, clase abstracta o clase vacía?

La pregunta que deberíamos habernos hecho a estas alturas es cómo el operador “new” es capaz de crear unas veces objetos locales y otras veces objetos remotos. Hemos visto cómo el mismo operador genera una cosa u otra dependiendo de la configuración de nuestra aplicación. Este comportamiento debería sorprendernos aún más al observar que no es posible redefinir dicho operador. Otros lenguajes nos permiten redefinir operadores y entre ellos el operador “new”. En C# podemos redefinir algunos operadores, por ejemplo los matemáticos (+, -, /, etc.) pero no podemos redefinir el operador “new”.

La solución a este misterio es muy compleja y necesitaríamos profundizar bastante en los interiores de .Net. Para entender mejor estas características hemos preparado una práctica especifica sobre AOP en C#.

Antes de continuar con otros aspectos de .Net Remoting vamos a ver una herramienta que puede ser útil para realizar el objetivo de nuestra práctica: separar el código entre el cliente y el servidor.

Curso de Middleware. Práctica 5. 13 de 27

ProxysSupongamos que tenemos un servicio remoto de tanta aceptación que resulta complejo distribuir nuestro interfaz o clase abstracta por todo el mundo.

Lo ideal sería que nuestros clientes pudieran generar el código a partir de dicha información, no?. Pues eso es lo que intenta hacer las herramientas incorporadas en el Visual Studio. También Mono dispone de una herramienta similar, aunque su funcionamiento es ligeramente diferente.

Aseguraros que tenéis un servidor arrancado con tcp o http, aunque os recomendamos http ya que podréis ver el wsdl desde el navegador. Un ejemplo de WSDL sería:

Desde el Visual Studio añadir una “referencia de servicio”. El nombre puede cambiar de una versión a otra, pero lo hacemos pulsando el botón derecho sobre referencias en el proyecto. Cuando se abra el menú contextual seleccionamos la opción añadir referencia de servicio.

Curso de Middleware. Práctica 5. 14 de 27

A continuación, se abrirá una pantalla para introducir datos. Cuando nos pida la url, debemos incluimos nuestra url (incluyendo “?wsdl”) para indicar el esquema del servicio.

En la pantalla aparecerá información sobre los servicios ofrecidos por la calculadora.

Curso de Middleware. Práctica 5. 15 de 27

Antes de pulsar “Ok” aseguraros que hemos cambiado el nombre del namespace a algo que nos recuerde que es nuestra calculadora. En el ejemplo anterior, yo he puesto “Calculo”.

El siguiente paso es construir el código del cliente que llame a un método de la calculadora. Por ejemplo, os propongo algo del siguiente estilo:

Curso de Middleware. Práctica 5. 16 de 27

using System;

using System.Threading;

using Cliente.Calculo;

namespace Cliente

{

class Program

{

public static void Main(string[] args)

{

Thread.Sleep(2 * 1000); // Esto es para darle tiempo

al servidor a que arranque

Console.WriteLine("Esto es una prueba de

calculadora");

CalculadoraPortTypeClient calc = new

CalculadoraPortTypeClient();

Console.WriteLine("La suma de 2 + 3 es : " +

calc.Suma(2.0, 3.0));

Console.WriteLine("Pulse Enter para salir...");

Console.ReadLine();

}

}

}

Y cuando ejecutemos nuestro cliente, podremos observar que el servicio ha funcionado correctamente:

Curso de Middleware. Práctica 5. 17 de 27

Si nuestro servidor aún conserva la traza, deberíamos haber visto una traza en el servidor indicando que va a sumar.

Lo que hemos hecho es construir un proxy usando la información que nos ha llegado desde WSDL. Antes de seguir deberías mirar el concepto de “patrón proxy”. Si no encuentras documentación en la red, pregunta a tu profesor. La siguiente gráfica, extraída de la wikipedia, representa dicho patrón en UML:

Este concepto es importante, ya que lo que la herramienta está generando es de hecho un tipo muy particular de “proxy”. Este patrón de una forma u otra se utiliza en la mayoría de los middleware. Por ejemplo, en Corba es un concepto fundamental y lo veremos en su práctica correspondiente.

Si quieres ver lo que se ha generado en el Visual Studio, haz lo siguiente. En la vista de clases, selecciona algún elemento relacionado con la calculadora y pide “ir a definición”, según la siguiente pantalla:

Curso de Middleware. Práctica 5. 18 de 27

Eso nos abrirá el código de nuestro proxy. En el ejemplo anterior se llama “Reference.cs”. No será un código fácil de entender. No está pensado para que lo procesen los humanos ya que es un código generado y mantenido automáticamente. El código generado sería algo parecido a lo siguiente (también es posible que cambie según la versión). Solo incluimos una parte para nuestra curiosidad, pero la totalidad del código puede ser algo abrumador.

Curso de Middleware. Práctica 5. 19 de 27

public double Suma(double izq, double dch) {

Cliente.Calculo.SumaRequest inValue = new

Cliente.Calculo.SumaRequest();

inValue.izq = izq;

inValue.dch = dch;

Cliente.Calculo.SumaResponse retVal =

((Cliente.Calculo.CalculadoraPortType)(this)).Suma(inValue);

return retVal.@return;

}

[System.ComponentModel.EditorBrowsableAttribute(System.ComponentM

odel.EditorBrowsableState.Advanced)]

System.Threading.Tasks.Task<Cliente.Calculo.SumaResponse>

Cliente.Calculo.CalculadoraPortType.SumaAsync(Cliente.Calculo.Sum

aRequest request) {

return base.Channel.SumaAsync(request);

}

public

System.Threading.Tasks.Task<Cliente.Calculo.SumaResponse>

SumaAsync(double izq, double dch) {

Cliente.Calculo.SumaRequest inValue = new

Cliente.Calculo.SumaRequest();

inValue.izq = izq;

inValue.dch = dch;

return ((Cliente.Calculo.CalculadoraPortType)

(this)).SumaAsync(inValue);

}

Si nos fijamos en el código observamos que se corresponde con un “proxy” de cliente. Esto es, el código que se inserta cuando creamos un objeto remoto en el cliente. Esta técnica de generación de proxys es muy habitual y es una alternativa a otras que veremos a lo largo del curso.

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

Por ahora, vamos a intentar afianzar algunos conceptos de teoría, revisando nuestros conocimientos de un “proxy”:

Curso de Middleware. Práctica 5. 20 de 27

• ¿Qué es un patrón proxy en la programación tradicional? ¿Y un Firewall-proxy en el mundillo de las redes?. ¿Podríamos decir que es lo mismo? Busca en la red alguna definición de un patrón “proxy”.

• ¿Qué relación tienen los conceptos de la pregunta anterior con lo que genera la herramienta y que también hemos llamado “proxy”?.

• Mira la gráfica de UML anterior. Es un gráfico de un patrón que no refleja los objetos distribuidos. ¿Sabrías decir qué elementos de la gráfica están en cliente y cuáles en el servidor?.

• Intenta buscar una implementación en algún lenguaje conocido como java o C#. ¿Se parece a lo que hemos hecho hasta la fecha con nuestra calculadora?. Seguramente no demasiado, ¿Verdad?. No es tan visible, ya que es el middleware quien genera el proxy de forma automática.

• En la gráfica UML aparece el interface de una clase. ¿Significa eso que no estábamos utilizando el concepto de proxy en nuestras primeras prácticas? Me refiero a las prácticas iniciales en las que no había interfaces, clases abstractas o similares.

• Revisa el código que has programado para este curso, ¿A qué clase o clases crees que se asocia el elemento “RealSubject” que aparece en el UML?

• Compara las dos gráficas de este capítulo (la del proxy y la de los canales). ¿Puedes ver su conexión? No es trivial y lo mejor es que le pidas a tu profesor que os lo explique.

No vamos a profundizar mucho más en el código, pero os animamos a que exploréis algunos de las técnicas utilizadas. Por ejemplo, observar cómo se hacen llamadas asíncronas en el proxy. O cómo se retornan Tareas (threads ligeros) en algunos de los métodos. Si os parece interesante, pide a tu profesor que te amplíe estos conceptos.

Este último ejercicio nos ha demostrado la capacidad de Remoting para generar automáticamente el proxy y de publicar las característica de un servicio sin necesidad de intercambiar IDL o interfaces.

Existe además otra herramienta denominada wsdl disponible tanto en Mono como en .Net de Microsoft. Esta herramienta genera el código necesario para conectar con servicios web (usando SOAP). Aunque esta herramienta estará obsoleta por la aparición de la Windows Communication Fundation, merece la pena dedicarle un rato a la herramienta y por ello os proponemos el siguiente ejercicio. Nota: la herramienta wsdl.exe se suele encontrar en un directorio llamado “C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin” o similar.

Curso de Middleware. Práctica 5. 21 de 27

Ejercicio con Servicios WebOs proponemos el siguiente ejercicio. Mirad la siguiente url: http://www.service-repository.com/ Esta url contiene un número considerable de servicios web. Podemos ver y utilizar muchos de ellos.

Para la realización de esta práctica yo tomé uno llamado TempConvert pero es posible que cuando hagamos la práctica no esté disponible. Elige cualquier otro que veas disponible (aparecerá en color verde). Si finalmente no pudieras acceder a algún servicio web, siempre podrás realizarla con un servicio web hecho por tí. Hacer uno no es muy complicado si utilizas JavaEE o WCF.

Podemos ver el api para conectar con los servicios. En nuestro ejemplo está en: http://www.w3schools.com/webservices/tempconvert.asmx?WSDL

Para generar el código de la librería repetiremos los pasos anteriores (añadir referencia externa).

Curso de Middleware. Práctica 5. 22 de 27

El siguiente paso es hacer un programa que utilice dicha librería y que conecte con el servidor. Por ejemplo, el siguiente código utiliza dicho servicio:

Curso de Middleware. Práctica 5. 23 de 27

using System;

using System.Threading;

using Cliente.Calculo;

using Cliente.Temperatura;

using System.ServiceModel;

namespace Cliente

{

class Program

{

public static void Main(string[] args)

{

Thread.Sleep(2 * 1000); // Esto es para darle tiempo

al servidor a que arranque

Console.WriteLine("Esto es un ejemplo de Web

Service");

Cliente.Temperatura.TempConvertSoapClient tempServ =

new TempConvertSoapClient("TempConvertSoap");

Console.WriteLine("CelsiusToFahrenheit responde con :

" + tempServ.CelsiusToFahrenheit("25"));

Console.WriteLine("Pulse Enter para salir...");

Console.ReadLine();

}

}

}

Si lo ejecutamos podremos ver algo similar a:

Curso de Middleware. Práctica 5. 24 de 27

Interesante!!, verdad?

Busca algún otro servicio web de la red, o utiliza alguno que hayas programado para esta u otra asignatura. Si no encuentras nada, pide a un compañero que te cree un servidor que publique un WDSL. Repite el proceso anterior y comprueba que puedes hacer un programa C# que “dialogue” con el servicio en pocas líneas de código.

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

Este ejemplo último es interesante. Veamos algunas preguntas:

• Hemos visto varios métodos para separar el “contrato” de la implementación. Desde usar interfaces hasta usar clases abstractas, etc. ¿Cuál es el método que hemos usado para conectarnos a Google?.

• ¿Cómo nos hemos informado sobre las características del servicio de Google?

• Un aspecto interesante es que hemos hablado con un servidor remoto sin apenas saber nada de la arquitectura del mismo. No sabemos nada del S.O., tipo de máquinas, lenguajes de programación utilizados, etc. ¿Cómo ha sido posible?

Curso de Middleware. Práctica 5. 25 de 27

EjercicioCon todo lo visto en esta práctica, ahora te proponemos aplicarlo al servidor de nombres que construimos en la práctica anterior. Modifica la práctica anterior para separar el código del cliente del servidor. Prueba con algunas alternativas y observa su funcionamiento. Y no te olvides del rendimiento. También prueba a generar el wsdl. Cuando termines, muestra tu trabajo al profesor.

Curso de Middleware. Práctica 5. 26 de 27

ResumenAl principio de esta práctica nos planteamos la duda de cómo separar el código cliente del servidor. Incluso surgió la pregunta de si era necesario un lenguaje tipo IDL. A lo largo de la misma hemos visto algunos de los mecanismos que nos permiten separar el código entre uno y otro. Hemos comprobado que incluso podemos generar el código del cliente a partir de la meta información que el propio servidor publica.

Podemos pensar que el WSDL hace la misma función que el IDL, pero la diferencia en Remoting es que el esquema también funciona en el sentido contrario: se genera a partir de código. Esto es posible por la capacidad de reflexión que tiene la plataforma (Reflexión es una propiedad de los lenguajes de programación que permite por programa interrogar las propiedades del propio código).

Curso de Middleware. Práctica 5. 27 de 27