jueves, 24 de octubre de 2013

Spring: Spring-WS con JAXB + Hibernate + DAO

Recorriendo Spring
Las Aplicaciones empresariales modernos rara vez están solas,  a menudo se basan en datos,  proporcionados por servicios  externos.  

Para favorecer la comunicación debe haber un protocolo de comunicación algún tipo , una forma estándar de enviar y recibir mensajes en un formato que sea reconocido y apoyado por todas las principales plataformas .

SOAP (Simple Object Aplication Protocol ) es un protocolo de este tipo que permite a las aplicaciones comunicarse mediante el intercambio de mensajes en un formato XML estándar.

Los Servicios web SOAP proporciona un mecanismo de integración entre plataformas, por ejemplo , los servicios web SOAP se usan comúnmente para integrar . NET y Java.

Casi todas las plataformas y marcos modernos ( Java,. Net , Ruby , PHP, etc ) ofrecen amplias bibliotecas y herramientas que permiten a los desarrolladores exponer y consumir servicios SOAP rápida y fácilmente .

Para este ejemplo recorreremos las virtudes de Spring para construir, desplegar y probar un  servicio SOAP  para la recuperación sencilla de una cuenta bancaria.

Estos son algunos de los hitos tecnológicos que abordaremos
·         Spring 3.1 para soporte de servicios web.
·         Maven para la gestión de proyectos y dependencias
·         Tomcat como contenedor
·         SoapUI para realizar pruebas sobre nuestro servicio.
·         Hibernate para trabajar en nuestra persistencia
·         Mysql para nuestro motor de base de datos

Patrones de diseño para nuestra capa de negocio como siempre utilizaremos 
·         DAO
·         Service Facede

Existen dos enfoques fundamentales para la creación de servicios web,  “First Contract” o “Bottom Up” o “Last Contract” o “Top Down” .

Es decir, crear primero los XSD y los WSDL y luego generar las clases Java o bien generar las clases Java y que luego se generen los XSD y los WSDL.

El último enfoque consiste en tomar Contrato código existente y la generación de un contrato de servicio directamente desde que el código con el fin de exponerlo como una interfaz SOAP. 
Hay una variedad de marcos de Java por ahí ( Axis2 , XFire etc ) que proporcionan estas herramientas Java2WSDL , para generar rápidamente los servidores proxy secundarios, clases Servlet necesarios para exponer un servicio SOAP.

El primer enfoque consiste en la definición del contrato de servicio antes de implementar el servicio. Esto significa que describe los parámetros de servicio y tipos devueltos mediante XSD ( XML Schema Definiciones), luego se usa el XSD para construir una WSDL (web service definition language) esto establece un contrato público  o descripción del servicio.
Sólo después de que el contrato de servicios se ha definido claramente , es la implementación del servicio.

Este artículo describirá un primer contrato de servicio , ya que este es el enfoque preferido por varias razones que analizaremos a lo largo del tutorial. 


Conociendo un poco de XSD

XML Schema es un lenguaje de esquema utilizado para describir la estructura y las restricciones de los contenidos de los documentos XML de una forma muy precisa, más allá de las normas sintácticas impuestas por el propio lenguaje XML.

Los esquemas 

Seria un loco si no arrancar diciendo que todo los elementos de XSD se encuentran contenido por un esquema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema>
........Contenido de nuestro xsd..............
</xs:schema>

y que los mismos tienen atributos como:


xmlns:xs="http://www.w3.org/2001/XMLSchema"

indica que los elementos y tipos de datos utilizados en el esquema 

vienen del espacio de nombres "http://www.w3.org/2001/XMLSchema".

Nota: También especifica que los elementos y tipos de datos que vienen del 

espacio de nombres "http://www.w3.org/2001/XMLSchema" deben llevar el prefijo xs


targetNamespace="http://www.w3schools.com"
Indica que los elementos definidos por este esquema (note, to, from, heading, body.) 
biene del namespace "http://www.w3schools.com"

xmlns = "mi direccion url" indica simplemente un namespace por defecto.

Un ejemplo de un esquema seria el siguiente:

<?xml version="1.0" encoding="UTF-8"?>  
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns="http://com/company/webservices/account" 
 targetNamespace="http://com/company/webservices/account"
   elementFormDefault="qualified" attributeFormDefault="unqualified">  
 ..............
...............
........
.....
..
 </xs:schema>

Nota: en pocas palabras en el esquema vamos a proporcionar URL importantes que a lo largo del tutorial entenderemos con mayor detalle.

Definiendo un Elemento

  Es un elemento que contiene únicamente texto, no contiene otro elemento o atributo. El texto puede ser de diferentes tipos.

Definiendo un ejemplo
<xs:element name="xxx" type="yyy"/>
Nota: Donde xxx es el nombre del elemento y yyy es el tipo del elemento.

Los tipos de elementos disponibles son:
·         xs:string
·         xs:decimal
·         xs:integer
·         xs:boolean
·         xs:date
·         xs:time

Algunos ejemplos que nos ayudaran a entender el concepto
<xs:element name="lastname" type="xs:string"/>
<xs:element name="age" type="xs:integer"/>
<xs:element name="dateborn" type="xs:date"/>

Definiendo Atributos

 En primera instancia un elemento simple no puede tener atributos, si un elemento tiene atributos es considerado un complex type.
Definiendo un ejemplo    
<xs:attribute name="xxx" type="yyy"/>
Nota: Donde xxx es el nombre del elemento y yyy es el tipo del elemento.

Los tipos de elementos disponibles son:
·         xs:string
·         xs:decimal
·         xs:integer
·         xs:boolean
·         xs:date
·         xs:time

Algunos ejemplos
<xs:attribute name="lang" type="xs:string"/>
<xs:attribute name="lang" type="xs:string" default="EN"/>
<xs:attribute name="lang" type="xs:string" use="required"/>



Definiendo Tipos Complejos

Una Elemento complejo es un elemento que contiene otros elementos o atributos.
Definiendo un complexType contenido por medio de un elemento

<xs:element name="employee">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="firstname" type="xs:string"/>
      <xs:element name="lastname" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>
  
Nota: Podemos observar que el complextype esta contenido dentro de un element, ahora si recordamos lo que mencionamos anterior un element que contiene a otros es un un elemento complejo.

Definiendo un complextype  relacionado a un element.

