Trabajo Sobre Servicios Web
Trabajo Sobre Servicios Web
Trabajo Sobre Servicios Web
GRADO
CONTINUA APLICACIONES
DISTRIBUIDAS
WS | ENUNCIADO Y ORIENTACIONES PARA SU DESARROLLO
2019-2020
1.- OBJETIVOS
Esta práctica consiste en realizar un sistema distribuido basado en tecnología de servicios Web que permita
ejecutar un generador de señales y obtener los valores de tiempo y calculados por dicho generador.
Para ello se va a implementar la funcionalidad del servicio usando dos tecnologías específicas diferentes
basadas en la arquitectura SOA, y que están asociadas a las tecnologías de servicios Web:
- Servicios SOAP. Fue el primer estándar para publicar y consumir servicios Web. Es un estándar
basado en XML, y todos los mensajes involucrados en el proceso de
uso/búsqueda/consumo/gestión de procesos están basados en el estándar SOAP
(http://www.w3.org/TR/soap/). En el caso de Java, existe un API específico denominado JAX-WS
(http://jax-ws.java.net/) que permite procesar la información SOAP de los mensajes sin necesidad de
un directorio de servicios. Este API fue diseñado originalmente por Sun (ahora Oracle) e
implementada en su pila de servicios Web llamada Metro (http://metro.java.net/)
Para el proceso de desarrollo, se puede partir de varias premisas a la hora de desarrollar un servicio
pero la más habitual es partir de una clase/interface que implemente/defina las operaciones que
publicará el servicio Web (a esta clase/interface se le denomina POJO, Plain Old Java Object).
Posteriormente, se deben añadir “anotaciones” a la clase para indicar el comportamiento del servicio
Web. Las “anotaciones” permiten añadir información al código Java sin interferir en el propio código.
Esta información puede ser usada por otros elementos en el proceso de desarrollo de código java (el
propio compilador) para añadir “elementos ejecutables” (código adicional, ficheros XML, etc.) a la
aplicación. En el caso de JAX-WS, las anotaciones permiten definir, entre otras cosas, que interface/
clase define al servicio web (@WebService) o qué métodos conforman el conjunto de operaciones
del servicio (@WebMethod). Por tanto, una vez desarrollada la clase que implementan las
operaciones del servicio, se procederá a “anotar” la clase para indicar las propiedades del servicio
SOAP. Para una revisión completa de las anotaciones disponibles en JAX-WS, se debe consultar el
siguiente enlace:
https://www.ibm.com/support/knowledgecenter/SSAW57_9.0.5/
com.ibm.websphere.nd.multiplatform.doc/ae/rwbs_jaxwsannotations.html
El procedimiento de desarrollo es bastante directo, pero puede ocurrir que los parámetros de las
operaciones puedan ser elementos complejos, como clases definidas por el usuario. En este caso es
necesario definir un “mapeo” de las clases de usuario a un formato XML que se pueda usar desde
SOAP (recuerde que SOAP es básicamente XML). Para esto se emplea JAXB, que es un API de
procesamiento XML que permite hacer este trabajo automáticamente, mediante anotaciones. Para
más detalles, se debe leer el siguiente enlace:
http://ingmmurillo.blogspot.com.es/2010/08/utilizacion-de-jaxb-para-mapear-clases.html
Para la parte práctica, se va a definir una interface que define las operaciones del servicio JAX-WS
(como un objeto POJO):
package es.uned.scc.grados.appdist.trabajos.ws;
import javax.jws.WebParam;
import javax.jws.WebService;
@WebService
public interface SignalGeneratorWS {
public OperationInfo start();
public OperationInfo stop();
public OperationInfo isRunning();
public SignalData getSignalValue();
public SignalParameters getSignalParameters();
public void setSignalParameters(
@WebParam(name="signal_parameters")SignalParameters signal_parameters);
Se puede observar que la interface define el servicio Web y dispone de una anotación especial para
nombrar el argumento de la operación setSignalParameters().
Las clases OperationInfo, SignalData y SignalParameters se proporcionan en el espacio
virtual del curso, y están implementadas en el fichero SignalModel.jar. Así mismo, en el propio curso
virtual se muestra el código asociado a dichas clases para comprender su funcionamiento en el
posterior uso de las mismas durante el trabajo. Importante: El código de estas clases ha de
utilizarse importando el archivo JAR directamente, nunca el código fuente. Asímismo, no se
han de añadir dichas clases al código presentado por el estudiante.
- Servicio REST. Al contrario que SOAP, REST (REpresentational State Transfer) no es un protocolo,
sino una forma más simple que SOAP para crear servicios Web (más bien, se puede decir que es
una arquitectura de desarrollo). Fue ideada por el Dr. Roy Fielding en su tesis doctoral, y
básicamente consiste en emplear el modelo de comunicación web tradicional (el protocolo HTTP)
para identificar/consumir servicios Web. Un servicio web creado sobre los principios de REST se
denomina servicio web RESTful. Igual que antes, para el caso de Java, existe un API de referencia
para la implementación de dichos servicios: JAX-RS (http://jcp.org/en/jsr/detail?id=311). Existen
varias implementaciones de este API, por ejemplo el contenedor Jersey (http://jersey.java.net/) entre
otros. Se recomienda leer los siguientes enlaces para comprender los fundamentos de REST:
http://www.infoq.com/articles/rest-introduction
http://eamodeorubio.wordpress.com/2010/07/26/servicios-web-2-%C2%BFque-es-rest/
http://www.fing.edu.uy/inco/grupos/lins/seminario/2010/Introduccion_WS-REST.pdf
Al igual que JAX-WS, JAX-RS dispone de sus propias anotaciones para definir el contrato del
servicio y las operaciones/recursos que ofrece. Para más detalles, consultar el enlace de la
implementación de referencia Jersey: http://jersey.java.net/nonav/documentation/latest/jax-rs.html,
donde se explican todas las anotaciones del estándar con ejemplos de su uso.
Para esta parte de la práctica, se va a definir una interface que define las operaciones del servicio
JAX-RS (como un objeto POJO), a cuya implementación se le deberán añadir las anotaciones JAX-
RS correspondientes:
package es.uned.scc.grados.appdist.trabajos.ws;
import es.uned.scc.grados.appdist.trabajos.signal.model.data.SignalData;
import
es.uned.scc.grados.appdist.trabajos.signal.model.data.SignalParameters;
import
es.uned.scc.grados.appdist.trabajos.signal.model.data.OperationInfo;
Para el desarrollo de esta práctica, se va a emplear Apache CXF (http://cxf.apache.org/) ya que proporciona
un entorno de ejecución e implementaciones de las APIs, tanto de JAX-WS como de JAX-RS. En particular,
se recomienda que se lean los ejemplos que están ubicados en la documentación del producto:
1) Escribir/Publicar/Consumir un servicio JAX-WS con CXF (http://cxf.apache.org/docs/a-simple-jax-ws-
service.html)
2) Conceptos básicos de JAX-RS (http://cxf.apache.org/docs/jax-rs-basics.html)
http://www.javamexico.org/blogs/mariogarcia/servicios_web_con_apache_cxf_utilizando_jaxws_y_jaxrs
Es importante descargarse la distribución CXF (la versión empleada es la 3.3.3, pero debería funcionar con
cualquiera superior o de la misma rama, 3.x) desde la web de cxf: http://cxf.apache.org/download.html. Para
la compilación/ejecución de los elementos de esta parte, hay que añadir los ficheros jar correspondientes a
esta distribución (o configurar el IDE de manera adecuada).
Esta clase proporciona la gestión de ejecución de la tarea asociada al generador y permite obtener la
referencia al objeto de la clase SignalGenerator, que proporciona los métodos de acceso al valor de la
señal (SignalData) y los parámetros de la misma ( SignalParameters). Para obtener el valor de la señal
se puede usar el siguiente grupo de sentencias ( sgThread es una instancia de la clase
SignalGeneratorThread):
SignalGenerator sg = this.sgThread.getSignalgenerator();
sd = new SignalData(sg.getTime(), sg.getOutput());
Para gestionar la lectura/escritura de los parámetros del generador de señales se pueden usar las siguientes
sentencias:
// Lectura
SignalGenerator sg = this.sgThread.getSignalgenerator();
SignalParameters sp = new SignalParameters(sg.getType(), sg.getAmplitude(), sg.getFrequency());
// Escritura
SignalGenerator sg = this.sgThread.getSignalgenerator();
sg.setSignalType(sp.getType());
sg.setAmplitude(sp.getAmplitude());
sg.setFrequency(sp.getFrequency());
La clase que implementa el servicio se denomina SignalGeneratorWSImpl, y debe tener las anotaciones
necesarias para marcar la clase como un servicio Web. Al implementar la interface correspondiente (que ya
está anotada como @WebService) automáticamente se define como servicio Web, pero es necesario añadir
el código asociado para identificar el punto de publicación del servicio Web (que permite conocer su WDSL,
y el punto de invocación de las operaciones por parte del cliente). De nuevo, es necesario usar las
anotaciones de JAX-WS para indicar esto. Las anotaciones permiten la herencia, así que podemos “redefinir”
la anotación e indicar un conjunto de propiedades de la anotación. En este caso, se necesita indicar que
interface define el conjunto de operaciones del servicio (propiedad endpointInterface) y el nombre a
través del cual se accederá al servicio ( serviceName). Por tanto, se deberá añadir la anotación a la clase
SignalGeneratorWSImpl, quedando la definición de la clase así:
@WebService(endpointInterface =
"es.uned.scc.grados.appdist.trabajos.ws.SignalGeneratorWS",
serviceName = "SignalGenerator")
public class SignalGeneratorWSImpl implements SignalGeneratorWS {
…
En el caso del servidor SOAP, es muy sencillo usar las funcionalidades de CXF para que ejecute un
contenedor de servicios JAX-WS, y publique en una URL conocida el servicio. El código necesario (que se
puede encontrar en la documentación de CXF) es el siguiente.
Se puede ver que solo se necesita indicar la dirección de publicación (address) y la clase que implementa el
servicio SOAP (se le pasa al constructor de la clase el tiempo asociado a la tarea definida en la clase
SignalGeneratorThread, pero el estudiante puede desarrollarlo de otra forma). El parámetro sampleTime
define el tiempo de actualización de la señal generada por la tarea (salida y tiempo), y debe usarse un valor
adecuado (se recomienda un valor de 0.01 segundos). Es importante que el servidor SOAP debe quedar
ejecutándose. Para ello se puede usar un mecanismo de entrada/salida que compruebe si el servidor debe
parar o no. Un ejemplo de mecanismo basado en petición de una cadena de texto, podría implementarse con
la siguiente función:
public void waitForKey(){
boolean waiting = true;
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter 'yes' to exit...");
String c;
while (waiting){
try {
c = in.readLine();
} catch (IOException e) {
e.printStackTrace();
c = "Yes";
}
if (c.equalsIgnoreCase("yes")){
waiting = false;
} else {
System.out.println("Entered '" + c + "' string (wrong!!!)\nPress 'yes' to exit...");
}
}
}
Una vez que el servidor SOAP está ejecutándose, se debería poder visualizar el documento WDSL de
definición del servicio en el siguiente enlace: http://localhost:9000/SignalGenerator?wsdl
El primer paso para desarrollar el cliente SOAP es obtener la “representación” del servicio como clases Java.
Para ello, se debe usar el enlace de acceso al fichero WDSL y usar alguno de los siguientes mecanismos:
1) Usar los asistentes del IDE de desarrollo para generar las clases que representan al servicio. Puede
consultar los siguientes enlaces para ver como se hace en Eclipse
(http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jst.ws.cxf.doc.user%2Ftasks
%2Fcreate_client.html) o Netbeans (http://netbeans.org/kb/docs/websvc/jax-ws.html, sección
Consuming the Web Service, Client 1: Java Class in Java SE Application).
2) Usando la utilidad wdsl2java que viene con la distribución de CXF. Se pueden ver las opciones de
las que dispone la utilidad en el siguiente enlace: http://cxf.apache.org/docs/wsdl-to-java.html. Este
comando generará las clases Java necesarias (que representan al servicio Web) que se deben
incorporar al proyecto en el que se defina el cliente (importándolas o copiándolas directamente en el
directorio donde se ubiquen las fuentes dl proyecto).
Se recomienda el uso de la primera opción, ya que genera las clases dentro del propio proyecto. Además, el
cliente SOAP debería implementarse en un proyecto nuevo, ya que al generar las clases que representan al
servicio es posible que se “sobre-escriban” las originales (se puede modificar el mapeo de los espacios de
nombres, pero es más sencillo aislar la generación de las clases en un proyecto nuevo). Una vez que se han
generado las clases, para poder usar el servicio web se debe usar la clase Proxy generada, que se debería
llamar SignalGeneratorWSProxy. Simplemente se debe instanciar y llamar al método adecuado.
Para probar el funcionamiento del servidor SOAP, se debe emplear la clase auxiliar ClientGUI
incorporándola en el código del cliente SOAP (mediante las clases que sean necesarias). Para su correcto
funcionamiento será necesario importar las librerías “Jfreechart” y “Jcommon”, cuyos JAR también se
proporcionan en el espacio virtual del curso.
La clase auxiliar ClientGUI proporciona una interface gráfica (GUI) que permite comprobar el
funcionamiento del servidor. Para emplear esta función es necesario que la clase cliente (o una auxiliar)
implemente la interface ClientPlot:
package es.uned.scc.grados.appdist.trabajos.plot;
import es.uned.scc.grados.appdist.trabajos.signal.model.data.SignalData;
import es.uned.scc.grados.appdist.trabajos.signal.model.data.SignalParameters;
De esta forma, la instancia de la clase que representa la GUI podrá invocar a las funciones de dicha interface
que, a su vez, estarán implementadas en la clase que emplea el cliente SOAP. La forma más sencilla de
desarrollar este mecanismo es asociar a una clase auxiliar denominada PlottingFrame la implementación
de la interface y usar las llamadas correspondientes dentro del código de cada función de la interface. Esta
sería la definición de dicha clase (sin la implementación de las funciones de interface, que le corresponde
realizar al estudiante).
public class PlottingFrame implements ClientPlot{
Para visualizar la interface, simplemente se debe crear una instancia de la clase PlottingFrame y mostrar
la interface, una vez creado el proxy de acceso al servicio SOAP
if (sgClient!=null){
PlottingFrame window = new PlottingFrame(sgClient);
window.show();
}
Finalmente, es necesario anotar el @Path asociado al propio servicio (todos los @Path definidos en las
operaciones son relativos al @Path del servicio). Para ello se debe añadir al principio de la definición de la
clase, la siguiente anotación:
@Path("SignalGenerator")
public class RESTSignalGeneratorWSImpl implements RESTSignalGenerator{
De esta forma, para invocar la operación start() del servicio REST se deberá usar el siguiente URI relativo:
“SignalGenerator/start”. En la siguiente sección, una vez desarrollado el servidor, se verán ejemplos de URI’s
absolutas para el uso del servicio REST.
La creación del servidor REST con CXF es muy sencilla, y consiste en cuatro pasos sencillos, que se
comentan a continuación:
Por tanto, solo es necesario codificar la clase RESTWSServer con un método main() que contenga las
líneas anteriores. Al arrancar el servidor REST deberían mostrarse las líneas de información sobre el
servidor, generadas por CXF (Jetty):
Una vez que se ejecute el servidor REST, se pueden invocar las operaciones del servicio usando cualquier
cliente HTTP (por ejemplo, un navegador web). Una sesión de trabajo típica sería:
1) Arrancar el generador de señales: http://localhost:9002/SignalGenerator/start
Cualquier servicio REST se puede consumir usando un cliente Web, esto es, un cliente que permite realizar
operaciones HTTP. CXF dispone de una clase denominada WebClient, que permite al desarrollador no
tener que ocuparse de los detalles de implementación del protocolo HTTP y le facilita el desarrollo de
clientes REST. La clase WebClient está definida en el paquete org.apache.cxf.jaxrs.client. y se
pueden ver un ejemplo de uso en el siguiente enlace: http://cxf.apache.org/docs/jax-rs-client-api.html
(sección CXF WebClient API).
Para obtener los valores de los resultados complejos (se entienden por complejos, las clases Java definidas
por el desarrollador distintas a los tipos primitivos de java) de las invocaciones a los métodos REST, es
necesario usar la clase Response (ubicada en el paquete javax.ws.rs.core.Response). Esta clase es
la responsable de “representar” el resultado obtenido de una invocación REST como una entidad XML (que
es lo que envía el método de servicio, al ser anotado para producir XML). Una vez que se dispone de esa
entidad XML, se puede convertir a un objeto de la clase correspondiente haciendo una conversión directa
(cast). Para usar ambas clases ( WebClient y Response) es necesario primero crear el objeto de la clase
WebClient:
Una vez hecho esto (en el constructor de la clase cliente o en la propia definición de los atributos de dicha
clase), se deben implementar los diferentes métodos que se invocarán desde la interface GUI
proprocionada. A modo de ejemplo, se muestra el código asociado a una de esas funciones y como se
realiza la invocación REST y la obtención del dato de la invocación con el objeto OperationInfo
(proporcionado en el curso virtual).
Como se puede apreciar en el código anterior, la clase Response se emplea para obtener la respuesta del
servicio REST y posteriormente, obtener un objeto Java que representa a la llamada ( OperationInfo en
este caso).
La estructura del código para el resto de métodos del cliente son similares, y se debe modificar lo necesario
adaptándolo a la estructura del método REST del servidor (la clase esperada y el @PATH del método,
básicamente).
Para probar el funcionamiento del servidor REST, de nuevo, se debe emplear la clase auxiliar ClientGUI
incorporándola en el código del cliente REST (mediante las clases que sean necesarias). Recuérdese que,
para emplear esta función, es necesario que la clase cliente (o una auxiliar) implemente la interface
ClientPlot. El procedimiento es similar al desarrollado en apartados anteriores.
3.- PRUEBAS
Una vez desarrolladas las clases necesarias, se debe probar la ejecución de los servidores SOAP y REST,
usando los desarrollos de los clientes SOAP y REST. A continuación se muestra el ejemplo desarrollado por
el equipo docente, que se encuentra disponible en el entorno virtual.
En primer lugar se debe ejecutar el servidor, tal y como se muestra en la siguiente figura.
Si todo es correcto, debería mostrar un mensaje indicando que está disponible para gestionar conexiones de
clientes (tal y como aparece en la figura anterior). En este punto, se debe comprobar que los servidores
están ofreciendo los respectivos servicios, tomando sendos pantallazos de los
1) SOAP Server. Pantallazo mostrado el fichero WSDL del servicio SOAP generado automáticamente
(http://localhost:9000/SignalGenerator?wsdl)
2) REST Server. Pantallazos de una sesión de trabajo (descritos al final del apartado 2.5).
A continuación se deben ejecutar los clientes, tal y como se muestra en la siguiente figura.
Si todo es correcto, debería mostrar el interface gráfico (GUI) desarrollado por el equipo docente (en ambos
caos) y que permite probar la funcionalidad pedida. Se puede ver el aspecto inicial en la figura 4.
Para arrancar el generador de señales (es decir, llamar al método start() del objeto remoto) se debe pulsar el
botón correspondiente. En la figura 5 se muestra una ejecución de una señal sinusoidal de frecuencia 0.05
Hz y amplitud 10.
4.- INFORME
Cómo informe del trabajo se debe entregar un fichero zip en la pestaña de Evaluación del curso virtual en
aLF. El fichero debe llamarse “Nombre_Alumno”.zip, donde Nombre_Alumno debe contener el nombre
completo del estudiante, sin usar acentos y usando guiones bajos (_) en vez de espacios en blanco entre
nombre y apellidos.
Deben estar situados en el directorio lib del fichero zip. Se deben incluir en dicho directorio, todos
los ficheros jar que se empleen en la ejecución de los servidores/clientes SOAP y REST. En el caso
de las librerías CXF, no es necesario incluirlas (debido al gran tamaño que tienen), pero en los
scripts de ejecución se deberán referenciar en el classpath con el path .\lib\apache-cxf-3.3.3\lib. De
esta forma, para que el equipo docente pueda probar la solución del alumno, solo deberá copiar
esas librerías a esa localización.
- Los ficheros fuentes de las clases generadas situados en el directorio sources, manteniendo la
estructura de paquetes (esto es, los subdirectorios asociados), pero sin incluir ficheros relativos al
IDE utilizado para el desarrollo, es decir, sólo se han de incluir las clases implementadas por el
estudiante.
- Documento en formato texto (puede ser word, txt o pdf) con la arquitectura desarrollada y
comentarios sobre los problemas encontrados durante el desarrollo y prueba de los
servidores/clientes SOAP/REST. Además se deben incluir los pantallazos/comentarios pedidos en la
parte de pruebas. Este documento debe estar situado en el directorio doc del fichero zip.
- Guiones de trabajo (scripts) para poder ejecutar las aplicaciones cliente y servidor, tanto de SOAP
como REST (usando los ficheros jar del directorio lib). Estos scripts deben estar situados en la raíz
del fichero comprimido y deben considerar los paths relativos al subdirectorio lib al ejecutar dichos
scripts (tenga en cuenta además, el path relativo a las librerías CXF y adicionales).