3.3 Caso de estudio: diseño e



Documentos relacionados
3.3 Caso de estudio: diseño e implementación de un servicio/cliente REST

envía al browser. El browser despliega el archivo.

Requisitos. Universidad ORT Arquitectura de Software

Servlets. Unidad: 4 Laboratorio de Programación. Universidad Nacional de la Patagonia Austral Unidad Académica Río Gallegos

Tema 4: Tecnologías Web Java

USANDO SERVLETS EN UN SERVIDOR WEB RESIN

Curso de Java POO: Programación orientada a objetos

Modelo de Objetos Distribuidos

JAVA EE 5. Arquitectura, conceptos y ejemplos.

Práctica sobre compartición de instancias remotas.

Desarrollo de Servicios Web con JBuilder

Un servlet es una clase java que implementa la Servlet interface. Un servlet corre dentro de un contexto denominado Servlet engine.

Sistemas de Información 12/13 Servlets y JSPs (Java Server Pages)

3.9 Patrón Distributed callback

Web Tier en JAVA. Nicolás Troncoso Carrère. Valparaíso, ILI 258 Departamento de Informática Universidad Técnica Federico Santa María

Ejemplos de Servlet y JSP Web Application Development

Figura 7-1 Enlace para instalar el servidor web Apache Jakarta Tomcat

NIVEL 16: ESTRUCTURAS N-ARIAS RECURSIVAS Aplicaciones Web, Html y Servlets. ISIS1206 Estructuras de Datos

J2EE Java 2 Enterprise Edition

Capitulo 5. Implementación del sistema MDM

Estructuras de Sistemas Operativos

Práctica 1: Instalación de un servidor de aplicaciones web y diseño de la vista de una aplicación

GUÍA TÉCNICA. Desarrollo de Proyectos en Plataforma Liferay en el Gobierno de Extremadura

COPIAS DE SEGURIDAD AUTOMÁTICAS DE DIRECCIONES CALLEÇPAÑA

Uso de excepciones en Java

App para realizar consultas al Sistema de Información Estadística de Castilla y León

[CASI v.0109] Pág. 1

Integración Capa Web de pojo-miniportal (1)

GLOSARIO. Arquitectura: Funcionamiento, estructura y diseño de una plataforma de desarrollo.

Oficina Online. Manual del administrador

Servidores Web (II) Contenidos

EXAMEN FINAL Metodología y Programación Orientada a Objetos. Curso Cuatrimestre de otoño. 17 de Enero de 2011

5.1 Introducción a Servicios Web

ATLAS MANUAL DE USUARIO SERVICIO DE TRAZAS

Sesión 17. Servicios web RESTful

Dossier de prácticas

Configuración servidor Tomcat

ATLAS MANUAL DE INTEGRACIÓN Cliente del Servicio de SMS

APLICACIONES MÓVILES NATIVAS. Sesión 8: Arquitectura de las aplicaciones Android

Introducción a Java LSUB. 15 de enero de 2015 GSYC

19. Packages o paquetes

Introducción a las Redes de Computadoras. Obligatorio

Patrones de Alto nivel: Patrones de Arquitectura Patrones de nivel medio: Patrones de Diseño Patrones de bajo nivel: Idioms

Framework 1. Web en Java. (solo aplicaciones en mantenimiento)

Oracle 12c DISEÑO Y PROGRAMACIÓN

SERVIDOR WEB PARA ACCESO EN TIEMPO REAL A INFORMACIÓN METEOROLÓGICA DISTRIBUIDA

Llamada a métodos remotos (RMI). Curso 04/05. Tema 9. Departament d Informàtica. Universitat de València. 1. Introducción 2

EXCEPCIONES EN JAVA. Las sentencias que tratan las excepciones son try y catch. La sintaxis es:

Mi propuesta consiste en crear un portal Web que contemple las siguientes funcionalidades:

Manual del Protocolo XML-RPC de Mensajería Negocios

CREAR UN SERVICIO WEB BASICO CON JAVA AXIS2. Víctor J. Sosa

SIEWEB. La intranet corporativa de SIE

- MANUAL TÉCNICO - Software de diagnóstico de la seguridad de la información y autoimplantación de LOPD. Rev. 01- FEBRERO 2013

GUÍA TÉCNICA. Desarrollo de Sistemas de Información la plataforma Business Intellingence Pentaho

2.2 Parsing de documentos XML

SOLUCION PARCIAL TASK SCHEDULER. Task Scheduler

Capítulo 3 Diseño del Sistema de Administración de Información de Bajo Costo para un Negocio Franquiciable

Instalar y configurar W3 Total Cache

Tutorial: Primeros Pasos con Subversion

Arquitectura de sistema de alta disponibilidad

Manual de Integrador.NET

Práctica 2: Instalación de un gestor de bases de datos relacionales y desarrollo de una aplicación Web con persistencia de datos

INTRODUCCIÓN N A LAS APLICACIONES WEB Y TECNOLOGÍA A JAVA

Ingeniería del Software Arquitectura Física en 3 niveles

Elementos requeridos para crearlos (ejemplo: el compilador)

2.2.- Paradigmas de la POO

Introducción a la programación orientada a objetos

HTTP, CGI, Applets y Servlets

Capas de la arquitectura de referencia

Tema 1. Introducción a JAVA

Sistemas de Caché. Para mejorar la velocidad de carga de una web. papers. acens

INSTALACIÓ N A3ERP. Informática para empresas INTRODUCCIÓN CONSIDERACIONES GENERALES DE LA INSTALACIÓN PAQUETES DE INSTALACIÓN PREDEFINIDOS

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

Programación en Java. Programación en OO

Introducción a la Firma Electrónica en MIDAS

Java Inicial (20 horas)

Documentación Técnica Conector

1. Creación del repositorio

ORBERE. Memoria Técnica del Aplicativo de Gestión de la producción para ADIMDE

Java RMI. Sistemas Distribuidos Rodrigo Santamaría

Unidad II. - Las técnicas en las que se basó, las categorías de análisis o ejes centrales que permiten guiar el proceso de investigación.

Ejercicios - Persistencia en Android: ficheros y SQLite

ATLAS MANUAL DE USUARIO ARBOL ACCESIBLE

INSTALACIÓN A3ERP INTRODUCCIÓN CONSIDERACIONES GENERALES DE LA INSTALACIÓN PAQUETES DE INSTALACIÓN PREDEFINIDOS

Manual de uso de la plataforma para monitores. CENTRO DE APOYO TECNOLÓGICO A EMPRENDEDORES -bilib

1

Java Servlets. Luis Fernando Llana Díaz. 17 de abril de Departamento de Sistemas Informáticos y ProgramaciónUniversidad Complutense de Madrid

UNIVERSIDAD DE OVIEDO

En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

