1 Followers
25 Following
firecafe36

firecafe36

SPOILER ALERT!

Creación de Servicios Web SOAP

Vamos a crear nuestros propios Servicios Web, que ofrecerán una serie de métodos a los que se podrá llamar mediante RPC desde cualquier sitio de Internet mediante protocolos estándar (mensajes SOAP).


Deberemos en consecuencia ser capaces de interpretar en nuestras aplicaciones los mensajes SOAP entrantes de petición para la invocación de un método. Más tarde, invocaremos el método pedido, y con el resultado que nos devuelva vamos a deber construir un mensaje SOAP de respuesta y devolvérselo al usuario.


Si tuviésemos que introducir nosotros el código para interpretar este mensaje de entrada, y producir manualmente el mensaje de contestación, el desarrollo de Servicios Web sería una tarea altamente costosa.


Es más, si se forzara al programador a componer el mensaje SOAP manualmente cada vez que desarrolle un Servicio Web, es muy posible que cometa algún error y no respete precisamente el estándar SOAP. Esto sería un serio problema para la interoperabilidad de los Servicios Web, que es una de las características que perseguimos con esta tecnología.


Para evitar estos inconvenientes, utilizaremos librerías que nos dejen leer o generar mensajes SOAP para la invocación de métodos recónditos, como es el caso de la API JAX-WS.


Además, para facilitar aún más la labor de desarrollar Servicios Web, normalmente contaremos con herramientas que desde las clases que implementan nuestro servicio produzcan automáticamente todo el código preciso para leer el mensaje SOAP de entrada, invocar el método, redactar el mensaje SOAP de salida, y devolverlo al cliente del servicio.


Por lo tanto, deberemos centrarnos únicamente en la tarea de programar la funcionalidad que implementan nuestros servicios, olvidándonos del mecanismo de invocación de éstos.


JAX-WS es una especificación estándar de Sun Microsystems, mas no todos y cada uno de los servidores de aplicaciones emplean esta librería para administrar los Servicios Web. Por servirnos de un ejemplo, es el caso de Weblogic, que si bien está basado en JAX-WS, sostiene ciertas extensiones dueñas sobre dicha API. Nos centraremos por lo tanto en el desarrollo de servicios con Netbeans y Glassfish, que incorpora las últimas versiones de las librerías estándar.


Los servicios Web desde la vista del Servidor


Como ya hemos visto, un documento WSDL define la interoperabilidad de los servicios Web e incluye la especificación sobre requerimientos de transporte y formato de los datos a través de la red. Por lo general, un WSDL no impone ningún requerimiento sobre el modelo de programación del cliente o del servidor. La especificación de servicios Web para Java EE (JSR-109) define tres formas de incorporar la lógica de negocio de servicio Web:


  • Como un Bean de Sesión sin estado: la implementación del servicio Web (componente
    Port) se efectúa creando un Bean de sesión sin estado, que implementa los métodos del SEI (
    Service Endpoint Interface) tal y como se describe en la especificación de EJB 3.0
  • Como una clase Java: en este caso se implementa el
    Portcomo un
    ServletJAX-WS
  • Como un
    Singleton Session Bean: en un caso así se crea un
    singleton session beanque incorpora los métodos de un SEI según la especificación de EJB 3.1

Un componente
Portdefine la vista del servidor de un Servicio Web. Cada
Portproporciona un servicio en una dirección física particular definida por el atributo
addressde la definicion <port> de un WSDL. Un componente Port "sirve" una petición de operación definida en un <portType> de un WSDL. La implementación del servicio (
Service Implementation Bean) depende del contenedor del componente Port, pero generalmente es una clase Java que puede implementar los métodos definidos en el SEI (
Service Endpoint Interface). El SEI es un mapeado java del <portType> y <binding> asociado a un <port> de un WSDL. Un servicio Web es un conjunto de
Portsque difieren únicamente en su dirección física, y son mapeados en componentes Port separados, cada uno de ellos con su potencialmente único, pero probablemente compartido, Service Implementation Bean. La próxima figura muestra la vista del servidor de un Servicio Web.



El ciclo de vida del Port está absolutamente controlado por el contenedor, mas en general sigue exactamente el mismo ciclo de vida que el del propio contenedor. Un Port es creado y también inicializado por el contenedor antes de que la primera llamada recibida en la dirección del <port> del WSDL sea servida. Un Port es destruido por el contenedor cuando éste considera que sea preciso hacerlo, como por ejemplo cuando el propio contenedor es
sutting down. Una implementación de un servicio Web JAX-WS reside en un contenedor Web, y por lo tanto puede desplegarse en un servidor Web o un servidor de aplicaciones, una implementación EJB, en cambio, reside en un contenedor EJB y sólo podrá desplegarse en un servidor de aplicaciones.


Un componente Port asocia una dirección de puerto (
port addressde un WSDL) con la implementación del servicio (
Service Implementation Bean). En general, el componente Port "pospone" la definición de los requerimientos del contenedor del servicio a la fase de despliegue, indicando dichos requerimientos en el descriptor de despliegue del componente. Un contenedor proporciona un
listeneren la dirección del puerto (
port addressde un WSDL) y un mecanismo para "enviar" la petición a la implementación del servicio Web. Un contenedor también proporciona servicios en tiempo de ejecución, como limitaciones de seguridad y mapeados de referencias lógicas a referencias físicas de objetos distribuidos y recursos.


Un desarrollador declara un componente
Porten un descriptor de despliegue de servicios Web. El descriptor de despliegue incluye el documento WSDL que describe el
PortTypey
bindingdel servicio Web. Cuando se usa JAX-WS, no es preciso proporcionar dicho descriptor de despliegue. La mayor parte de la información del descriptor de despliegue se incluye en las anotaciones de la implementación del servicio. Podríamos usar el descriptor de despliegue para "sobreescribir" o bien prosperar la información proporcionada por las anotaciones de la implementación del servicio.