<xs:element name="employee" type="personinfo"/>

<xs:complexType name="personinfo">
  <xs:sequence>
    <xs:element name="firstname" type="xs:string"/>
    <xs:element name="lastname" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

Nota: ya aquí se tenia claro el concepto de desacoplamiento, por que definimos al tipo complejo con un nombre, el que los elementos podrán referencias de manera directa.


Cual es el beneficio ¿? Esta a la vista brindado por el desacoplamiento que puede ser referenciado por N elementos.

<xs:element name="employee" type="personinfo"/>
<xs:element name="student" type="personinfo"/>
<xs:element name="member" type="personinfo"/>

<xs:complexType name="personinfo">
  <xs:sequence>
    <xs:element name="firstname" type="xs:string"/>
    <xs:element name="lastname" type="xs:string"/>
  </xs:sequence>

</xs:complexType>



Nota: Un tema para tener en cuenta que los elementos pertenecientes al tipo complejo siempre estarán contenido por una secuencia.

Definiendo Restricciones

Las restricciones se utilizan para definir los valores aceptables para elementos o atributos XML.

Definiendo un elemento con restricción.

<xs:element name="age">
  <xs:simpleType>
    <xs:restriction base="xs:integer">
      <xs:minInclusive value="0"/>
      <xs:maxInclusive value="120"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>
  
Nota: es claro que el element edad por supuesto es Integer y que como mínimo puede tener un valor 0 y como máximo 120.

Ahora es claro que voy a poder plantear el desacoplamiento del ejemplo anterior, asignándole a tipo simple un nombre que será referenciado por el elemento en cuestión.

<xs:element name="car" type="carType"/>

<xs:simpleType name="carType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="Audi"/>
    <xs:enumeration value="Golf"/>
    <xs:enumeration value="BMW"/>
  </xs:restriction>

</xs:simpleType>
 
 
Para limitar el contenido de un elemento XML para un conjunto de valores aceptables, nos gustaría utilizar la restricción de enumeración.


<xs:element name="car" type="carType"/>

<xs:simpleType name="carType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="Audi"/>
    <xs:enumeration value="Golf"/>
    <xs:enumeration value="BMW"/>
  </xs:restriction>
</xs:simpleType>
 
    
Nota: el claro que limite a car a una lista de posibles opciones.
Ahora la verdad es una de las formas de limitar los valores por XSD es la utilización de 
expresiones regulares

<xs:element name="letter">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:pattern value="([a-z])*"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>
  
Nota: y es claro que limito al element letter a que pueda aceptar solo letras.
Ahora podríamos mostrar un poco de esto pero el objetivo no es convertirnos en expertos en XSD sino que puedan seguir el tutorial, pero si realmente están interesados en indagar un poco mas sobre el tema los invito a recorrer internet donde van a encontrar mucho material sobre el tema. 



Primero creamos un nuevo proyecto.

Como la mayoría de los proyectos, realizaremos el proceso de creación del proyecto.


Lo siguiente es seleccionar la template para Simple Spring Web Maven.


Lo que desde luego nos generar una estructura de base como la siguiente.




Entendiendo las dependencias de mi proyecto.