Curso Java Web (JSP's/Servlets)

Manual de referencia para la invocación de WebServices con Aduanas (SMS v3.0)

Tema 6: Comparativa CORBA/Servicios Web

Introducción a JAX-WS Web Services

SERVLETS. Aplicaciones Distribuidas

Gestión Documental PREPARACION DEL ENTORNO DE DESARROLLO

Propuesta de Portal de la Red de Laboratorios Virtuales y Remotos de CEA

Introducción - por qué usarlas?(1)

INTRODUCCION. Tema: Protocolo de la Capa de aplicación. FTP HTTP. Autor: Julio Cesar Morejon Rios

La interoperabilidad se consigue mediante la adopción de estándares abiertos. Las organizaciones OASIS y W3C son los comités responsables de la

Primera Escuela de la Red Temática SVO. Madrid, Noviembre, 2006 JAVA BÁSICO. Raúl Gutiérrez Sánchez LAEFF - INTA raul@laeff.inta.

Transcripción:

3.3 Caso de estudio: diseño e implementación de un servicio/cliente REST

Índice Descripción del caso de estudio Diseño por capas Protocolo REST Implementación REST de la capa Acceso al Servicio Implementación REST de la capa Implementación del Servicio Comentarios finales

Descripción del caso de estudio (1) Retomando el ejemplo ilustrado en el tema 2 BD Portal 3 1: Petición a http://ws.udc.es/movies/releases ws.udc.es BD Internet Apl. cliente 2: Respuesta en formato estructurado Servicio Movies

Descripción del caso de estudio (2) El caso de estudio está inspirado en el ejemplo anterior Cliente MoviesSearchClient Imprime la información de las películas que se estrenan en una fecha Ejemplo: MoviesSearchClient.{sh, bat} 19 10 2001 Cliente MoviesAdminClient Permite añadir/actualizar/eliminar información sobre películas Ejemplos: MoviesAdminClient.{sh, bat} -a Movie-1.xml Movie-2.xml... MoviesAdminClient.{sh, bat} -u Movie-1.xml Movie-2.xml... MoviesAdminClient.{sh, bat} r 1 2... NOTA: El uso de XML para pasar la información de las películas con las opciones -a y -u es sólo por comodidad (como alternativa a pasar la información en múltiples parámetros)

Descripción del caso de estudio (3) Servicio Ofrece cuatro operaciones Obtener la información de las películas que se estrenan en una determinada fecha Añadir la información de una película Actualizar la información de una película Eliminar la información de una película a partir de su identificador Por sencillez, los dos clientes tienen una interfaz de línea de comandos y el servicio guarda los datos de cada película la en un fichero.properties REST y SOAP El ejemplo proporciona dos implementaciones del servicio: una REST y otra SOAP Los clientes pueden configurarse (sin necesidad de recompilación) para usar una u otra

Descripción del caso de estudio (y 4) NOTA Ambos clientes pueden ejecutarse desde la intranet del servicio o desde Internet Sin embargo, en un caso real, posiblemente MoviesSearchClient representa una versión simplificada de un cliente (aplicación de escritorio o aplicación Web) que accede desde Internet MoviesAdminClient sería de utilidad a los empleados de la empresa que ofrece el servicio de las películas, y posiblemente estaría implementado como una aplicación Web (instalada en la propia empresa, y seguramente sólo accesible desde ella) que accede directamente a la base de datos en la que se guardaría la información de las películas

Índice Descripción del caso de estudio Diseño por capas Protocolo REST Implementación REST de la capa Acceso al Servicio Implementación REST de la capa Implementación del Servicio Comentarios finales

Diseño por capas (1) El diseño por capas es una de las técnica de diseño más usadas en Ciencias de la Computación (e.g. arquitecturas de protocolos de Redes). Una capa inferior proporciona un servicio a otra capa superior. El servicio ofrecido se define mediante un contrato de servicio io (similar a interfaz JAVA) Permite independizar el software de ambas capas: Al la capa superior no le importa cómo se implementa el servicio Cambios en la manera de implementar una capa no afectan a la otra. Múltiples usos en integración de aplicaciones.

Diseño por capas (1) En el caso de estudio usaremos el diseño por capas para ocultar las tecnologías de acceso e implementación de servicios Interfaz de Usuario Implementación del Servicio Acceso al Servicio Lógica de Negocio Cliente Servidor

Diseño por capas (2) Capa Interfaz de Usuario Implementa la interfaz de usuario No depende de la tecnología de acceso al servicio Capa Acceso al Servicio Implementa el acceso al servicio Ofrece una API sencilla que oculta la tecnología usada para acceder al servicio Capa Implementación del Servicio Utiliza una tecnología para implementar el servicio y delega en la capa Lógica de Negocio Capa Lógica de Negocio Implementa la lógica que expone el servicio No depende de la tecnología de implementación del servicio Ofrece una API sencilla a la capa de Implementación del Servicio

Ventajas Diseño por capas (3) La persona (o el grupo de personas) que se encarga del desarrollo de una capa no necesita conocer las tecnologías usadas en otras capas Cada capa puede ser desarrollada en paralelo con el resto de capas Se facilita el mantenimiento del software Cambios en la implementación de una capa (e.g. uso de nuevas tecnologías) no conllevan cambios en el resto de capas Riesgos El software es más complejo Si el software a construir es muy sencillo (e.g. un prototipo), puede no valer la pena. Si realmente ambas capas no cambian independientemente, tampoco compensa.

Comentarios adicionales Capa Lógica de Negocio Diseño por capas (4) Es muy frecuente que esta capa ya exista previamente a la creación del servicio (el servicio surge como necesidad de exponertodaopartedelacapalógicadenegocioaotras o parte de de a otras aplicaciones) El diseño por capas permite habilitar como servicio una aplicación ya existente ya que la capa de implementación del servicio no modifica la implementación de la capa Lógica de Negocio (no sería deseable y quizás tampoco posible) Cliente o servidor? Aunque el gráfico anterior ilustra un cliente puro y un servidor puro, en un sistema distribuido, una aplicación puede actuar tanto de cliente como de servidor (e.g. un servicio que como parte de su implementación necesita hacer uso de otros servicios)

Diseño por capas (5) El diseño por capas es útil siempre que queremos aislar una parte del software de cambios en otra parte del mismo. Si hay cambios en una capa, puede haber que hacer cambios en la parte de traducción entre capas, pero no será necesario hacerlo en la otra capa. Permite que ambos lados de la capa evolucionen por separado de acuerdo a sus necesidades y a su propio ritmo. Por ejemplo, si ahora el servidor devuelve un nuevo campo o cambia el nombre de uno existente, el cliente no tiene porque verse afectado por ello. Las capas introducen costes extra de tiempo de desarrollo y eficiencia, por lo que deben utilizarse sólo cuando son necesarias.

Diseño por capas (y 6) El diseño por capas también sirve para implementar la idea de plugin. Algunos paquetes software permiten que desarrolladores externos extiendan su funcionalidad programando componentes plugin. Ejemplos: Toolbars y botoneras en navegadores. Adaptadores para distintas bases de datos en paquetes empresariales. Funciones definidas por el usuario en bases de datos. Apps en facebook El paquete software original no puede modificarse o no es práctico hacerlo. Pero si el software original usa diseño por capas, el programador externo puede añadir / sustituir i su propia implementación de una capa.

Estructura de paquetes del caso de estudio Subsistema Util Subsistema Movies es.udc.ws.movies es.udc.ws.util configuration exceptions Otros... client model service rest jaxws xml proxy servlets xml Subpaquetes...

Visión de la estructura de paquetes en capas Interfaz de Usuario es.udc.ws.movies.client Implementación del Servicio es.udc.ws.movies.rest.servlets es.udc.ws.movies.rest.xml Acceso al Servicio es.udc.ws.movies.service i es.udc.ws.movies.service.rest.proxy es.udc.ws.movies.service.rest.xml Lógica de Negocio es.udc.ws.movies.model Cliente Servidor NOTAS Subsistema Util: todas las capas lo usan es.udc.ws.movies.xml: usado por todos los paquetes, excepto la capa Lógica de Negocio

es.udc.ws.util.exceptions [Subsistema Util] (1) Exception RuntimeException ParsingException ServiceException InstanceException DuplicateInstanceException InstanceNotFoundException

es.udc.ws.util.exceptions [Subsistema Util] (y 2) Tanto en este paquete como en otros, se ha seguido la siguiente estrategia de definición de excepciones Excepciones graves Representan errores de infraestructura Ejemplo: un documento XML no está bien formado o no es válido (ParsingException), un error en el acceso al servicio (ServiceException), etc. Se han modelado como excepciones de runtime Sólo es preciso capturarlas en los lugares en los que explícitamente se quieren tratar En resto de sitios, fluyen hacia arriba Permiten especificar un mensaje y/o encapsular una excepción (la que produjo el problema) Útil para depuración Excepciones que indican un error lógico Representan errores originados por el usuario Ejemplo: se intenta eliminar la información de una película especificando un identificador que no existe (InstanceNotFoundException), etc. Se han modelado como excepciones de tipo checked (derivan directamente de Exception)

es.udc.ws.util.configuration [Subsistema Util] (1) RuntimeException MissingConfigurationParameterException UnavailableConfigurationParametersException ConfigurationParametersManager - parameters : Map<String, String> + getparameter(name : String) : String ConfigurationParametersManager Permite leer parámetros de configuración de un fichero.properties Se pueden parsear fácilmente con java.util.properties El directorio en el que está el fichero tiene que formar parte del classpath Cachea los parámetros en memoria

es.udc.ws.util.configuration [Subsistema Util] (y 2) Ejemplo de fichero de configuración # --------------------------------------------------------------------- # MovieInformationServiceRESTProxy (Movies) # --------------------------------------------------------------------- MovieInformationServiceRESTProxy/endpointAddress=\ http://localhost:8080/ws-movies-service/rest/movieinformationprovider NOTA: para evitar conflictos de nombres, se ha usado la convención clase/parámetro para nombrar los parámetros de configuración Ejemplo de uso String endpointaddress = ConfigurationParametersManager.getParameter( "MovieInformationServiceRESTProxy/endpointAddress");

es.udc.ws.util.configuration.configurationparametersmanager (1) public final class ConfigurationParametersManager { private static final String CONFIGURATION_FILE FILE = "ConfigurationParameters.properties"; private static Map<String, String> parameters; private ConfigurationParametersManager() {} public static String getparameter(string name) throws MissingConfigurationParameterException, UnavailableConfigurationParametersException { String value; try { value = getparameters().get(name); } catch (IOException e) { throw new UnavailableConfigurationParametersException(e); } } if (value == null) { throw new MissingConfigurationParameterException(name); } else { return value; }

es.udc.ws.util.configuration.configurationparametersmanager (y 2) private static Map<String, String> getparameters() throws IOException { if (parameters == null) { /* Read property file.*/ Class configurationparametersmanagerclass = ConfigurationParametersManager.class; ClassLoader classloader = configurationparametersmanagerclass.getclassloader(); InputStream inputstream = classloader.getresourceasstream(configuration _ FILE); Properties properties = new Properties(); properties.load(inputstream); inputstream.close(); } /* * We use a "HashMap" instead of a "Properties" because * HashMap's methods are *not* synchronized (then, they are * faster) and the parameters are read-only. */ parameters = new HashMap(properties); } } return parameters;

es.udc.ws.movies.service [Capa Acceso al Servicio] (1) es.udc.ws.util.configuration ConfigurationParametersManager MovieInformationServiceFactory + getmovieinformationservice() : MovieInformationService <<interface>> MovieInformationService + findmoviesbyreleasedate(releasedate : Calendar) : List<MovieInformationTO> + addmovie(movieinformation : MovieInformationTO) : Long + updatemovie(movieinformation : MovieInformationTO) : void + removemovie(identifier : Long) : void es.udc.ws.movies.model MovieInformationTO

es.udc.ws.movies.service [Capa Acceso al Servicio] (2) Define un sencillo API para acceder al servicio Oculta la tecnología usada para acceder al servicio MovieInformationService Patrón Facade Interfaz con un método por cada una de las operaciones del servicio MovieInformationServiceFactory Patrón Factory Permite construir una instancia de MovieInformationService sin que el llamador necesite conocer la clase que implementa el interfaz getmovieinformationservice Lee de un fichero de configuración el nombre de la clase que implementa el interfaz Construye una instancia de esa clase

es.udc.ws.movies.service [Capa Acceso al Servicio] (y 3) MovieInformationService + MovieInformationServiceFactory Además de las anteriores ventajas (sencillez + ocultación de tecnología), permiten que el cliente pueda acceder a distintas implementaciones del servicio sin necesidad de recompilarlo Basta especificar en la configuración la implementación de MovieInformationService que se desea El caso de estudio proporciona dos implementaciones de MovieInformationService: una para acceder a un servicio REST y otra para acceder a uno SOAP En ambos casos, los clientes son los mismos (no se han implementado dos veces) Ejemplo de uso Calendar releasedate =... MovieInformationService service = MovieInformationServiceFactory.getInstance(); List<MovieInformationTO> movies = service.findmoviesbyreleasedate(releasedate);

es.udc.ws.movies.service.movieinformationservicefactory (1) public class MovieInformationServiceFactory { private final static String PROXY_CLASS_NAME_PARAMETER = "MovieInformationServiceFactory/proxyClassName"; private static Class proxyclass; private MovieInformationServiceFactory() {} public final static MovieInformationService getmovieinformationservice() throws ServiceException { } try { return (MovieInformationService) getproxyclass().newinstance(); } catch (Exception e) { throw new ServiceException(e); }

es.udc.ws.movies.service.movieinformationservicefactory (y 2) private static Class getproxyclass() throws ClassNotFoundException { if (proxyclass == null) { String proxyclassname = ConfigurationParametersManager.getParameter( g g PROXY_CLASS_NAME_PARAMETER); proxyclass = Class.forName(proxyClassName); } return proxyclass; } } java.lang.class forname: carga una instancia de una clase (bytecodes) en memoria y devuelve una instancia de la clase Class que representa la clase cargada newinstance: permite crear una instancia de la clase

es.udc.ws.movies.client [Capa Interfaz de Usuario] (1) es.udc.ws.movies.xml MovieXMLConversor es.udc.ws.movies.service AdminClient MovieInformationServiceFactory <<interface>> MovieInformationService es.udc.ws.movies.model SearchClient MovieInformationTO

es.udc.ws.movies.client [Capa Interfaz de Usuario] (y 2) AdminClient utiliza MovieXMLConversor (apartado 2.2) 2) porque la información de las películas con las opciones -a y -u está en XML Utiliza MovieXMLConversor.toMovieInformation para crear una instancia de MovieInformationTO a partir de un documento XML Luego invoca a addmovie o updatemovie (MovieInformationService) con esa instancia de MovieInformationTO