La plataforma Java EE seis, soporta las siguientes implementaciones de servicios Web: como un componente Web JAX-WS en un contenedor de
Servlets, y como un componente EJB de sesión
statelesso
singleton.


El embalado de un servicio Web en un módulo Java EE es específico de la metodología de implementación, mas sigue los requerimientos para un archivo EJB-JAR o archivo WAR. Contiene los ficheros de clases java del SEI y los documentos WSDL del servicio Web. Además contiene el descriptor XML de despliegue que define los
Portsdel servicio y su estructura.


El modelo de programación JAX-WS


Para desarrollar una implementación de un servicio web (
web service endpoint) podemos optar por 2 puntos de partida: una clase Java que incorpora el servicio Web o bien un archivo WSDL. Cuando empezamos por una
clase java, utilizaremos herramientas para producir los instrumentos necesarios, entre ellos el WSDL del servicio. Cuando nuestro punto de partida es un
fichero WSDL(así como los ficheros de esquema que describen los modelos de datos utilizados en el servicio), utilizaremos herramientas para producir el SEI (
Service Endpoint Interface)


Sobre JAX-WS y la implementación del servicio


JAX-WS impone la existencia de un
Service Implementation Beananotado con
javax.jws.WebServiceen un componente Port. Como ya hemos indicado en el apartado anterior, la implementación del servicio(
Service Implementation Bean) dependerá del contenedor del componente Port, mas en generales una clase Java que puede incorporar los métodos definidos en el SEI (
Service Endpoint Interface).En los apartados siguientes veremos que JAX-WS deja incorporar un
Service Implementation Beancomo una clase Java anotada (modelo de servlets), o bien como un EJB de sesión sin estado (modelo EJB).


Si empezamos por una clase java, vamos a tener la seguridad de que la clase que implementa el servicio tiene los tipos de datos java adecuados, mas el desarrollador tienen menos control sobre el esquema XML generado. Si comenzamos por el WSDL y esquemas, el desarrollador tiene un control total sobre qué esquema se está usando, pero menos control sobre el
endpointdel servicio generado y de las clases que emplea.


Nosotros vamos a explicar en primer lugar cómo crear un servicio desde una clase java, y también veremoscómo emplear Netbeans para crear un servicio a partir de un wsdl.


Cuando nuestro punto de partida es una clase java, tenemos que proseguir ciertas limitaciones en la implementación de nuestro servicio. Una implementación válida de un servicio web es una clase java que cumple las próximas restricciones:


  • La clase ha de estar anotada con javax.jws.Webservice (o de forma alternativa con javax.xml.ws.Provider, si se quiere trabajar de forma directa a nivel de mensajes XML)
  • Podemos anotar cualquiera de sus métodos con javax.jws.WebMethod
  • Todos sus métodos pueden lanzar la excepción
    java.rmi.RemoteExceptionademás de cualquier otra excepción propia del servicio
  • Los parámetros de sus métodos y tipos de retorno deben ser compatible con JAXB (JAXB impone unas reglas para mapear tipos java con tipos de ficheros de esquema XML)
  • Ningún parámetro y/o género de retorno pueden incorporar la interfaz
    java.rmi.Remoteni directa ni indirectamente

La clase java anotada con @WebService define un SEI de forma
implícitapor lo que no será necesario administrar dicha interfaz. Podemos especificar de forma explícita una interfaz añadiendo el atributo endpointInterface a la anotación @WebService. En un caso así, sí es necesario suministrar la interfaz que defina los métodos públicos libres en la clase que incorpora el servicio.


Una implementación de un servicio Web que utiliza la anotación @WebService no es necesario que especifique la ubicación del WSDL. Si se usa el atributo wsdlLocation en la anotación @WebService, el archivo WSDL debe ser empaquetado así como las clases java del servicio web.


A continuación vamos a explicar con más detalle cómo implementar el servicio utilizando el modelo de servlets (el servicio se ejecuta en un contenedor web) y ejb (el servicio se ejecuta en un contenedor EJB).


Implementación del servicio JAX-WS con el modelo de servlets


Un
Service Implementation Beanque se ejecuta en un contenedor Web debe proseguir los siguientes requerimientos (ciertos cuales ya los hemos indicado en el apartado anterior):


  • La clase que implementa el servicio debe estar anotada con javax.jws.WebService (o de forma alternativa, con javax.xml.ws.Provider)
  • Si se implementa el servicio Web partiendo de código java, la anotación javax.jws.WebService NO es necesario concretar el SEI, la clase que incorpora el servicio implícitamente define un SEI. Los métodos de negocio de la implementación han de ser
    públicos, y NO pueden ser
    finalo
    static. Solamente los métodos anotados con @WebMethod en la implementación del servicio son expuestos al usuario.
  • Si se implementa el servcicio Web desde el WSDL, el SEI generado desde el WSDL ha de estar anotado con javax.jws.WebService. La implementación del servicio debe estár también anotada con javax.jws.WebService. La implementación del servicio puede implementar el SEI, aunque no es necesario. En este caso se deben incorporar todos los métodos indicados en el SEI. Los métodos de negocio de la implementación han de ser
    públicos, y NO pueden ser
    finalo
    static. Se puede implementar métodos auxiliares que no figuren en el SEI.
  • La implementación del servicio debe tener un constructor público por defecto
  • La implementación del servicio no debe guardar el estado
  • La clase ha de ser pública, no puede ser
    finalni
    abstracta
  • La clase no debe implementar el método finalize()