En esta oportunidad solo analizaremos las necesidades a nivel de dependencias para mi proyecto.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>SpringWebService01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
<properties>
  <spring.version>3.0.5.RELEASE</spring.version>
  <spring.ws.version>2.0.0.RELEASE</spring.ws.version>
  <log4j.version>1.2.16</log4j.version>
  <context.path>SpringWebService01</context.path>
 </properties>
 <build>
  <plugins>
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>1.4</version>
    <executions>
     <execution>
      <goals>
       <goal>xjc</goal>
      </goals>
      <phase>generate-sources</phase>
     </execution>
    </executions>
    <configuration>
     <clearOutputDir>false</clearOutputDir>
     <outputDirectory>src/main/java</outputDirectory>
     <schemaDirectory>src/main/webapp/schemas</schemaDirectory>
     <includeSchema>**/*.xsd</includeSchema>
     <bindingDirectory>src/main/resources/bindings</bindingDirectory>
     <enableIntrospection>false</enableIntrospection>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>
     <warName>${context.path}</warName>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <dependencies>
  <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>${log4j.version}</version>
  </dependency>
  <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aspects</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>commons-collections</groupId>
   <artifactId>commons-collections</artifactId>
   <version>3.2</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-oxm</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.ws</groupId>
   <artifactId>spring-ws-core</artifactId>
   <version>${spring.ws.version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.ws.commons.schema</groupId>
   <artifactId>XmlSchema</artifactId>
   <version>1.4.3</version>
  </dependency>
  <dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>2.2.2</version>
    </dependency>
    <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>                
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>  
        <!-- Hibernate framework -->
    <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>3.6.3.Final</version>
  </dependency>
 <dependency>
   <groupId>javassist</groupId>
   <artifactId>javassist</artifactId>
   <version>3.12.1.GA</version>
  </dependency>  
 
 <!-- Hibernate library dependecy start -->
 <dependency>
  <groupId>dom4j</groupId>
  <artifactId>dom4j</artifactId>
  <version>1.6.1</version>
 </dependency>
 
 <dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.1.1</version>
 </dependency>
 <dependency>
  <groupId>antlr</groupId>
  <artifactId>antlr</artifactId>
  <version>2.7.7</version>
 </dependency>
 <!-- Hibernate library dependecy end -->
 </dependencies>
</project>

Dentro de las dependencias registradas vamos a encontrar algunos grandes grupos y por supuesto las necesidades de cada uno.

Generales:  Gestión General de la aplicación.

  • log4j: gestion de log en la aplicacion
  • commons-collections: Gestión de collection
  • XmlSchema: libreria necesaria para la gestion de ws.
  • mysql-connector-java: Conector java para MYSQL.

Spring: Librerías necesarias para el funcionamiento de IOC.

  • spring-orm: facilita la comunicación con ORM
  • spring-core: el corazon de spring.
  • spring-context: gestion de conexto.
  • spring-beans: gestion de beans.
  • spring-aop: gestion de aspectos para temas transversales.
  • spring-aspects: gestion de aspectos.
  • spring-oxm:  permite la gestión de documento XML desde/hacia un objeto, brinda una abstracción de trabajo con JAXB.
  • spring-ws-core: Liberia necesaria para la gestión de WS.
  • spring-jdbc: Libreria necesaria para gestionar la conexion JDBC.
Hibernate:  Librerías necesarias para el funcionamiento de Hibernate.

  • hibernate-core: Corazon de hibernate
  • javassist: dependencia Hibernate
  • dom4j: dependencia hibernate
  • commons-logging: dependencia hibernate
  • antlr: dependencia hibernate

Jaxb: Librerías necesarias para el funcionamiento de Jaxb

  • Plugin: Plugin del cual hablaremos mas adelante.


Nota: Es necesario entender las librerías y el significado de su inclusion por que generan sobrecarga al empaquetado final y en muchos casos ni se utilizan en el desarrollo.



Definiendo el contrato del servicio XSD

Teniendo en cuenta que estamos construyendo un servicio para recuperar simples datos bancarios vamos a empezar por la definición de nuestra entidad de negocio, una cuenta. Vamos a definir nuestra entidad cuenta

Un detalle que no debemos perder de vista que son los atributos del esquema targetNamespace, xmlns porque son las URL que utilizara JAXB para la generación de código.

src/main/webapp/schemas/AccountDetails.xsd: Definiendo la estructura de datos que utilizaremos en el retorno del servicio.

<?xml version="1.0" encoding="UTF-8"?>  
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns="http://com/company/webservices/account" 
 targetNamespace="http://com/company/webservices/account"
   elementFormDefault="qualified" attributeFormDefault="unqualified">  
      <xs:element name="Account" type="Account"/>  
      <xs:complexType name="Account">  
           <xs:sequence>  
                <xs:element name="AccountNumber" type="xs:string"/>  
                <xs:element name="AccountName" type="xs:string"/>  
                <xs:element name="AccountBalance" type="xs:double"/>  
                <xs:element name="AccountStatus" type="EnumAccountStatus"/>  
           </xs:sequence>  
      </xs:complexType>  
      <xs:simpleType name="EnumAccountStatus">  
           <xs:restriction base="xs:string">  
                <xs:enumeration value="Active"/>  
                <xs:enumeration value="Inactive"/>  
           </xs:restriction>  
      </xs:simpleType>  
 </xs:schema>

En primera instancia definí un elemento Acount que referencia la estructura de datos complejos para los elementos:
  • AccountNumber
  • AccountName
  • AccountBalance 
  • AccountStatus 
Nota: Realice una restricción en cuanto a los valores que puede aceptar proporcionando una lista de opciones. Active / Inactive. Esto se realizo con el objetivo de entender la restricciones en la definición de un archivo XSL. 


src/main/webapp/schemas/AccountDetailsServiceOperations.xsd: Ahora vamos a definir nuestro Input/Output. Es decir la estructura con la que realizaremos la solicitud y la que utilizaremos para la respuesta.

<?xml version="1.0" encoding="UTF-8"?>  
 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 xmlns="http://com/company/webservices/accountservice" 
 xmlns:account="http://com/company/webservices/account" 
 targetNamespace="http://com/company/webservices/accountservice" 
 elementFormDefault="qualified">  
      <xsd:import namespace="http://com/company/webservices/account" 
      schemaLocation="AccountDetails.xsd"/>  
      <xsd:element name="AccountDetailsRequest">  
           <xsd:complexType>  
                <xsd:sequence>  
                     <xsd:element name="accountNumber" type="xsd:string"/>  
                </xsd:sequence>  
           </xsd:complexType>  
      </xsd:element>  
      <xsd:element name="AccountDetailsResponse">  
           <xsd:complexType>  
                <xsd:sequence>  
                     <xsd:element name="AccountDetails" type="account:Account"/>  
                </xsd:sequence>  
           </xsd:complexType>  
      </xsd:element>  
 </xsd:schema>

En primera instancia defino la estructura para  para el Input o request al cual llamaremos AccountDetailsRequest  y posterior defino los elementos que en este caso solo sera uno llamado AcountNumber de tipo string. Tenemos que recordar que vamos a buscar informacion por medio de esta entrada de datos.

Lo siguiente es definir la estructura para mi output o response al cual lo llamaremos AccountDetailsResponse el cual contiene un elemento de tipo Acount.  Acount ?? sisi retorna una estructura como la que definimos en el anterior XSD. Motivo por el cual si notamos podremos observar el import del XSD antes analizado.

Como un plus una herramienta que ayuda es XMLSPY

Es una herramienta como tantas otras para edicion de archivos xml, xsl, xsd entre otros formatos, con la particularidad que muestra una representación grafica de los elementos.


Entre algunas de sus funciones te proporciona la posibilidad de visualizar el archivo en navegador.




Nota Final: Es sin duda una potente herramienta que entre otras cosas proporciona un entorno donde se puede cargar datos de motores de base de datos, generar datos de pruebas....etc. Sin duda es una de las mejores herramientas para tratamiento de este tipo de archivos.


Conociendo un amigo JAXB

Una parte fundamental de los servicios web es la conversión de mensajes SOAP de XML a objetos Java y viceversa. Para facilitar esto vamos a utilizar el marco de trabajo JAXB para para que lo realice por nosotros. JAXB  es un marco poderoso y flexible, y cuenta con grandes mejoras a otros marcos como Castor.

Java Arquitecture for XML Binding (JAXB) específicamente con las interfaces Marshaller y Unmarshaller es responsables de dirigir el proceso de serialización de los contenidos de un objeto de alguna clase de Java a datos XML y viceversa. En palabras simples, JAXB nos permite almacenar y cargar información de nuestro modelo de objetos hacia y desde un archivo XML.

Para esta función de seguro podremos encontrar numerosas librerías con el objetivo de facilitarnos la vida a la hora de esta función, en lo personal realice algunas cosas en con JAXB y me parece simple y facil de aprender.

Como funciona ?

Para poder utilizar nuestros tipos definidos XSD en la aplicación, necesitamos generar clases Java de esos tipos. Es decir necesito crear una estructura de clases que realice el mapeo con los archivos XSD.

Como lo implemento en Proyecto?

Para esto vamos a utilizar una herramienta disponible de JAXB que es un plugin para Maven el que deberemos referencia en POM, nuestro plugin se llama jaxb-maven-plugin. El plugin está configurado para analizar un conjunto de XSD del generador y ejecutar la clase de JAXB para crear clases Java para cada uno de los tipos definidos. 

Como Gestiónar JAXB desde Maven

Hay un tema en la declaracion de plugin en nuestro archivo POM cuando lo gestionamos desde M2ECLIPSE  incluir el siguiente código en la seccion build.

<build>
  <plugins>
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxb2-maven-plugin</artifactId>
    <version>1.4</version>
    <executions>
     <execution>
      <goals>
       <goal>xjc</goal>
      </goals>
      <phase>generate-sources</phase>
     </execution>
    </executions>
    <configuration>
     <clearOutputDir>false</clearOutputDir>
     <outputDirectory>src/main/java</outputDirectory>
     <schemaDirectory>src/main/webapp/schemas</schemaDirectory>
     <includeSchema>**/*.xsd</includeSchema>
     <bindingDirectory>src/main/resources/bindings</bindingDirectory>
     <enableIntrospection>false</enableIntrospection>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>
     <warName>${context.path}</warName>
    </configuration>
   </plugin>
  </plugins>
 </build>