es.udc.ws.movies.model [Capa Lógica de Negocio] (1) - instance : MovieInformationFacade MovieInformationFacade + getinstance() : MovieInformationFacade + findmoviesbyreleasedate(releasedate : Calendar) : List<MovieInformationTO> + addmovie(movieinformation : MovieInformationTO) : Long + updatemovie(movieinformation : MovieInformationTO) : void + removemovie(identifier : Long) : void MovieInformationTO - identifier : Long - title : String - runtime : short - releasedate : Calendar - directornames : List<String> - actornames : List<String> - genres : List<Genre> - synopsis : String El tipo Genre está declarado dentro de MovieInformationTO: public enum Genre { COM, DRA, HOR, ROM, SFI, THR}; + Constructor + Métodos get/set

es.udc.ws.movies.model [Capa Lógica de Negocio] (2) Versión simplificada de la capa Lógica de Negocio Guarda la información de las películas en un directorio Utiliza un fichero.properties para guardar la información de cada película En un caso real se utilizaría una BD MovieInformationFacade Patrón Facade En este caso, además, es un Singleton Proporciona una operación por cada caso de uso Un diseño más elaborado podría utilizar Un interfaz con las operaciones públicas de la actual clase MovieInformationFacade Una factoría para poder crear instancias del interfaz Una clase de implementación del interfaz (la actual clase MovieInformationFacade) i ) addmovieinformation El identificador en el MovieInformationTO recibido es null Genera un identificador, d añade la información ió con ese identificador d y lo devuelve