Por ejemplo, podemos incorporar nuestro servicio Web como:


Con esto vamos a haber incorporado la funcionalidad del servicio como una clase Java ordinaria, sin precisar tener conocimientos de ninguna librería adicional.


De forma opcional, podemos añadir al servicio un campo context en el que se inyectará un objetoWebServiceContext que nos dará acceso al contexto del servicio:


Dado que verdaderamente el servicio es un componente web, a través de este objeto podremos tener acceso acomponentes de la API de
servletscomo la petición HTTP (HttpServletRequest), la sesión(HttpSession), etc.


Anotaciones


Podemos detallar la forma en la que se crea el servicio a través de diferentes anotaciones. Las primordiales anotaciones disponibles son:


Estilo y codificación del servicio


Hemos visto que mediante la anotación @SOAPBinding podemos mudar el estilo y la codificación del servicio. Los posibles estilos son:


  • SOAPBinding.Style.RPC: Se utilizan mensajes SOAP orientados a RPC, en los que se codifican en XML las llamadas a métodos recónditos.
  • SOAPBinding.Style.DOCUMENT: Se utilizan mensajes SOAP orientados al documento. Puesto que en estos mensajes se puede incluir cualquier tipo de documento XML, también se pueden usar para invocar operaciones de servicios.

diseño web profesional tarragona én la codificación:


  • SOAPBinding.Use.LITERAL: Esta es la única codificación admitida en el WS-I Basic Profile (BP), que da soporte a los servicios de tipo
    document/literaly
    RPC/literal.
  • SOAPBinding.Use.ENCODED: Se trata de una codificación que deja representar una mayor pluralidad de estructuras de datos que la precedente, pero está desaprobada por el BP por ser la causa de gran cantidad de incompatibilidades entre servicios. En verdad JAX-WS es incompatible con los servicios de esta clase. Esta codificación se acostumbra a emplear con servicios de tipo RPC, dando lugar al tipo
    RPC/encoded.

En el caso de los servicios de tipo
document/literal, también podemos especificar la manera en la que se representan los modelos de datos de los parámetros de las operaciones:


  • SOAPBinding.ParameterStyle.BARE: Los parámetros se pasan de manera directa.
  • SOAPBinding.ParameterStyle.WRAPPED: Los parámetros se pasan envueltos en tipos de datos complejos.

Por defecto los servicios serán del tipo
document/literal/wrapped.


Requerimientos de un servicio Web JAX-WS



Tipos de datos compatibles


Cuando trabajamos con JAX-WS, los tipos de datos que vamos a poder utilizar como género de los parámetros y de valor de retorno de los métodos de nuestro servicio serán las clases soportados por JAXB.


Podremos usar cualquiera de los modelos básicos de Java:


Además, también vamos a poder usar cualquiera de los
wrappersde estos tipos básicos:


Las siguientes clases de Java también son aceptadas como tipos válidos por JAX-WS:


Además de estos datos, se permitirá el uso de colecciones cuyos elementos podrán ser de cualquiera de los tipos aceptados. Estas compilaciones podrán ser
arrays, tanto unidimensionales como multidimensionales, o bien clases del marco de colecciones de Java:


Implementación del servicio Web con el modelo EJB


Se puede emplear un
Stateless Session Bean, como se define en la especificación de
Enterprise Java Beans, para ser desplegado en un contenedor EJB. También se puede utilizar un
Singleton Session Beantal y como se define en la especificación EJB treinta y uno, para incorporar un servicio Web JAX-WS para ser desplegado en un contenedor EJB.


Los requerimientos para crear una implementación de un servicio como un EJB de sesión sin estado y
singleton, son las mismas que hemos visto en el apartado anterior para el modelo de programación con
servlets.


Podemos anotar un
beande sesión sin estado con la anotación javax.ejb.Stateless. En este caso, la clase del
beanya no debe incorporar la interfaz javax.ejb.SessionBean.


Un EJB de sesión sin estado y
singletonque incorporen un servicio Web utilizando la API de JAX-WS debería usar
javax.xml.ws.WebServiceContext, el que puede inyectarse usando la anotación @Resource, como hemos visto en el ejemplo de la sección precedente.


En el caso de emplear un
beande sesión
singletonse usa la anotación javax.ejb.Singleton.


Por ejemplo, podemos incorporar nuestro servicio Web como un
Stateless Session Beande la próxima forma:


Empaquetado y despliegue de un servicio Web


Los componentes
Portpueden empaquetarse en un fichero WAR o en un fichero EJB-JAR. Los componentes
Portempaquetados en un fichero WAR deben emplear una implementación de un servicio con el modelo de programación de servlets JAX-WS. Los componentes
Portempaquetados en un fichero EJB-JAR deben usar un
statelesso
singleton beanpara incorporar el servicio web.


El desarrollador es quien se encarga de empaquetar (bien incluyendo de forma directa, o referenciando), los siguientes elementos:


  • el archivo WSDL (opcional si se utilizan anotaciones JAX-WS)
  • la clase SEI (opcional con JAX-WS)
  • la clase que implementa el servicio y sus clases dependientes
  • los artefactos portables generados por JAX-WS (clases java generadas cuando se incorpora un servicio Web a partir de una clase java, para asistir al
    marshaling/unmarshalingde las invocaciones y contestaciones del servicio web, así como de las salvedades específicas del servicio devueltas por dicho servicio)
  • descriptor de despliegue en un módulo java EE (opcional si se emplean anotaciones JAX-WS. Cualquier información contenida en este fichero "sobreescribe" la pertinente información detallada con anotaciones)

El archivo WSDL se suele almacenar en un directorio wsdl, para su publicación durante el despliegue.