Ahora analicemos un poco el código para entender como configurarlo, lo primero que debemos entender que JAXB utiliza una herramienta de compilador de esquemas xjc para las transformaciones que realiza.

Ahora la siguiente sección que definimos es claramente la configuración

  • clearOutputDir: indica si hay que limpiar el directorio indicado en cada ejecucion.
  • outputDirectory: indica el directorio de generacion.
  • schemaDirectory: indica el directorio donde se encuentran los esquemas.
  • includeSchema:  indico un patron de busqueda en este caso le digo que tome todo los XSD.
  • src/main/resources: indico el directorio de generacion de archivos XSD.
  •  enableIntrospection: indico la posibilidad de generación Boolean getters/setters.
Error: Si agregas el plugin y el POM te dice flaco que esta pasando y te arroja lindos mensajes como:


Que pasa que no le gusta ? y al parecer algunas herramientas para trabajar con Maven como M2ECLIPSE no tienen una buena interpretación de esta configuración en el POM por lo que requieren de un complemento de conexion M2eclipse para JAXB.

Me compro un buen libro de JAXB y hago a mano mis clases ? naaaa tranqui que hay una solución. 

Primero vaya a  "Install New Software" en nuestro eclipse y agregar la siguiente URL: http://bitstrings.github.com/m2e-connectors-p2/releases/




Seleccione el conector para JAXB2


En el siguiente paso nos presenta una validación del proceso de instalación a la cual le damos aceptar.


Finalmente nos indicara que debe reiniciar a lo que accederemos.


Nota: con todo esto ya tendríamos nuestro plugin listo para generar las clases de intercambio con los XSD, por lo que nuestro pom dejaría de gritar.

Ahora con todo esto debería pararme sobre el proyecto,  boton derecho -> Run As ->Maven Generate Source.



Si todo marcha bien por consola debería tener un resultado como el siguiente.


Si todo marcha bien y Maven no se enoja deberia haber creado las clases necesarias para mi proyecto en correspondencia con los archivos XSD.

Que bueno me facilito la vida la generación de código y ahora tengo las clases necesarias para poder trabajar.

Ahora como sabe donde generar las clases el JAXB ?  

Bueno recuerdan que hablamos de los esquemas en los archivos XSD!.  Bueno los 2 elementos que utiliza el JAXB para direccionar la generación son 

  • xmlns: indica el default namespace 
  • targetNamespace: Indica que los elementos definidos por este esquema biene del namespace

Es lo logico por que son propiedades utilizadas por los archivos XSD para conectarse.




Nota Final: en pocas palabras nos genera las clases necesarias para poder trabajar, pero analicemos  un poco la generación  muy en poco detalle dado que no es el objetivo del tutorial entender las anotaciones en clase que utiliza jaxb para vincular al los XSD.

com.company.webservice.account/Account.java:  Genero una clases con getter y setter para la estructura Account que definimos en nuestro XSD. 

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.10.22 at 01:22:14 AM ART 
//


package com.company.webservices.account;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for Account complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="Account">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="AccountNumber" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="AccountName" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="AccountBalance" type="{http://www.w3.org/2001/XMLSchema}double"/>
 *         &lt;element name="AccountStatus" type="{http://com/company/webservices/account}EnumAccountStatus"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Account", propOrder = {
    "accountNumber",
    "accountName",
    "accountBalance",
    "accountStatus"
})
public class Account {

    @XmlElement(name = "AccountNumber", required = true)
    protected String accountNumber;
    @XmlElement(name = "AccountName", required = true)
    protected String accountName;
    @XmlElement(name = "AccountBalance")
    protected double accountBalance;
    @XmlElement(name = "AccountStatus", required = true)
    protected EnumAccountStatus accountStatus;

    /**
     * Gets the value of the accountNumber property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getAccountNumber() {
        return accountNumber;
    }

    /**
     * Sets the value of the accountNumber property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setAccountNumber(String value) {
        this.accountNumber = value;
    }

    /**
     * Gets the value of the accountName property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getAccountName() {
        return accountName;
    }

    /**
     * Sets the value of the accountName property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setAccountName(String value) {
        this.accountName = value;
    }

    /**
     * Gets the value of the accountBalance property.
     * 
     */
    public double getAccountBalance() {
        return accountBalance;
    }

    /**
     * Sets the value of the accountBalance property.
     * 
     */
    public void setAccountBalance(double value) {
        this.accountBalance = value;
    }

    /**
     * Gets the value of the accountStatus property.
     * 
     * @return
     *     possible object is
     *     {@link EnumAccountStatus }
     *     
     */
    public EnumAccountStatus getAccountStatus() {
        return accountStatus;
    }

    /**
     * Sets the value of the accountStatus property.
     * 
     * @param value
     *     allowed object is
     *     {@link EnumAccountStatus }
     *     
     */
    public void setAccountStatus(EnumAccountStatus value) {
        this.accountStatus = value;
    }

}

com.company.webservice.account/EnumAccountStatus.java:  si recuerdan generamos una restricción en el XSD y lo logico es que jaxb genero una estructura de enumeracion para este tipo de datos.


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.10.22 at 01:22:14 AM ART 
//