es.udc.ws.movies.model [Capa Lógica de Negocio] (y 3) MovieInformationTO Patrón Transfer Object (TO) Representa un conjunto de datos En este caso, modela la información de una película

es.udc.ws.movies.xml (1) MovieXMLConversor + tomovieinformation(in : InputStream) : MovieInformationTO + tomovieinformationlist(movieelements : List<Element>) : List<MovieInformationTO> + toxml(movieinformation : MovieInformationTO, out : OutputStream) : void + toxml(movieinformationlist : List<MovieInformationTO>): List<Element> es.udc.ws.movies.model MovieInformationTO

es.udc.ws.movies.xml (y 2) Se usa en todas las capas, excepto en la capa Lógica de Negocio MovieXMLConversor Realiza conversiones de MovieInformationTO a/desde XML Los métodos tomovieinformation y el primer método toxml se estudiaron en el apartado 2.2 tomovieinformationlist Recibe una lista de elementos JDOM, donde cada elemento (tag movie) representa la información de una película Lo utiliza la implementación REST de la capa de Acceso al Servicio (operación findmoviesbyreleasedate) Segundo método toxml Operación inversa a tomovieinformationlist Lo utiliza la capa de Implementación del Servicio (para devolver los datos de las películas que se estrenan en una determinada fecha)

Índice Descripción del caso de estudio Diseño por capas Protocolo REST Implementación REST de la capa Acceso al Servicio Implementación REST de la capa Implementación del Servicio Comentarios finales

Protocolo REST (1) El protocolo REST no es Restful: Es el utilizado más habitualmente en la actualidad, aunque esto empieza a cambiar. Protocolo inspirado en las APIs REST de upcoming.org, Google Calendar y el servicio de geolocalización de Google Maps En la sección 3.6, veremos la alternativa RESTful. Existe una URL por cada una de las operaciones que ofrece el servicio ii Las operaciones de lectura se invocan mediante GET y las de modificación mediante POST Tras invocar una URL del servicio, el cliente puede obtener Código de estado 200: la respuesta contiene un documento XML con la respuesta del servicio Otro código de estado: ha ocurrido algún tipo de error grave La URL no existe (404) Un problema grave (e.g. problemas de acceso al repositorio de información) durante la ejecución del servicio (500) Etc.

Protocolo REST (2) Cuando el código de estado es 200, las respuestas son del estilo <?xml version="1.0" encoding="utf-8"?> <response xmlns="http://ws.udc.es/movies/xml" contenttype="...">... Otros elementos... </response> Valores del atributo t contenttype DATA: la respuesta indica que la operación se ha ejecutado correctamente, y si la operación devuelve datos, éstos vienen dentro del tag response EXCEPTION: la respuesta indica que la operación no se ha podido realizar debido a un error de programación o del usuario (e.g. parámetros incorrectos, información incorrecta, la película cuya información se está intentado actualizar no existe, etc.)

Protocolo REST (3) Obtener la información de las películas que se estrenan en una determinada fecha Petición GET a http://xxx/ws-movies-service/rest/movieinformationprovider/ findmoviesbyreleasedate?day=19&month=10&year=2001 Cliente Servicio <?xml version="1.0" encoding="utf-8"?> <response xmlns="http://ws.udc.es/movies/xml" contenttype="data"> <movie> <identifier>3</identifier> <title>la Maldición ió del Escorpión de Jade</title>... </movie> <movie> </movie> </response> <identifier>4</identifier> > <title>amelie</title>...

Protocolo REST (4) Añadir la información de una película Petición POST a http://xxx/ws-movies-service/rest/movieinformationprovider/addmovie <?xml version="1.0" encoding="utf-8"?> <movie xmlns="http://ws.udc.es/movies/xml"> <title>amelie</title> <runtime>140</runtime>... </movie> Cliente Servicio <?xml version="1.0" encoding="utf-8"?> <response xmlns="http://ws.udc.es/movies/xml" contenttype="data"> <identifier>4</identifier> </response>

Protocolo REST (5) Actualizar la información de una película Petición POST a http://xxx/ws-movies-service/rest/movieinformationprovider/updatemovie <?xml version="1.0" encoding="utf-8"?> <movie xmlns="http://ws.udc.es/movies/xml"> <identifier>4</identifier> <title>amelie</title> <runtime>120</runtime> >... </movie> Cliente Servicio <?xml version="1.0" encoding="utf-8"?> <response xmlns="http://ws.udc.es/movies/xml" contenttype="data"/>

Protocolo REST (6) Eliminar la información de una película Petición POST a http://xxx/ws-movies-service/rest/movieinformationprovider/removemovie (parámetro HTTP identifier identificador de la película a eliminar) Cliente Servicio <?xml version="1.0" encoding="utf-8"?> <response xmlns="http://ws.udc.es/movies/xml" / / l" contenttype="data"/> tt

Protocolo REST (7) Respuestas con contenttype="exception" son del estilo <?xml version="1.0" encoding="utf-8"?> <response xmlns="http://ws.udc.es/movies/xml" / / contenttype="exception"> <exception code="xxx" message="yyy" /> </response> Elemento exception Atributo code (entero) 1: parámetros incorrectos 2: formato de XML incorrecto (XML mal formado) 3: información de la película incorrecta (XML no válido) 4: instancia no encontrada (útil para las operaciones de actualización y eliminación) Atributo message: mensaje textual (útil para depuración)