El descriptor de despliegue es específico del módulo que contenga el servicio web. En el caso del
empaquetado EJB, el fichero descriptor de despliegue con la información del servicio web se localizaría en META-INF/webservices.xml. El directorio wsdl estará en META-INF/wsdl. Si utilizamos un
warpara empaquetar nuestro servicio web, el descriptor de despliegue se localiza en WEB-INF/webservices.xml, y el directorio que contiene el archivo WSDL se encuentra en WEB-INF/wsdl.


Adicionalmente, se puede empaquetar el fichero de
catálogojax-ws-catalog.xml en el directorio META-INF/jax-ws-catalog.xml, en el caso de un módulo EJB, y WEB-INF/jax-ws-catalog.xml, en el caso de un módulo web. El archivo de catálogo se usa esencialmente para solucionar las referencias a documentos de servicios web, específicamente documentos WSDL y ficheros de esquema.


Un ejemplo del contenido del archivo
jax-ws-catalog.xmles el siguiente:



Despliegue del servicio Web


JAX-WS veintidos RI (Implementación de referencia de JAX-WS. RI=
Reference Implementation) soporta 2 modelos de despliegue para publicar servicios web. Una posibilidad es emplear el modelo definido por JSR-109 (
Web Services for Java EE), que usa el archivo
webservices.xmlpara acotar el despliegue de los servicios. Otra opción es usar el modelo de despliegue específico de JAX-WS RI, que define la configuración del despliegue en los ficheros
web.xmly
sun-jaxws.xml. Esta segunda opción es la que vamos a comentar aquí.


Nosotros vamos a usar Glassfish treinta y uno para realizar el despliegue. Glassfish incluye todas las librerías y clases necesarias para desarrollar/desplegar servicios web en la plataforma Java EE, de forma que no necesitaremos incluir ningún descriptor de despliegue en nuestro empaquetado, ni siquiera el archivo web.xml en el caso de usar el modelo de servlets.


En el caso de usar el modelo
servlets, y por consiguiente empaquetar nuestras clases en un
war, este género de despliegue (sin usar ningún descriptor) NO es portable. Para un contenedor no Java EE, por poner un ejemplo un Tomcat, el archivo war que contiene el servicio web no es diferente al
warde una aplicación
servlet/jsp. Si deseamos trabajar con creacion de paginas web en zaragoza , tenemos que elegir una implementación de JAX-WS y también incluir en el
warlas librerías precisas de JAX-WS.


Otra cuestión con respecto al tipo de despliegue sin descriptores, es que la implementación de nuestro servicio web no sería "descubierta" automáticamente por el contenedor (por poner un ejemplo Tomcat). Para esto deberíamos incluir algunos descriptores de despliegue para "decirle" a la librería JAX-WS cómo deseamos que se desplieguen nuestros servicios web. Estos descriptores son específicos de la librería que estemos usando. Así, por servirnos de un ejemplo, si decidimos desplegar nuestro servidor en un Tomcat, tendríamos que añadir en el directorio WEB-INF los archivos sun-jaxws.xml y web.xml. agencia seo huelva án información para efectuar el "despliegue" de los servicios web.



Descriptores de despliegue JAX-WS:
web.xmly
sun-jaxws.xml


En el archivo web.xml declaramos el
listenerJAX-WS WSServletContextListener, que inicializa y configura el
endpoint(componente
port) del servicio web, y el
servletJAXWS WSServlet, que es el que sirve las peticiones al servicio, utilizando la clase que implementa dicho servicio. Un ejemplo de contenido de archivo web.xml podría ser éste:


El archivo sun-jaxws.xml contiene la definición de la implementación del
endpointdel servicio. Cada
endpointrepresenta un
portWSDL, y contiene toda la información sobre la clase que incorpora el servicio,
url-patterndel
servlet, información de
binding, ubicación del fichero WSDL, y nombres "cualificados" (
qualified names) del
porty
servicedel WSDL. Si no detallamos la ubicación del archivo WSDL, éste será generado y publicado a lo largo del despliegue. Por servirnos de un ejemplo, en el próximo fichero sun-jaxws.xml señalamos que la implentación de nuestro servicio viene dada por la clase ws.news.NewsService (dicha clase debe estar anotada conc @WebService).



Responsabilidades del contenedor


El contenedor en el que resida nuestro servicio Web debe suministrar el
runtimede JAX-WS, para soportar solicitudes de invocación sobre los componentes
portdesplegados en dicho contenedor. El soporte de ejecución de JAX-WS se encargará de convertir los mensajes SOAP de las llamadas entrantes al servicio en llamadas al API java de JAX-WS, y viceversa (transformar la respuesta java en mensaje SOAP). El contenedor será el responsable de:


  • "Escuchar" en un puerto determinado, o en la URI de la implementación del servicio, esperando peticiones SOA/HTTP)
  • "Parsear" el mensaje de entrada, dependiendo del género de "enlazado"
    bindingdel servicio
  • "Mapear" el mensaje a la clase y método pertinente, conforme con los datos de despliegue del servicio
  • Crear los objetos java adecuados para el sobre (
    envelope) SOAP conforme a la especificación JAX-WS
  • Invocar al
    Service Implementation Beany al método de instancia con los parámetros java adecuados
  • Capturar la contestación de la invocación si el estilo es petición-respuesta
  • Mapear los objetos de contestación java al mensaje SOAP
  • Crear el
    envelopeadecuado del mensaje para su transporte
  • Enviar el mensaje al cliente Web


Despliegue del servicio web