package com.company.webservices.account;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for EnumAccountStatus.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * <p>
 * <pre>
 * &lt;simpleType name="EnumAccountStatus">
 *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
 *     &lt;enumeration value="Active"/>
 *     &lt;enumeration value="Inactive"/>
 *   &lt;/restriction>
 * &lt;/simpleType>
 * </pre>
 * 
 */
@XmlType(name = "EnumAccountStatus")
@XmlEnum
public enum EnumAccountStatus {

    @XmlEnumValue("Active")
    ACTIVE("Active"),
    @XmlEnumValue("Inactive")
    INACTIVE("Inactive");
    private final String value;

    EnumAccountStatus(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static EnumAccountStatus fromValue(String v) {
        for (EnumAccountStatus c: EnumAccountStatus.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }

}

com.company.webservice.account/ObjectFactory.java: esta es nuestra fabrica de objetos Account por codigo.


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.10.22 at 01:22:14 AM ART 
//


package com.company.webservices.account;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;


/**
 * This object contains factory methods for each 
 * Java content interface and Java element interface 
 * generated in the com.company.webservices.account package. 
 * <p>An ObjectFactory allows you to programatically 
 * construct new instances of the Java representation 
 * for XML content. The Java representation of XML 
 * content can consist of schema derived interfaces 
 * and classes representing the binding of schema 
 * type definitions, element declarations and model 
 * groups.  Factory methods for each of these are 
 * provided in this class.
 * 
 */
@XmlRegistry
public class ObjectFactory {

    private final static QName _Account_QNAME = new QName("http://com/company/webservices/account", "Account");

    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.company.webservices.account
     * 
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link Account }
     * 
     */
    public Account createAccount() {
        return new Account();
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link Account }{@code >}}
     * 
     */
    @XmlElementDecl(namespace = "http://com/company/webservices/account", name = "Account")
    public JAXBElement<Account> createAccount(Account value) {
        return new JAXBElement<Account>(_Account_QNAME, Account.class, null, value);
    }

}

com.company.webservices.accountservice/AccountDetailsRequest.java: si miramos podremos observar que es una clase con un  propiedad  acountnumber y con sus correspondientes getter y setter, ahora si recordamos el XSD es como definimos nuestra estructura request.


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.10.22 at 01:22:14 AM ART 
//


package com.company.webservices.accountservice;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType>
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="accountNumber" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "accountNumber"
})
@XmlRootElement(name = "AccountDetailsRequest")
public class AccountDetailsRequest {

    @XmlElement(required = true)
    protected String accountNumber;

    /**
     * Gets the value of the accountNumber property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getAccountNumber() {
        return accountNumber;
    }

    /**
     * Sets the value of the accountNumber property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setAccountNumber(String value) {
        this.accountNumber = value;
    }

}

com.company.webservices.accountservice/AccountDetailsResponse.java: si miramos podremos observar que es una clase con un objeto Account como propiedad y con sus correspondientes getter y setter, ahora si recordamos el XSD es como definimos nuestra estructura response.

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.10.22 at 01:22:14 AM ART 
//


package com.company.webservices.accountservice;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import com.company.webservices.account.Account;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType>
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="AccountDetails" type="{http://com/company/webservices/account}Account"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "accountDetails"
})
@XmlRootElement(name = "AccountDetailsResponse")
public class AccountDetailsResponse {

    @XmlElement(name = "AccountDetails", required = true)
    protected Account accountDetails;

    /**
     * Gets the value of the accountDetails property.
     * 
     * @return
     *     possible object is
     *     {@link Account }
     *     
     */
    public Account getAccountDetails() {
        return accountDetails;
    }

    /**
     * Sets the value of the accountDetails property.
     * 
     * @param value
     *     allowed object is
     *     {@link Account }
     *     
     */
    public void setAccountDetails(Account value) {
        this.accountDetails = value;
    }

}

com.company.webservices.accountservice/ObjectFactory.java: nuestra fabrica de objetos para los objetos antes mencionados.


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.10.22 at 01:22:14 AM ART 
//


package com.company.webservices.accountservice;

import javax.xml.bind.annotation.XmlRegistry;


/**
 * This object contains factory methods for each 
 * Java content interface and Java element interface 
 * generated in the com.company.webservices.accountservice package. 
 * <p>An ObjectFactory allows you to programatically 
 * construct new instances of the Java representation 
 * for XML content. The Java representation of XML 
 * content can consist of schema derived interfaces 
 * and classes representing the binding of schema 
 * type definitions, element declarations and model 
 * groups.  Factory methods for each of these are 
 * provided in this class.
 * 
 */
@XmlRegistry
public class ObjectFactory {


    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.company.webservices.accountservice
     * 
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link AccountDetailsRequest }
     * 
     */
    public AccountDetailsRequest createAccountDetailsRequest() {
        return new AccountDetailsRequest();
    }

    /**
     * Create an instance of {@link AccountDetailsResponse }
     * 
     */
    public AccountDetailsResponse createAccountDetailsResponse() {
        return new AccountDetailsResponse();
    }

}

Mirando los códigos en pocas palabras genero clases con una correspondencia para los archivos XSD.



Generando nuestra clases DAO con hibernate.

Primero que nada utilizaremos Hibernate para la persistencia y por supuesto las mejoras que proporciona spring para manejo con ORM.

com.company.hbm/Account.hbm.xml:  donde encontraremos los archivos de mapeo Hibernate.


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 13-sep-2013 15:59:20 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
    <class name="com.company.modelo.AccountEntity" table="account" catalog="springbanco">
        <id name="id" type="java.lang.Integer">
            <column name="Id" />
            <generator class="identity" />
        </id>
        <property name="Numero" type="string">
            <column name="Numero" length="50" />
        </property>
        <property name="Nombre" type="string">
            <column name="Nombre" length="100" />
        </property>
        <property name="Balance" type="string">
            <column name="Balance" length="100" />
        </property>
        <property name="Estado" type="string">
            <column name="Estado" length="50" />
        </property>
    </class>
</hibernate-mapping>

Nota: Es nuestro archivo de mapeo que hace referencia a la estructura de la tabla y base de datos.

com.company.modelo/AccountEntity.java:  el pojo con el que se hace el mapping.


package com.company.modelo;

public class AccountEntity {
    private Integer id;
    private String Numero;
    private String Nombre;
    private String Balance;
    private String Estado;
    