Protocolo REST (8) Ejemplo de respuesta con contenttype="exception" Petición GET a http://xxx/ws-movies-service/rest/movieinformationprovider/ p// / / / / findmoviesbyreleasedate?day=19&month=10 Cliente Servicio <response xmlns="http://ws.udc.es/movies/xml" contenttype="exception"> <exception code="1" message="incorrect/missing 'day', 'month', or 'year' parameter" /> </response>

Protocolo REST (y 9) Añadir/actualizar la información de una película Se ha decido pasar los datos de la película en XML Esta es la solución que emplea, por ejemplo, el API REST de Google Calendar Otra alternativa consistiría en usar parámetros HTTP para pasar la información de una película Ejemplo Parámetros univaluados: identifier, title, runtime, releasedate y sypnosis Parámetros multivaluados: director, actor y genre Esta es la solución que emplea, por ejemplo, el API REST de upcoming.org

Índice Descripción del caso de estudio Diseño por capas Protocolo REST Implementación REST de la capa Acceso al Servicio Implementación REST de la capa Implementación del Servicio Comentarios finales

es.udc.ws.movies.service.rest.xml [Capas de Acceso e Implementación del Servicio] (1) ServiceResponseXMLConversor + toserviceresponse(in : InputStream) : ServiceResponse + toxml(serviceresponse : ServiceResponse, out : OutputStream) tst : void ServiceResponse - contenttype : ContentType - dataelements: List<Element> - exception: ExceptionInServiceResponse + ServiceResponse() + ServiceResponse(dataElement : Element) + ServiceResponse(dataElements : List<Element>) + ServiceResponse(exception p : ExceptionInServiceResponse) p + Métodos get El tipo contenttype está declarado dentro de ServiceResponse: public enum ContentType { DATA, EXCEPTION}; org.jdom 0..n Element ExceptionInServiceResponse - code : int - message: String + Constructor + Métodos get

es.udc.ws.movies.service.rest.xml [Capas de Acceso e Implementación del Servicio] (y 2) ServiceResponse Modela las respuestas del servicio Puede contener Datos (contenttype == DATA) Por comodidad existen tres constructores ServiceResponse(): la respuesta no incluye información ServiceResponse(Element): la respuesta incluye un elemento (e.g. el identificador de una película añadida) ServiceResponse(List<Element>): la respuesta incluye una lista de elementos (e.g. la lista de películas a estrenar) Una indicación de que la petición no se ha podido realizar (contenttype t tt == EXCEPTION) Cuarto constructor ServiceResponseXMLConversor Realiza conversiones de ServiceResponse a/desde XML La capas de Acceso al Servicio e Implementación del Servicio también se apoyan en MovieXMLConversor