La herramienta de despliegue empieza el proceso examinando el instrumento desplegado para determinar qué módulos poseen servicios Web, para ello analiza las anotaciones de servicios web o bien los descriptores de despliegue contenidos en el módulo. A continuación obtiene la información de enlazado (
binding), despliega los componentes y servicios web definidos en el módulo. Seguidamente publica los documentos WSDL que representan a los servicios web desplegados, configura al servidor e empieza la aplicación.


Creación de un servicio Web con JDK 1.6


Igual que en el caso de los clientes del servicio de servicios web, desde la versión 1.6de JDK se incluyen herramientas para generar servicios web desde una clase java. Específicamente la herramienta que se utilizará para generar el servicio es wsgen, que de la misma manera que wsimport se podrá emplear tanto en línea de comando como en forma de labor de ant.


Lo primero que vamos a deber hacer es compendiar la clase que incorpora el servicio de la misma manera que cualquier otra clase Java, con la herramienta javac. Hecho esto, produciremos el servicio con wsgen a partir de la clase compilada. Utilizaremos wsgen de la siguiente forma:


La clase que implementa el servicio (<nombre.clase.servicio>) se especificará a través de su nombre completo, esto es, incluyendo el nombre del paquete al que pertenece. Podemos proporcionar otros parámetros para indicar la manera en la que se deben generar las clases, como el directorio donde queremos que guarde los fuentes de las clases generadas (<src.dir>), el directorio donde guardará estas clases compiladas (<dest.dir>), y el classpath, en el que deberá encontrarse la clase detallada.


En el caso concreto del servicio Hello definido previamente, podríamos generar las clases precisas (después de haber compilado la clase Hello) de la próxima forma:


Con esto habremos creado las clases necesarias para publicar el servicio. Con JDK 1.6no será necesario contar con un servidor de aplicaciones para publicar este servicio, sino lo vamos a poder publicar desde cualquier aplicación Java. Podemos publicarel servicio de la próxima forma:


El método Endpoint.publish usa por defecto un contenedor servidor HTTP "ligero" que viene incluido con Java SE 6 y además nos permite desplegar nuestro servicio web sin tener que empaquetar ni desplegar nuestra aplicación. Esto es particularmente útil y práctico a lo largo del desarrollo. De esta forma no es preciso tener en marcha Glassfish, Tomcat o bien cualquier otro servidor. De hecho, si previamente tenemos en marcha una instancia de algún otro servidor usando el mismo puerto, al hacer la llamada a Endpoint.publish nos dará un fallo informándonos de que dicho puerto ya está en empleo.


Cuando ejecutemos la aplicación, vamos a poder acceder al WSDL del servicio a través de cualquier navegador en la próxima dirección:


Creación de un servicio Web JAX-WS con Maven


Vamos a ilustrar cómo, desde una clase java, y utilizando el modelo de programación de
servlets, podemos construir, empaquetar y desplegar un servicio Web JAX-WS con Maven. Utilizaremos para ello la clase expertoJava.Hola como clase que va a incorporar nuestro servicio Web.


Comenzamos creando una aplicación Web con Maven (similar a la que creamos para nuestro cliente Web del servicio en la sesión anterior)


Añadiremos en nuestro pom el plugin para desplegar nuestro proyecto en glassfish (
maven-glassfish-plugin), usando la goal
glassfish:deploy:


Para producir los artefactos precisos en la parte del servidor del servicio web no es necesario que incluyamos ningún
pluginadicional en el pom de nuestro proyecto (plugin wsgen). A lo largo de del despliegue se generarán automáticamente los ficheros necesarios (entre ellos el SEI de nuestro servicio Web) para poder emplear nuestro servicio Web.


Nombres de los instrumentos generados por Maven


Por defecto, el nombre del instrumento generado por Maven está formado por el artifactId de nuestro proyectoseguido de la versión del mismo (por poner un ejemplo, en nuestro ejemplo se produce el artefacto
HolaMundo-diez-SNAPSHOT.war).Para usar cualquier otro nombre de nuestra elección simplemente tendremos que apuntarlo utilizando laetiqueta <finalName>nombre-artefacto-que-queramos</finalName>, en la etiqueta <build></build> de nuestro
pom.xml


Ahora creamos nuestro servicio web como la clase src/main/java/expertoJava/Hola.java anotada con @Webservice. El código será el siguiente:


Como ya se ha comentado, NO es preciso configurar el despliegue usando /src/main/webapp/WEB-INF/web.xml y /src/main/webapp/WEB-INF/sun-jaxws.xml pues vamos a desplegar nuestro servicio Web sobre Glassfish.


Ahora ya estamos en disposición de compilar y desplegar nuestro servicio web:


Recordemos que el servidor de aplicaciones debe estar en marcha para poder desplegar nuestro servico webcon:


Podemos ver el
wsdlgenerado en:


Suponemos que la raíz del contexto se ha definido como "HolaMundoRaizContexto" en el fichero
src/main/webapp/WEB-INF/glassfish-web.xml. Por defecto, si no detallamos el nombre del servicio en el atributo
serviceNamede la anotación
@WebService, éste es el nombre de la clase, seguido del sufijo "Service".


Dicho
wsdlse genera automáticamente al desplegar el servicio. También se produce de forma automática código para probar los servicios desplegados. Siguiendo con nuestro ejemplo, vamos a poder probar nuestro servicio en la dirección:


Creación de servicios web con Netbeans


Vamos a ver a continuación cómo crear servicios web paso a paso usando Netbeans.Seguiremos un ejemplo del servicio web de conversión (de euros a ptas y a la inversa) para ilustrar el procedimiento que utilizaremos en Netbeans para crear un servicio web. Seguiremos los siguiente pasos:


Lo primero que necesitamos es un contenedor en el que crear nuestros servicios. Este contenedor será normalmenteun proyecto web de Netbeans, si bien también podríamos usar un módulo EJB. Para nuestro ejemplo guiadovamos a crear un nuevo proyecto web llamado ConversionWeb al que añadiremos nuestro servicio.



Una vez tenemos el proyecto web en el que introducir el servicio, pinchamos sobre él con el botón derechoy escogimos

New > Web Service ...
para añadir un servicio web.


Introduciremos el nombre que le queremos dar al servicio (nombre de la clase que implementará el servicio) y el paquete en el que estará. Aquí podemos crear un servicio web desde cero, o usar un EJB de sesión existente. Si utilizásemosesta segunda opción, los métodos del EJB se ofrecerían como operaciones del servicio web de forma automática, sin precisar hacer nada más. Para nuestro ejemplo vamos a quedarnos con la opción por defecto, que es crearel servicio web desde cero en una nueva clase Java plana.



Una vez pulsemos el botón
Finishse creará el servicio. En la vista de código podemos ver que se ha creado la clase
ConversionSW.java.



Ahora vamos añadir una operación a nuestro servicio, pulsando con el botón derecho sobre el servicio creado y escogiendo la opción "Add operation".



Al añadir una operación vamos a deber precisar su nombre, el género de datos devuelto, y sus parámetros. Ennuestro caso vamos a crear la función euro2ptas, con un parámetro euros de tipo double,y que devuelve un valor de tipo int.



Una vez añadida la operación, en la vista de código vemos el esqueleto de la implementación de nuestro servicio. Vamos a deber introducir en el métodoeuro2ptas el código que realice la conversión de euros a pesetas.



Con esto ya tenemos incorporado el servicio. Ahora podemos desplegar nuestro servicio con la opción "Run", con el botón derecho sobre el nodo del proyecto.



Podemos ver cómo está configurada por defecto la opción "Run" del menú contextual del proyecto (y también el resto de opciones de tal menú), seleccionando
"Properties"y a continuación
Actionsen el panel de la izquierda, y la acción
"Run Project"de la lista de acciones de la derecha. En la siguiente figura se muestra el comando maven que se ejecuta cuando seleccionamos la opción
"Run"del menú contextual del proyecto.



Una vez la aplicación esté ejecutándose en el servidor, vamos a poder probar el servicio pinchando sobre él servicio Webcon el botón derecho y eligiendo la opción

Test Web Service
.



Se abrirá en el navegador una web desde la que vamos a poder probar el servicio. En ella podremos observar un
enlace al documento WSDLque define el servicio, el cual nos resultará de utilidad cuando deseemos crear un clienteque acceda a él, y la lista de operaciones que ofrece. Para cada operación vamos a tener cuadros de texto paraintroducir el valor de los parámetros de entrada que necesita, y un botón para invocarla.



Si probamos la operación
euro2ptaspasando como parámetro
18.95, veremos el resultado deinvocar el servicio, y además abajo en la misma página se mostrarán los mensajes SOAP usados para dichainvocación.



Creación de servicios a partir de EJBs existentes


Además de poder crear servicios web desde cero, también podremos crearlos desde EJBs existentes. De esta formalo que vamos a estar haciendo es exportar las operaciones de los EJBs en forma de servicios web, para poder acceder a ellasdesde aplicaciones desarrolladas en otras plataformas o en cualquier otro lugar de la red.


Con Netbeans crear un servicio web desde un EJB es inmediato. Imaginemos que tenemos un EJB en nuestra aplicaciónllamado ConversionEJBBean, que proporciona las operaciones euros2ptas y ptas2euros. Podremosexportar dichas operaciones en forma de servicio web de la siguiente forma:


Crearemos un nuevo servicio web en nuestro proyecto, con
New->Web Service...al igual que en el caso anterior.


Ahora, además del nombre del servicio y el bulto en el que deseamos crear sus clases, deberemosespecificar que cree el servicio web a partir de un EJB (

Create Web Service from Existing Session Bean
), y pulsamos el botón
Browse ...para elegir el EJB a partir delcual deseemos crear el servicio.



Seleccionaremos el EJB que queremos utilizar (en nuestro caso ConversionEJBBean), y pulsamos
OKy a continuación
Finishpara concluir la creación del servicio web.



Con esto podremos acceder a las operaciones de nuestro EJB de sesión mediante un servicio web. A continuación mostramos el código de nuestro servicio Web, creado desde el EJB existente:


Creación de servicios a partir del WSDL


Hemos visto como crear con Netbeans servicios web desde códigoJava que ya tenemos incorporado. Esta es la forma más inmediata de crear servicios web, no obstante, si lo que buscamos es una altainteroperabilidad, no resulta la forma más conveniente de hacerlo. Podría darnos inconvenientes sobre todo en el caso en que nuestras operacionesintercambien géneros de datos complejos, ya que podríamos tener problemasal procurar recomponer dichos tipos desde clientes del servicio de diferentesplataformas.


Lo esencial en un servicio web SOAP es el
contratoque existe entrecliente y servicio, es decir, el documento WSDL. Por consiguiente, a la hora decrear servicios web complejos es conveniente comenzar definiendo dichocontrato. De esta manera tendremos mayor control sobre los datos quese serializan durante la invocación del servicio, con lo que podremosdefinir las estructuras de datos que consideremos más convenientes parael intercambio. Una vez definido el contrato (WSDL), generaremos a partirde él el esqueleto para la implementación del servicio que cumpla condicho contrato.



Creamos el WSDL y archivo de esquema