 public Integer getId() {
  return id;
 }
 public void setId(Integer id) {
  this.id = id;
 }
 public String getNumero() {
  return Numero;
 }
 public void setNumero(String numero) {
  Numero = numero;
 }
 public String getNombre() {
  return Nombre;
 }
 public void setNombre(String nombre) {
  Nombre = nombre;
 }
 public String getBalance() {
  return Balance;
 }
 public void setBalance(String balance) {
  Balance = balance;
 }
 public String getEstado() {
  return Estado;
 }
 public void setEstado(String estado) {
  Estado = estado;
 }
    
    
    
}

Nota: Aquí es cuando el tipo dice pero para creo que definí ya en el proyecto una clase entidad que tiene casi la misma estructura !!!! si si es verdad pero solo es una casualidad por que las clases que construimos con JAXB de XSL a Objetos las definimos como respuesta a la necesidad del input del servicio que no siempre se va corresponder directamente a una tabla del modelo de datos, en este caso si por que es con fines educaciones.

Importante: la configuración para hibernate la abordaremos al momento de analizar los spring bean configuracion.

com.company.webservice.dao/AccountDao.java: es la interfaz o contrato de los métodos que tendrá la implementacion hibernate, 


package com.company.webservice.dao;

import com.company.modelo.AccountEntity;
import com.company.webservices.account.Account;

public interface AccountDao {
 public AccountEntity getAccountDetails(String accountNumber);
}

com.company.webservice.dao/AccountDaoImpl.java: Corresponde a la implementacion hibernate.


package com.company.webservice.dao;

import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.company.modelo.AccountEntity;
import com.company.webservices.account.Account;
import com.company.webservices.account.EnumAccountStatus;

@Repository
public class AccountDaoImpl extends HibernateDaoSupport implements AccountDao {

 @Autowired
 public AccountDaoImpl(SessionFactory sessionFactory) {
  super.setSessionFactory(sessionFactory);
 }
 
 public AccountEntity getAccountDetails(String accountNumber) {
  
  return (AccountEntity)getHibernateTemplate().get(AccountEntity.class, new Integer(accountNumber));
  
 }

}

Por un lado extenderemos de HibernateDaoSupport a fin de optimizar el trabajo Hibernate, por otro lado podemos notar que inyectara por setter la sessionfactory necesaria para poder trabajar. Cuando analicemos el archivo de configuracion spring podremos tener un mayor entendimiento pero quiero comentar que la sessionfactory tiene la informacion de datasoruce para la conectividad al motor y la información necesaria para el mapeo hibernate. 

Algunos conceptos de Notaciones con Spring.

@Component es el estereotipo principal, indica que la clase anotada es un component (o un Bean de Spring).  @Repository, @Service y @Controller son especializaciones de @Component para casos concretos (persistencia, servicios y presentación). 

Nota: Esto significa que puede usarse siempre @Component pero lo adecuado es usar estos estereotipos ya que algunas herramientas o futuras versiones de Spring pueden añadir semántica adicional (por ejemplo Spring Roo usa estas anotaciones y genera aspectos).

En nuestra clase de implementacion dao marcamos a la clase como @Repository. Esta es una anotación de Spring. Estamos indicando que esta es una clase relacionada con la capa de persistencia, y que debe ser un Singleton (sólo habrá una instancia de la clase HibernateDaoSupport, y todos los Threads de la aplicación la compartirán).

@Autowired. Esta es una anotación de Spring. Sirve para indicarle a Spring que cuando vaya a crear la instancia de HibernateDaoSupport debe "inyectarle" (pasarle) en el constructor una referencia al SessionFactory (el SessionFactory sí lo configuraremos mediante XML, lo veremos más adelante).

En cuanto a código no hay grandes complicaciones, utilizamos los métodos de getHibernateTemplate para obtener un objeto AccountEntity de acuerdo a un accountnumber ingresado por parametro.

Generando nuestra clases SERVICE FACADE.

Ahora vamos a generar la interfaz y la implementacion para el servicio en primera instancia vamos a crear nuestra interfaz.


com.company.webservice.service/AccountService.java: Generaremos solo un método que reciba un numero de cuenta y retorne un objeto Account.


package com.company.webservice.service;

import com.company.webservices.account.Account;

public interface AccountService {

 public Account getAccountDetails(String accountNumber);  
 
}

com.company.webservice.service/AccountServiceImpl.java: es la implementacion de nuestro servicio que recibe un numero de cuenta y retorna un account que es la clase que definimos como medio de transporte para los datos que retornamos por el servicio.

package com.company.webservice.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import com.company.modelo.AccountEntity;
import com.company.webservice.dao.AccountDao;
import com.company.webservice.dao.AccountDaoImpl;
import com.company.webservices.account.Account;
import com.company.webservices.account.EnumAccountStatus;

@Service
public class AccountServiceImpl implements AccountService {

  @Autowired
  private AccountDao accountdao;
 
 public Account getAccountDetails(String accountNumber) {
  System.out.println("paso por servicio");
  
   
   AccountEntity obj = accountdao.getAccountDetails(accountNumber); 
   
   
   Account account = new Account();  
      account.setAccountNumber(obj.getNumero());  
      account.setAccountStatus(EnumAccountStatus.ACTIVE);  
      account.setAccountName(obj.getNombre());  
      account.setAccountBalance(Double.parseDouble(obj.getBalance()));        
      return account;     
   
 }


}

Ahora si analizamos el uso de estos patrones la capa de servicio es la que utilizara todos los dao que pueda tener definido el modelo de datos para responder a necesidades especificas indicadas para la exposicion de funcionalidades. 

En nuestro caso utilizara un dao para enviar un numero de cuenta y obtener la cuenta de la base de datos mysql y luego cargara el objeto account para retornarlo para que se pueda preparar el retorno del servicio a la clase endpoint.

Nota: La notacion @Service sirve como una especialización de la notacion @component, teniendo en cuenta las clases de implementación para ser detectado automáticamente por medio de escaneo classpath. Lo que significa que puedo anotar mi clases de servicio con @Component, pero anotando con @Service las clases se adaptan más adecuadamente para su procesamiento por herramientas o asociarse con los aspectos, ya que @Service es ideal para la utilizacion de pointcut. Por lo que claramente que para la definicion de nuestra implementacion del servicio debemos utilizar la notacion @service.