es.udc.ws.movies.service.rest.proxy [Capa de Acceso al Servicio] es.udc.ws.movies.service <<interface>> MovieInformationService MovieInformationRESTProxy es.udc.ws.movies.service.rest.xml es.udc.ws.movies.service.xml ServiceResponseXMLConversor MovieXMLConversor MovieInformationRESTProxy Patrón Proxy (en conjunción con el interfaz que implementa) Representa un sustituto (proxy) local del servicio remoto Utiliza Jakarta Commons HttpClient para realizar las peticiones HTTP Framework Open Source de Apache (http://jakarta.apache.org/commons/httpclient) Recientemente HttpClient ha pasado a formar parte de Apache HttpComponents (http://hc.apache.org)

Visión global de HttpClient [org.apache.commons.httpclient] (1) HttpClient t + HttpClient() + executemethod(method : HttpMethod) : int HttpStatus <<interface>> HttpMethod + getresponsebodyasstream() : InputStream + releaseconnection() : void Declara constantes para cada uno de los códigos de estado: SC_OK (200), SC_ NOT _ FOUND (404), SC_INTERNAL_SERVER_ERROR (500), etc.

Visión global de HttpClient [org.apache.commons.httpclient.methods] (2) org.apache.commons.httpclient <<interface>> HttpMethod th HttpMethodBase ExpectContinueMethod GetMethod th + GetMethod(uri : String) Resto de métodos HTTP: OptionsMethod, HeadMethod, etc. EntityEnclosingMethod + setrequestentity(requestentity : RequestEntity) : void PostMethod PutMethod + PostMethod(uri : String) + addparameter(paramname: String, paramvalue : String) : void <<interface>> RequestEntity InputStreamRequestEntity + InputStreamRequestEntity( content : InputStream, contenttype : String) : void

Visión global de HttpClient (3) HttpMethod Representa una petición HTTP y su correspondiente respuesta Por cada tipo de operación HTTP (GET, POST, etc.) existe una clase XXXMethod (GetMethod, PostMethod, etc.) que implementa el interfaz HttpMethod Método InputStream getresponsebodyasstream() Permite leer la respuesta de una operación HTTP Método void releaseconnection() Libera la conexión HTTP (HttpClient puede reusarla para otras peticiones) HttpClient int executemethod(httpmethod method) Envía la petición HTTP especificada por parámetro Devuelve un código de estado con el resultado de la petición

Visión global de HttpClient (4) GetMethod Constructor GetMethod(String uri) Permite construir un objeto para realizar una petición GET a la URI especificada por parámetro Los parámetros (si los hay) forman parte de la URI Esquema típico para enviar una petición GET y leer su respuesta GetMethod method = new GetMethod("http://example.org/resource?param1=val1&param2=val2"); try { /* 1: Enviar petición. */ HttpClient client = new HttpClient(); int statuscode = client.executemethod(method); /* 2: Procesar respuesta. */ if (statuscode == HttpStatus.SC_OK) { InputStream in = method.getresponsebodyasstream(); << Tratar respuesta >> } else { << Tratar error >> } } catch (<<...>>) { << Tratar excepciones >> } finally { method.releaseconnection(); }

Visión global de HttpClient (5) GetMethod (cont) Sólo se deben usar caracteres ASCII en la URL Si en el valor de un parámetro es necesario usar un carácter especial (e.g.?, &, blanco, etc.) o un carácter no ASCII (e.g. ñ, á, etc.), se puede usar java.net.urlencoder Ejemplo String url =... url += "parameter=" + URLEncoder.encode(value, "UTF-8"); En http://wiki.apache.org/httpcomponents/frequentlyaskedapp licationdesignquestions se documentan otras opciones posibles

Visión global de HttpClient (6) PostMethod Constructor PostMethod(String uri) Permite construir un objeto para realizar una petición POST a la URI especificada por parámetro é d Permite añadir parámetros a la petición Los parámetros no forman parte de la URI sino del cuerpo de la petición Método void addparameter(string paramname, String paramvalue) Método void setrequestentity(requestentity requestentity) Permite asociar un objeto RequestEntity Un objeto RequestEntity permite enviar texto en el cuerpo de la petición HTTP

Visión global de HttpClient (7) InputStreamRequestEntity Constructor InputStreamRequestEntity(InputStream content, String contenttype) Construye un RequestEntity que lee el texto desde un InputStream y cuyo tipo de contenido viene especificado en contenttype (e.g. "text/xml; charset=utf-8" si se envía XML en UTF-8)

Visión global de HttpClient (8) Esquema típico para enviar una petición POST con texto en el cuerpo y leer su respuesta PostMethod method = new PostMethod("http://example.org/resource"); try { /* 1: Preparar petición. */ InputStream xmlinputstream =... InputStreamRequestEntity requestentity = new InputStreamRequestEntity(xmlInputStream, "text/xml; charset=utf-8"); /* 2: Enviar petición. */ HttpClient client = new HttpClient(); method.setrequestentity(requestentity); int statuscode = client.executemethod(method); /* 3: Procesar respuesta. */ if (statuscode == HttpStatus.SC_OK) { InputStream in = method.getresponsebodyasstream(); << Tratar respuesta >> } else { << Tratar error >> } } catch (<<...>>) { << Tratar excepciones >> } finally { method.releaseconnection(); }

Visión global de HttpClient (y 9) Esquema típico para enviar una petición POST con parámetros y sin texto en el cuerpo, y leer su respuesta PostMethod method = new PostMethod("http://example.org/resource"); http://example.org/resource method.addparameter("param1", "val1"); method.addparameter("param2", "val2"); try { /* 1: Enviar petición. */ HttpClient client = new HttpClient(); int statuscode = client.executemethod(method); /* 2: Procesar respuesta. */ << Tratar código de estado >> if (statuscode == HttpStatus.SC_OK) { InputStream in = method.getresponsebodyasstream(); << Tratar respuesta >> } } catch (<<...>>) { << Tratar excepciones. >> } finally { method.releaseconnection(); }

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (1) public class MovieInformationServiceRESTProxy implements MovieInformationService { private final static String ENDPOINT_ADDRESS_PARAMETER = "MovieInformationServiceRESTProxy/endpointAddress"; private static String endpointaddress; public MovieInformationServiceRESTProxy() {} public List<MovieInformationTO> findmoviesbyreleasedate( Calendar releasedate) throws ServiceException { /* Prepare request. */ int day = releasedate.get(calendar.day_of_month); int month = releasedate.get(calendar.month) Calendar.JANUARY + 1; int year = releasedate.get(calendar.year); GetMethod method = new GetMethod( getendpointaddress() + "/findmoviesbyreleasedate" + "?day=" + day + "&month=" + month + "&year=" + year);

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (2) try { /* Send request. */ HttpClient client = new HttpClient(); int statuscode = client.executemethod(method); /* Process reply. */ handlehttpstatuscode(statuscode); InputStream in = method.getresponsebodyasstream(); ServiceResponse serviceresponse = ServiceResponseXMLConversor.toServiceResponse(in); if (serviceresponse.getcontenttype() == ServiceResponse.ContentType.DATA) { return MovieXMLConversor.toMovieInformationList( serviceresponse.getdataelements()); } else { // ServiceResponse.ContentType.EXCEPTION throw getunexpectedserviceexception( serviceresponse.getexception()); }

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (3) } } catch (ServiceException e) { throw e; } catch (Exception e) { throw new ServiceException(e); } finally { method.releaseconnection(); } public Long addmovie(movieinformationto movieinformation) throws MovieInformationException, ServiceException { PostMethod method = new PostMethod(getEndpointAddress() + "/addmovie"); try { /* Prepare request. */ ByteArrayOutputStream xmloutputstream = new ByteArrayOutputStream(); MovieXMLConversor.toXML(movieInformation, xmloutputstream); ByteArrayInputStream xmlinputstream = new ByteArrayInputStream(xmlOutputStream.toByteArray()); InputStreamRequestEntity requestentity = new InputStreamRequestEntity(xmlInputStream, "text/xml; charset=utf-8");

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (4) /* Send request. */ HttpClient client = new HttpClient(); method.setrequestentity(requestentity); int statuscode = client.executemethod(method); /* Process reply. */ handlehttpstatuscode(statuscode); t d t t C d InputStream in = method.getresponsebodyasstream(); ServiceResponse serviceresponse = ServiceResponseXMLConversor.toServiceResponse(in); if (serviceresponse.getcontenttype() == ServiceResponse.ContentType.DATA) { Element identifierelement = serviceresponse.getdataelements().get(0); Long identifier = Long.valueOf(identifierElement.getTextTrim()); return identifier;

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (5) } else { // ServiceResponse.ContentType.EXCEPTION ExceptionInServiceResponse exception = serviceresponse.getexception(); } if (exception.getcode() == ExceptionCodes.INCORRECT_MOVIE_INFORMATION) { throw new MovieInformationException( exception.getmessage()); } else { throw getunexpectedserviceexception(exception); } } catch (MovieInformationException e) { throw e; } catch (ServiceException e) { throw e; } catch (Exception e) { throw new ServiceException(e); } finally { method.releaseconnection(); } }

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (6) public void removemovie(long identifier) throws InstanceNotFoundException, ServiceException { /* Prepare request.*/ PostMethod method = new PostMethod(getEndpointAddress() + "/removemovie"); method.addparameter("identifier", identifier.tostring()); try { /* send request. */ HttpClient client = new HttpClient(); int statuscode = client.executemethod(method); /* Process reply. */ handlehttpstatuscode(statuscode); InputStream in = method.getresponsebodyasstream(); ServiceResponse serviceresponse = ServiceResponseXMLConversor.toServiceResponse(in);

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (7) if (serviceresponse.getcontenttype() == ServiceResponse.ContentType.EXCEPTION) { ExceptionInServiceResponse exception = serviceresponse.getexception(); } if (exception.getcode() == ExceptionCodes.INSTANCE_NOT_FOUND) { throw new InstanceNotFoundException( identifier, MovieInformationTO.class.getName()); } else { throw getunexpectedserviceexception(exception); } } } catch (InstanceNotFoundException e) { throw e; } catch (ServiceException e) { throw e; } catch (Exception e) { throw new ServiceException(e); } finally { method.releaseconnection(); }

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (8) // updatemovie -> similar a addmovie... private static String getendpointaddress() throws MissingConfigurationParameterException, UnavailableConfigurationParametersException { } if (endpointaddress == null) { endpointaddress = ConfigurationParametersManager.getParameter( ENDPOINT_ADDRESS_PARAMETER); } return endpointaddress; private void handlehttpstatuscode(int statuscode) throws ServiceException { } if (statuscode!= HttpStatus.SC_OK) { throw new ServiceException( "HTTP error; status code = " + statuscode); }

es.udc.ws.movies.service.rest.proxy.movieinformationservicerestproxy (y 9) private ServiceException getunexpectedserviceexception ( ExceptionInServiceResponse exception) { return new ServiceException("Server exception; " + "code = " + exception.getcode() + "; message = " + exception.getmessage()); g } }

Comentarios MovieInformationServiceRESTProxy lee de la configuración la dirección base del servicio MovieInformationServiceRESTProxy/endpointAddress=\ http://localhost:8080/ws-movies-service/rest/movieinformationprovider Excepciones ServiceException (runtime) Error de comunicación o invocación incorrecta (e.g. parámetros erróneos) MovieInformationException (runtime) El XML de la información de la película no está bien formado o no es válido InstanceNotFoundException (checked) No se encuentra el objeto En el ejemplo (updatemovie y removemovie) se utiliza para señalar que la película sobre la que se intenta hacer una acción no existe

Índice Descripción del caso de estudio Diseño por capas Protocolo REST Implementación REST de la capa Acceso al Servicio Implementación REST de la capa Implementación del Servicio Comentarios finales

Aplicaciones Web Java EE (1) En Java EE, las aplicaciones Web se instalan en servidores (contenedores) de aplicaciones HTTP war Aplicaciones Web Cliente Servidor de aplicaciones El API de programación Web que ofrece el servidor de aplicaciones está estandarizada El API está formada en su mayoría por interfaces y clases abstractas Las aplicaciones Web se distribuyen en ficheros WAR Un fichero WAR (Web ARchive) es un fichero JAR con una estructura de directorios estandarizada Contiene clases objeto (.class), librerías utilizadas (.jar), ficheros de configuración ió y otras abstracciones propias de una aplicación Web

Aplicaciones Web Java EE (2) Una aplicación Web que utilice las APIs estándares puede instalarse en cualquier servidor de aplicaciones Java EE Existen muchos servidores de aplicaciones Ejemplos de servidores de código cerrado BEA WebLogic Server: http://www.bea.com IBM WebSphere ApplicationServer: http://www.ibm.com Sun Java System Application Server: http://www.sun.com Oracle OaceApplication ppcato Server: e http://www.oracle.com co Ejemplos de servidores de código abierto JBoss AS: http://www.jboss.org Apache Tomcat: http://tomcat.apache.org h Sólo proporciona las APIs de servlets y JSP NOTA: para la programación de servicios REST sólo necesitamos el lapid de servlets

Aplicaciones Web Java EE (y 3) Las APIs de programación Web Java no están pensadas para devolver HTML/XHTML, sino cualquier tipo de información sobre HTTP Una aplicación Web puede devolver sólo markup para navegadores, actuar como un servicio para aplicaciones cliente, o ambas cosas En consecuencia, se pueden utilizar para implementar servicios Web Utilizaremos el API de Servlets, en conjunción con JDOM, para implementar servicios REST Un servlet es una clase que puede recibir las peticiones asociadas a una o varias URLs y devolver las respuestas

Visión global del framework de servlets (1) javax.servlet <<interface>> Servlet + init(config : ServletConfig) : void + destroy() : void + service(request : ServletRequest, response : ServletResponse) : void GenericServlet + init(config : ServletConfig) : void + destroy() : void + service(request : ServletRequest, response : ServletResponse) : void javax.servlet.http HttpServlet + service(request : ServletRequest, response : ServletResponse) : void # service(request s : HttpServletRequest, response s : HttpServletResponse) s : void # doget(request : HttpServletRequest, response : HttpServletResponse) : void # dopost(request : HttpServletRequest, response : HttpServletResponse) : void

Visión global del framework de servlets (2) javax.servlet <<interface>> javax.servlet.servletrequest + getparameter(name : String) : String + getparametervalues(name : String) : String[] + getinputstream() : ServletInputStream <<interface>> javax.servlet.servletresponse + setcontenttype(type : String) : void + getouputstream () : ServletOutputStream javax.servlet.http <<interface>> HttpServletRequest <<interface>> HttpServletResponse tr

Visión global del framework de servlets (3) Normalmente el desarrollador implementa un servlet extendiendo de HttpServlet y redefine los métodos doxxx (e.g. doget, dopost, doput, dodelete, etc.) necesarios Ciclo de vida de un servlet Cuando el servidor de aplicaciones Web necesita cargar un servlet en memoria (e.g. al arrancar, la primera vez que se accede a él, etc.), crea una instancia de la clase de implementación y llama a la operación init (interfaz Servlet) En una máquina virtual Java, sólo existe una instancia de cada servlet para cada aplicación Web instalada Cuando el servidor de aplicaciones Web decide eliminar un servlet de memoria (e.g. lleva cierto tiempo sin usarse), llama a la operación destroy (interfaz Servlet)

Visión global del framework de servlets (4) Ciclo de vida de un servlet (cont) Cuando el servidor de aplicaciones Web recibe una petición dirigida a un servlet, invoca la operación service (interfaz Servlet) La operación pública service de HttpServlet llama a la operación protegida service Esta operación es una operación plantilla (Template Method), que llama a doget, dopost, doput, dodelete, etc., según la petición HTTP sea GET, POST, PUT, DELETE, etc.

Visión global del framework de servlets (5) Modelo multi-thread de procesamiento de peticiones Cada vez que el servidor de aplicaciones Web recibe una petición dirigida a un servlet, la petición se procesa en un thread independiente (concurrentemente con otras peticiones) Internamente los servidores de aplicaciones suelen usar un pool de threads Existe un conjunto (pool) de threads de tamaño configurable Cada petición se inserta en una cola La petición es servida por uno de los threads libres del pool Si no hay ningún thread libre, la petición permanece en la cola hasta que uno termina su trabajo

Visión global del framework de servlets (6) Modelo multi-thread de procesamiento de peticiones (cont) Dado que sólo existe una instancia de cada servlet para cada aplicación Web instalada, los métodos doxxx tienen que ser thread-safe No es necesario utilizar synchronized ( ni eficiente!!!) si la implementación de los métodos doxxx sólo hace uso de variables locales (pila o heap) o de variables globales (static) de sólo lectura (típicamente caches), que es lo normal Si los métodos doxxx modifican alguna estructura global (un atributo del servlet o alguna variable global), necesitan sincronizar su acceso Sin embargo, en general, eso es mala idea, dado que una aplicación con estas características no funcionará en un entorno cluster En un entorno cluster, el servidor de aplicaciones Web está replicado en varias máquinas para conseguir escalabilidad y tolerancia a fallos En estos casos, es mejor usar una base de datos para las estructuras globales que sean de lectura/escritura

Visión global del framework de servlets (7) ServletRequest String getparameter(string g name) Permite obtener el valor de un parámetro univaluado String[] getparametervalues(string name) Permite obtener el valor de un parámetro multivaluado También se puede usar con parámetros univaluados NOTA Un parámetro multivaluado es aquel que tiene (o puede tener) varios valores Al realizar la petición HTTP el parámetro (en la URI o en el cuerpo del mensaje, dependiendo del tipo de petición) se especifica n veces, cada una con su valor Ejemplo: http://example.com/weather?city=coruna&city=santiago pe / eat e cty a&cty tago ServletInputStream getinputstream() Permite leer el cuerpo de la petición ServletInputStream es una clase abstracta que implementa java.io.inputstream

Visión global del framework de servlets (y 8) ServletResponse void setcontenttype(string type) Especifica el tipo de contenido de la respuesta (e.g. "text/xml; charset=utf-8") ServletOutputStream getoutputstream() Permite escribir la respuesta ServletOutputStream es una clase abstracta que implementa java.io.outputstream

es.udc.ws.movies.service.rest.servlets [Capa Implementación del Servicio] (1) javax.servlet.http HttpServlet FindMoviesByReleaseDateServlet AddMovieServlet UpdateMovieServlet RemoveMovieServlet es.udc.ws.movies.xml MovieXMLConversor es.udc.ws.movies.model MovieInformationFacade ServletUtils

es.udc.ws.movies.service.rest.servlets [Capa Implementación del Servicio] (y 2) ServletUtils + writeserviceresponse(response : HttpServletResponse) : void + writeserviceresponse(dataelement i t t : Element, response : HttpServletResponse) tr : void + writeserviceresponse(dataelements : List<Element>, response : HttpServletResponse) : void + writeserviceresponse(exceptioncode : int, exceptionmessage: String, response : HttpServletResponse) : void es.udc.ws.movies.service.rest.xml ServiceResponseXMLConversor ServiceResponse Los métodos writeserviceresponse construyen un objeto ServiceResponse apropiado y escriben el XML en la respuesta usando ServiceResponseXMLConvesor

es.udc.ws.movies.service.rest.servlets.findmoviesbyreleasedate (1) public class FindMoviesByReleaseDateServlet extends HttpServlet { public void doget(httpservletrequest request, HttpServletResponse response)throws IOException { /* Get releasedate. */ Calendar releasedate; try { String day = request.getparameter("day"); String month = request.getparameter("month"); String year = request.getparameter("year"); releasedate = DateOperations.getDate(day, month, year); } catch (Exception e) { ServletUtils.writeServiceResponse( ExceptionCodes.INCORRECT_PARAMETERS, "Incorrect/missing 'day', 'month', or 'year' " + "parameter", response); return; }

es.udc.ws.movies.service.rest.servlets.findmoviesbyreleasedate (y 2) /* Find movies. */ List<MovieInformationTO> movieinformationlist = MovieInformationFacade.getInstance(). findmoviesbyreleasedate(releasedate); /* Generate response. */ ServletUtils.writeServiceResponse( MovieXMLConversor.toXML(movieInformationList), response); } }

es.udc.ws.movies.service.rest.servlets.addmovieservlet (1) public class AddMovieServlet extends HttpServlet { public void dopost(httpservletrequest request, HttpServletResponse response) throws IOException { MovieInformationTO movieinformation; /* Get movie from request. */ try { movieinformation = MovieXMLConversor.toMovieInformation( request.getinputstream()); } catch (ParsingException e) { ServletUtils.writeServiceResponse( ExceptionCodes.XML_INCORRECT_FORMAT, e.getmessage(), response); return; }

es.udc.ws.movies.service.rest.servlets.addmovieservlet (y 2) /* Add movie to repository. */ Long identifier; try { identifier = MovieInformationFacade.getInstance(). addmovie(movieinformation); } catch (MovieInformationException e) { ServletUtils.writeServiceResponse( ExceptionCodes.INCORRECT_MOVIE_INFORMATION, e.getmessage(), response); return; } /* Generate response. */ Element identifierelement = new Element("identifier", MovieXMLConversor.XML_NS); identifierelement.settext(identifier.tostring()); ServletUtils.writeServiceResponse(identifierElement, response); } }

es.udc.ws.movies.service.rest.servlets.removemovieservlet (1) public class RemoveMovieServlet extends HttpServlet { public void dopost(httpservletrequest request, HttpServletResponse response) throws IOException { /* Get movie identifier. */ Long identifier; try { identifier = Long.valueOf(request.getParameter("identifier")); } catch (Exception e) { ServletUtils.writeServiceResponse( ExceptionCodes.INCORRECT_PARAMETERS, "Incorrect/missing 'identifier' parameter", response); return; }

es.udc.ws.movies.service.rest.servlets.removemovieservlet (y 2) } /* Remove movie from repository. */ try { MovieInformationFacade.getInstance(). removemovie(identifier); } catch (InstanceNotFoundException e) { ServletUtils.writeServiceResponse( ExceptionCodes.INSTANCE_NOT_FOUND, e.getmessage(), response); return; } /* Generate response. */ ServletUtils.writeServiceResponse(response); }

es.udc.ws.movies.service.rest.servlets.servletutils (1) public class ServletUtils { private ServletUtils() {} public final static void writeserviceresponse( HttpServletResponse response) throws IOException { } writeserviceresponse(new ServiceResponse(), response); public final static void writeserviceresponse(element dataelement, HttpServletResponse response) throws IOException { } writeserviceresponse(new ServiceResponse(dataElement), response); public final static void writeserviceresponse( List<Element> dataelements, HttpServletResponse response) throws IOException { } writeserviceresponse(new ServiceResponse(dataElements), response);

es.udc.ws.movies.service.rest.servlets.servletutils (y 2) } public final static void writeserviceresponse(int exceptioncode, String exceptionmessage, HttpServletResponse response) throws IOException { } ServiceResponse serviceresponse = new ServiceResponse( new ExceptionInServiceResponse(exceptionCode, exceptionmessage)); writeserviceresponse(serviceresponse, response); private final static void writeserviceresponse( ServiceResponse serviceresponse, HttpServletResponse response) throws IOException { } OutputStream out = response.getoutputstream(); response.setcontenttype("text/xml; charset=utf-8"); ServiceResponseXMLConversor.toXML(serviceResponse, out, true); out.close();

Comentarios Los anteriores servlets no tratan IOException ni MovieInformationFacadeException java.io.ioexception (checked): representa un error de escritura en el OutputStream de la respuesta MovieInformationFacadeException i i (runtime): cada caso de uso de la fachada del modelo puede devolverla si ocurre un error grave (e.g. error de acceso al repositorio) Ambas excepciones representan errores relativos a la infraestructura usada y ajenos al usuario El servidor de aplicaciones captura IOException y las excepciones de runtime, y si se producen, devuelve una respuesta con código de estado 500 (INTERNAL SERVER ERROR), que es lo que deseamos

Empaquetamiento de una aplicación Web (1) jar cvf aplicacionweb.war directorio Opciones similares al comando Unix tar El nombre de una aplicación Web no tiene porque coincidir con el de su fichero.war El nombre se decide al instalar el fichero.war en el servidor de aplicaciones Maven genera automáticamente ficheros.war para los módulos con empaquetamiento war. Estructura de un fichero.war Directorio WEB-INF/classes Ficheros.class que conforman la aplicación Web, agrupados en directorios según su estructura en paquetes Sin ficheros fuente! Directorio WEB-INF/lib Ficheros.jar de librerías que usa la aplicación Sin ficheros fuente!

Empaquetamiento de una aplicación Web (2) Estructura de un fichero.war (cont) WEB-INF/web INF/web.xml Configuración estándar de la aplicación Web Directorio raíz y subdirectorios Vista de la aplicación (ej.: ficheros HTML, páginas JSP, imágenes, etc.) Visible a los navegadores Lo que hay debajo de WEB-INF sólo es visible a los servlets y páginas JSP de la aplicación Un fichero war se puede instalar (deployment) en Un fichero.war se puede instalar (deployment) en cualquier servidor de aplicaciones Web Java EE