En Netbeans podremos generar un servicio web a partir de un documentoWSDL de forma sencilla. La primera cosa que vamos a deber hacer es redactar eldocumento WSDL, y el esquema asociado que definirá nuestro servicio. Ya hemos visto cómo hacer ésto en la sesión precedente. Recordemos que debemos pinchar con el botón derecho sobre nuestro proyecto y seleccionar
New > Other .... Nos aparecerá la ventana para crear un nuevo archivo, y dentro de ella elegiremos la categoría
XMLy el tipo
XML Schema(para el archivo de esquema), y
WSDL Document, para el documento WSDL. Al continuar con el asistente, podremosintroducir los datos básicos del documento WSDL , como su nombre, espacio denombres, puertos, operaciones, mensajes, género de codificación, nombre delservicio, etc.


Una vez hayamos terminado de redactar el documento WSDL que actuará comocontrato de nuestro servicio, vamos a deber crear nuestro servicio web. Paracrear un servicio web que se ajuste a dicho contrato vamos a pinchar con el botónderecho sobre nuestro proyecto y seleccionaremos
New > Web Service from WSDL....Tras rellenar todos y cada uno de los datos del asistente se generarán una serie de clasescon el esqueleto de la implementación de nuestro servicio y los modelos de datosnecesarios. Ahora vamos a deber rellenar el código de cada operación del serviciopara darle su funcionalidad, y con esto habremos terminado de incorporar nuestroservicio.


Paso de datos binarios


Supongamos que queremos crear un servicio Web que dé información binaria, por poner un ejemplo archivos de imágenes en formato
jpg. Por defecto, la infraestructura de servicios de JAX-WS no puede informar a los clientes del servicio sobre cómo deben interpretar los datos binarios. Esto es, si en un mensaje SOAP incluimos datos binarios, éstos tendrán asociado el tipo
base64Binary, que será mapeado a un array de
bytes, y en consecuencia, el cliente del servicio debe saber cómo interpretar apropiadamente dichos datos.


Para poder enviar en nuestro mensaje SOAP un objeto
java.awt.Image, por servirnos de un ejemplo, y que el usuario lo reciba como tal (y no como un array de bytes que más tarde deba transformar al tipo
java.awt.Image), básicamente lo que tendremos que hacer será editar el fichero de esquema generado para que devuelva datos binarios de tipo
image/jpeg, y a continuación modificaremos el fichero wsdl a fin de que utilice el fichero de esquema con la nueva configuración.


En el fichero de esquema (
.xsd) debemos añadir el atributo expectedContentTypes="mime_type" al elemento que devuelve los datos binarios (especificado mediante el atributo type=xs:bas64Binary o type=xs:hexBinary). Este atributo (
expectedContentTypes) notifica al cliente del servicio de que debe mapear los datos binarios a un tipo Java (según las reglas de mapeado de tipo MIME a tipos Java), en vez de a un array de bytes. A continuación mostramos una tabla que muestra el mapeado entre tipos MIME y typos Java.


MIME es un estándar que clasifica los recursos y provee información (a los programas) acerca de cómo manejarlos. Esto deja la adecuada manipulación e interpretación de diferentes tipos de ficheros por la parte de los programas (como navegadores). Por poner un ejemplo, gracias a MIME, los navegadores pueden abrir correctamente un archivo ".txt" como un recurso de texto plano y no como un video o bien otro tipo.Cuando un tipo MIME no es concretado para un recurso, el programa que lo maneje puede "suponerlo" desde la extensión del mismo (por poner un ejemplo, un archivo con la extención ".bmp" debería contener una imagen de mapa de bits). Pero esto puede no siempre y en toda circunstancia dar buenos resultados ya que una sola extensión puede asociarse a más de un formato. Por su parte, los modelos MIME son únicos. Ésta es la primordial razón para utilizar las clases MIME siempre y cuando resulte posible.


Así, por ejemplo, el próximo elemento:


será mapeado a
byte []


mientras que el elemento:


será mapeado a
java.awt.Image


Vamos a ilustrar el uso de este atributo con un ejemplo. Imaginemos que tenemos un servicio Web que proporciona fotos, en un caso así, de flores de jardín, en formato
jpeg.


Nuestro servicio Web, llamado
FlowerServiceproporciona la operación
getFlower. Dicha operación tiene como entrada un
stringque representa el nombre de una flor, y como salida el archivo
jpgcon la fotografía de dicha flor, o una excepción en el caso de no existir dicho fichero.


A continuación mostramos un extracto del wsdl con los mensajes asociados a la operación
getFlower:


La definición de tipos pertinente en el archivo de esquema
xsdes la siguiente:


Si probamos el servicio web FlowerService mediante
Test Web Service, vamos a ver algo parecido a:



Nosotros queremos ver una imagen, y no una serie de símbolos. Sin embargo, puesto que
java.awt.Imgno es un género de esquema válido, precisamos configurar manualmente el archivo de esquema para devolver datos binarios con formato
image/jpeg.


Si el servicio web lo hemos creado en un proyecto web Maven, necesitaremos incluir alguna modificación en el
pomdel proyecto. En concreto tendremos que añadir explícitamente el
plugin

wsgen
y poner la propiedad
genWsdla
true. Dicha propiedad produce el fichero wsdl de nuestro servicio web en el directorio por defecto
dólares americanos project.build.directory/jaxws/wsgen/wsdl(siendo
$ project.build.directoryel directorio
targetde nuestro proyecto). A continuación mostramos las modificaciones a realizar en el archivo
pom.xml.


Si volvemos a "compilar" nuestra aplicación, podemos ver que en el directorio
target/jaxws/wsgen/wsdlse han generado los archivos
wsdly
xsdde nuestro servicio web. El siguiente paso es "copiar" dichos ficheros en el directorio
src/main/resources. Cuando compilemos, por defecto todos y cada uno de los ficheros del directorio
src/main/resourcesse copiarán en el WAR generado en el directorio WEB-INF/classes.