Por otro lado podemos notar la inyeccion de dependencia de la clase dao que al indicarle @Autowired buscara el bean de manera automatica para dejar la clase disponible.

Creando nuestro Service EndPoint

El endpoint es un punto de conexión donde archivos HTML o paginas active server son expuestas.  Endpoint proporciona información necesaria para direccionar un WebService. Proporciona una referencia o especificación que se utiliza para definir un grupo o familia de propiedades de direccionamiento de mensajes y dar características de los mensajes tales como la fuente y el destino del EndPoint.
Posibilita el uniforme direccionamiento de los mensajes.

En la vida real es la implementacion de los metodos que un cliente puede llamar sobre un servicio web.

Nota: Una Service Endpoint Interface (SEI) declara los métodos que un cliente puede invocar sobre un web service, La interfaz no es estrictamente necesaria, ya que de no estar presente, la clase de implementación implícitamente define una interfaz.Si queremos definir una interfaz explícitamente, debemos agregar el elemento “endpointInterface” a la anotación WebService



com.company.webservice.service.endpoints/AccountServiceEndpoint.java:  nuestra implementacion de servicio endpoint.

package com.company.webservice.service.endpoints;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import com.company.webservice.service.AccountService;
import com.company.webservice.service.AccountServiceImpl;
import com.company.webservices.account.Account;
import com.company.webservices.accountservice.AccountDetailsRequest;
import com.company.webservices.accountservice.AccountDetailsResponse;

@Endpoint
public class AccountServiceEndpoint {

  private static final String TARGET_NAMESPACE = "http://com/company/webservices/accountservice";
  
  @Autowired  
  private AccountService accountservice; 
 
  @PayloadRoot(localPart = "AccountDetailsRequest", namespace = TARGET_NAMESPACE)  
         public @ResponsePayload AccountDetailsResponse getAccountDetails(@RequestPayload AccountDetailsRequest request)  
         {  
         
           
           System.out.println("Entro al metodo");  
              AccountDetailsResponse response = new AccountDetailsResponse();  
            
              System.out.println(request.getAccountNumber());  
              Account account = accountservice.getAccountDetails(request.getAccountNumber());  
              System.out.println("Encontro " + account.getAccountName());
              response.setAccountDetails(account);  
              System.out.println("salgo del metodo");  
              return response;  
         }  
 
}

Ahora el código antes mencionado hace uso del soporte de anotaciones de spring para Servicios web, vamos a explicar un poco el código.

@ Enpoint es una versión especializada de la notacion standar de spring  @ Component y permite esta clase del servicio ser leida y registrada en los escaneos componente Springs.

Spring proporciona dos anotaciones para la inyección de dependencias @Autowired y @Qualifier.

@Autowired funciona por tipo, y es que ella sola se encarga de buscar un bean de la clase correspondiente definida.


@Autowired  

 private AccountService accountService_i; 


La gran limitación de esta anotación es que no es posible hacer inyección por nombre (¿qué pasa si tenemos varios beans del mismo tipo?), por lo que la solución pasa por complementarla con @Qualifier. Que de esta forma buscaria el tipo  y que tenga un nombre particular.

@Autowired  
@Qualifier("minombreespecifico")
private AccountService accountService_i; 


Perdón me fui del tema.!!!

Lo siguiente es definir el namespace definido en nuestro XSD, esto es usado para el Endpoint para mapear la solicitud con el metodo a procesar.

private static final String TARGET_NAMESPACE = "http://com/company/webservices/accountservice";

A continuación trabajamos sobre la solicitud o Request, @ PayloadRoot indica que este método va a procesar solicitudes de servicio con el elemento XML coincidente el definido por el atributo LocalPart. En el ejemplo anterior, nuestro método procesará las solicitudes de entrada de tipo AccountDetailsRequest con espacio de nombres http://com/company/webservices/accountservice. Recuerda que hemos definido este tipo XSD  y espacio de nombres antes.

 @PayloadRoot(localPart = "AccountDetailsRequest", namespace = TARGET_NAMESPACE)

A continuación llego el momento de trabajar sobre la respuesta o Request @ ResponsePayload indica el tipo de ser devuelto en la respuesta SOAP. En este ejemplo, el objeto AccountDetailsResponse se convierte a XML y devuelve a la aplicación cliente como una respuesta SOAP. @RequestPayload  AccountDetails  indica a spring convertir las solicitudes entrantes AccountDetails a partir de XML a clase Java.

public @ResponsePayload AccountDetailsResponse getAccountDetails(@RequestPayload AccountDetailsRequest request) 

En Tucumano!! la respuesta del metodo se convierte a XML y el input que llega en XML a clase para que lo pueda procesar por java. 

Finalmente nuestro código del método realiza las siguientes operaciones.

Genero una instancia de la respuesta.
AccountDetailsResponse response = new AccountDetailsResponse();  

Obtengo la cuenta utilizando el numero de cuenta a buscar de la solicitud, procesandolo con la implementacion de mi servicio.
Account account = accountService_i.getAccountDetails(request.getAccountNumber());  

Finalmente cargo la cuenta en el objeto respuestas.
response.setAccountDetails(account);  

Retorno la respuesta
return response;  

Podemos notar claramente que realizaremos una inyección de la clase servicio para trabajar en todo el procesamiento del metodo implementado del servicio.

Configurando mi Spring Bean Configuration 

Finalmente la parte interesante y que por supuesto cierra el circuito de funcionamiento nuestro Titorial.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:sws="http://www.springframework.org/schema/web-services"
 xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    
    
    <sws:annotation-driven />
 <context:component-scan base-package="com.company.webservice" />
 
 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>com.mysql.jdbc.Driver</value>
  </property>
  <property name="url">
   <value>jdbc:mysql://localhost:3306/springbanco</value>
  </property>
  <property name="username">
   <value>root</value>
  </property>
  <property name="password">
   <value></value>
  </property>
 </bean>
 
 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
   <ref bean="dataSource"/>
  </property>
  <property name="mappingResources">
   <list>
       <value>com/company/hbm/Account.hbm.xml</value>    
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
    <prop key="hibernate.show_sql">true</prop>   
   </props>
  </property>
 </bean>
 
 
 
 
 
 
 <bean id="AccountDetailsService" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition" lazy-init="true">
        <property name="schemaCollection">
            <bean class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
                <property name="inline" value="true" />
                <property name="xsds">
                    <list>
                        <value>schemas/AccountDetailsServiceOperations.xsd</value>
                    </list>
                </property>
            </bean>
        </property>
        <property name="portTypeName" value="AccountDetailsService"/>
        <property name="serviceName" value="AccountDetailsServices" />
        <property name="locationUri" value="/endpoints"/>
    </bean>