A continuación vamos a apuntar de forma explícita al servidor de aplicaciones que queremos emplear nuestra versión del archivo wsdl (que se encontrará en el directorio WEB-INF/classes. Si no lo hacemos así, el servidor de aplicaciones generará su propio fichero wsdl. Para ello, lo que debemos hacer es indicar de forma explícita el atributo
wsdlLocationde nuestro servicio web, "apuntando" al directorio en donde estará el wsdl que queremos emplear.


A continuación mostramos la modificación a efectuar en el archivo
src/main/resources/FlowerService.xsd:


Una vez efectuadas las modificaciones anteriores, si volvemos a realizar un "test" sobre nuestro servicio Web, vamos a ver un resultado afín al siguiente:



Servicios web con estado


Una de las características que más se han echado en falta en los primeros años de existencia de los servicios web es la capacidad de mantener un estado. Los servicios web eran servicios sin estado, en los quecada llamada era independiente de las demás, y si queríamos identificar de alguna manera al cliente del servicio queestaba realizando la llamada debíamos suministrar como parámetro un identificador creado por nosotrosque nos indicase de qué cliente se trataba. Esta falta complicaba incorporar elementos como por ejemploun carrito de la compra mediante servicios web, al que pudiesemos añadir productos en sucesivas llamadas.


Para reemplazar esta carencia se han ideado diferentes técnicas, como por ejemplo el acceder a sesiones HTTPa través del objeto WebServiceContext. También encontramos otro enfoque consistente en aprovechar latecnología
WS-ReliableMessagingpara incorporar el estado. Cada canal de datos tiene un identificadorde sesión único, al que podemos acceder a través de la propiedad com.sun.xml.ws.sessionid del objeto WebServiceContext, y que puede ser usado para identificar a cada cliente del servicio y de este modo mantener su estado.


Sin embargo, desde JAX-WS 2.1 aparece la tan esperada posibilidad de crear servicios web con estado(
stateful). En este caso tendremos una instancia diferente de la clase del servicio para cada usuario.De esta forma, en la clase que implementa el servicio vamos a poder acotar variables de instancia (en contraste a los servicios
stateless, en los que todos los campos debían ser estáticos), y cada clienteaccederá a sus datos propios (guardados en los campos de la instancia específica a la que esté accediendo).


Estos servicios con estado están basados en la tecnología
WS-Addressing. Estatecnología permite identificar un
endpointde un servicio mediante XML, de forma independienteal protocolo de transporte que se vaya a emplear para acceder a él. De esta forma se podrá especificarno sólo la dirección del servicio, sino también la instancia concreta a la que queremos acceder de dichoservicio.


Vamos a ver un ejemplo fácil de servicio con estado. Para crear un servicio de esta clase deberá estar anotado con @Addressing, para poder identificar desde el cliente la instancia específica del servicio a la que conectarse, y con @Stateful para marcarlo como servicio web con estado. Alestar marcado de esta manera, el contenedor le inyectará de forma automática un objeto de tipo StatefulWebServiceManager en un campo manager que será público y estático (o privado y alcanzable mediante
gettersy
setters).


Sabemos que cada cliente tendrá acceso a una instancia de este servicio. Mas, ¿cuándo se crea dicha instancia?¿y quién será el encargado de crearla? No puede hacerse automáticamente cuando desde el cliente llega unapetición, en tanto que no conocemos qué parámetros hay que pasarle al constructor (por esa razón los servicios
statelessdeben tener un constructor vacío, que es el que usa el contenedor para instanciarlos). En consecuencia, estos servicios con estado deberán ser instanciados desde otros servicios. Por poner un ejemplo, si deseamos acceder a nuestra cuenta podemos hacerlo a través de un servicio BancoSW como elsiguiente:


Lo único que tiene de singular este servicio es que como resultado nos devuelve un objeto W3CEndpointReference, es decir, una referencia a un
endpointcodificada a través de
WS-Addressing. El
endpointal que hará referencia será a la instancia del servicioCuentaSW correspondiente a la cuenta pedida. Así cada cliente podrá acceder a unacuenta diferente, sosteniendo cada una de ellas su estado separadamente.


Podemos destacar también que la operación export del
managerde la cuenta es laque produce la referencia al
endpoint. Cuando deseemos cerrar la sesión podemos usar unexport a fin de que la instancia detallada del servicio deje de estar disponible como servicio web.


Vamos ahora a ver cómo accederemos a este servicio desde el cliente. Para esto la primera cosa que deberemoshacer es crear en nuestro proyecto cliente del servicio los
stubspara acceder a los dos servicios creados anteriormente (de la misma manera que hicimos en la sesión precedente). Hecho esto vamos a poder introducirel código del cliente del servicio como se muestra a continuación:


Podemos observar que creamos los servicios BancoSW y CuentaSW igual que cualquierotro servicio. El puerto del banco también se obtiene de exactamente la misma forma que anteriormente, y desde élpodemos llamar a la operación abrirCuenta para obtener el
endpointde la cuenta a laque queremos acceder. Ahora es cuando viene la parte diferente, en tanto que el puerto de la cuenta deberá conseguirse a fin de que acceda al
endpointconcreto que nos ha suministrado el banco. Para ello debemosutilizar una versión opción alternativa del método getPort sobre el servicio CuentaSWService. Enesta versión deberemos suministar tanto el
endpointobtenido, como la clase que define el tipo de puertoal que accederemos (CuentaSW). Esta versión de getPort sólo está disponible a partir deJAX-WS 2.1, por lo que con versiones anteriores de la librería no podremos acceder a este género de servicios.