</beans>

El <sws:annotation-driven />  busca todos los beans con la anotación @EndPoint y 
mapea el métodos anotados con @PayloadRoot.

¿Cómo le decimos a Spring que por notaciones genere los beans de manera dinamica para las clases marcadas como @Repository, @Service, si tuvieramos @Component definidos en nuestro proyecto ? Debemos hacer uso de la etiqueta component-scan, a la que podemos pasar como atributo uno o varios paquetes (separados por coma):

en nuestro ejemplo lo utilizamos 
<context:component-scan base-package="com.company.webservice" />

Con esto haremos que Spring escanee el paquete com.company.webservice en busca de clases anotadas con las notaciones antes mencionadas. Este proceso se realiza al levantar el contexto y hay que tener en cuenta que Spring escaneará tanto los sub-paquetes como las dependencias Jar. En el peor de los casos el proceso tardará segundos, pero lo recomendable es evitar que la búsqueda sea demasiado general.  Por lo que una posible solucion es incluir los diferentes paquetes de manera directa separados por comas.

Nota: te aviso que si no colocas la sentencia para que realice el scaneo la generacion de Bean dinamicas no se realizara por lo que anivel codigo en la ejecucion tus clases no estaran instanciadas por lo que garantizo que rompe por todos lados.

El siguiente Bean es el datasource y que en nuestro caso tiene información de la base de datos.


<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/springbanco</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

El siguiente es el sessionFactory que se relaciona con el datasource, contiene el mapeo de clases y tablas y la definicion de propiedades de Hibernate, este es muy importante dado que es quien se inyecta en la clase dao de implementacion dao que extiende de hibernatedaosupport.


<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingResources">
<list>
   <value>com/company/hbm/Account.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>

Nuestro ultimo Beans es AccountDetailsService que es el beans para el spring ws.
Uso de DefaultWsdl11Definition permite la generación automática de WSDL. Spring utiliza las definiciones de esquemas listados en la propiedad schemaCollection, así como el portType, serviceName y locationUri para generar un archivo WSDL la primera vez que se solicita. 


<bean id="AccountDetailsService" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition" lazy-init="true">
        <property name="schemaCollection">
            <bean class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
                <property name="inline" value="true" />
                <property name="xsds">
                    <list>
                        <value>schemas/AccountDetailsServiceOperations.xsd</value>
                    </list>
                </property>
            </bean>
        </property>
        <property name="portTypeName" value="AccountDetailsService"/>
        <property name="serviceName" value="AccountDetailsServices" />
        <property name="locationUri" value="/endpoints"/>
    </bean>

Nota: Aunque esta es una característica de gran alcance que se debe utilizar con precaución en la producción como el proceso de generación de WSDL puede ser muy lento. 

Un enfoque que se utilizaba en el pasado es copiar el WSDL generado por el navegador para su proyecto y exponerlo de manera estática.

Configurando Web.xml

Si no configuramos esto no vamos para ningun lado


<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">

    <display-name>SpringWebService01</display-name>
    
   <!--
  - Location of the XML file that defines the root application context.
  - Applied by ContextLoaderListener.
 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
             /WEB-INF/config/spring.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    
    <!--
  - Servlet that dispatches request to registered handlers (Controller implementations).
 -->
    <servlet>
        <servlet-name>webservices</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value></param-value>
  </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
  <servlet-name>webservices</servlet-name>
  <url-pattern>*.wsdl</url-pattern>
 </servlet-mapping>

 <servlet-mapping>
  <servlet-name>webservices</servlet-name>
  <url-pattern>/endpoints/*</url-pattern>
 </servlet-mapping>

</web-app>

Primero que nada tengo que decirle al contexto donde se encuentra el archivo de configuracion spring para que lo pueda levantar en el inicio.

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
             /WEB-INF/config/spring.xml
        </param-value>
    </context-param>

El siguiente paso es carga el contexto de aplicación de Spring con el archivo de configuración se ha definido anteriormente.


<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>


El siguiente paso es trabajar sobre el Servlet del servicio Web que intercepta las peticiones HTTP entrantes.  Asegura WSDL es consciente del contexto. Transforma dirección de SOAP a localhost: 8080. ContextConfigLocation con un conjunto de parámetros vacío significa que la Spring no intentará cargar la configuración webservices-servlet.xml defecto.


<servlet>
        <servlet-name>webservices</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
<servlet-name>webservices</servlet-name>
<url-pattern>*.wsdl</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>webservices</servlet-name>
<url-pattern>/endpoints/*</url-pattern>
</servlet-mapping>


Probando nuestro Servicio con SOAPUI

SoapUi es una herramienta que entre otras cosas nos permite realizar un intercambio de mensajes con servicios web, es decir nos permite cargar datos al input y nos muestra la respuesta del servicio.

Nota: La primera prueba de fuego de nuestro servicio es que debe responder al wsdl, si esto no sucede no vamos para ningun lado.  Y ojo por que vamos a necesitar el WSDL.

En nuestro caso el wsdl sera 
http://localhost:8080/SpringWebService01/AccountDetailsService.wsdl



El siguiente paso es intalar el SOAPUI que no es nada complicado








El siguiente paso es crear nuestro proyecto SOAP con nuestro WSDL y realizar las pruebas.

Primero vamos a crear nuestro proyecto



El siguiente paso es cargar nuestro WSDL


El siguiente paso es abrir el editor de solicitudes.


El siguiente paso es cargar el input de la solicitud que realizaremos al Servicio.


Le damos el boton verde de arriba a la izquiera para ejecutar al consulta al Servicio y obtener la respuesta.


Esta fue la solicitud que mandamos.


Esta fue la respuesta del servicio.


A perdón un ultimo detalle,  esta es una captura de la base de datos.como para antender que estamos consultando de mysql.





Código fuente del proyecto

El codigo fuente del proyecto lo podemos descargar. Codigofuente.zip

El spcript de la base de datos mysql lo pueden descargar. script.sql





No hay comentarios:

Publicar un comentario