Diferencia entre revisiones de «Spring Web Services»

De Dos Ideas.
Saltar a: navegación, buscar
(Página creada con ' [http://static.springframework.org/spring-ws/sites/1.5/ Spring Web] Services (Spring-WS) es un producto de Spring Framework para facilitar la creación de servicios web basados…')
 
 
(No se muestran 26 ediciones intermedias de 6 usuarios)
Línea 1: Línea 1:
[http://static.springframework.org/spring-ws/sites/1.5/ Spring Web] Services (Spring-WS) es un producto de Spring Framework para facilitar la creación de servicios web basados en el intercambio de documentos (document driven).  
+
[[Spring Web Services]] (Spring-WS) es un producto de [[Spring Framework]] para facilitar la creación de servicios web basados en el intercambio de documentos (document driven).  
  
 
Spring-WS Se basa en servicios en los cuales primero se establece un contrato y luego se implementan (contract first), evitando atar al contrato como sucede en los casos en los cuales se genera el mismo a partir de las clases java.
 
Spring-WS Se basa en servicios en los cuales primero se establece un contrato y luego se implementan (contract first), evitando atar al contrato como sucede en los casos en los cuales se genera el mismo a partir de las clases java.
  
==Las principales características de Spring-WS==
+
{{curso|url=http://www.dosideas.com/cursos/course/view.php?id=10|nombre=Servicios Web con Spring-WS}}
 +
 
 +
==Las principales características de SpringWS==
  
 
Facilita aplicar las mejores practicas para la creación de servicios web
 
Facilita aplicar las mejores practicas para la creación de servicios web
 
Facilidad para distribuir los pedidos xml a través de diferentes tipos de mapeos
 
Facilidad para distribuir los pedidos xml a través de diferentes tipos de mapeos
 
Soporte para varias librerias de manejo de XML (DOM, SAX, StAX, JDOM, dom4j, XDOM)
 
Soporte para varias librerias de manejo de XML (DOM, SAX, StAX, JDOM, dom4j, XDOM)
Soporte para mapeo de xml a objetos (Castor, JiBX, JAXB, XStream)
+
Soporte para mapeo de xml a objetos ([[Castor]], JiBX, JAXB, XStream).
Integración con Spring Framework
+
 
 +
== Interceptores ==
 +
Si usamos ''org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping'' para hacer el binding de los endpoints podemos agregar varios interceptores. Solo hay que definirlos dentro de la lista de interceptores que tiene como opción la clase.
 +
 
 +
Ejemplo:
 +
<code xml>
 +
<!-- rooteo de los mensajes a cada uno de los endpoints -->
 +
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
 +
    <property name="mappings">
 +
        <props>
 +
            <!-- rooteo para el ejemplo de payload -->
 +
            <prop key="{http://dosideas.com/oc/schemas}OrdenDeCompraRequest">ordenDeCompraMarshallingEndPoint</prop>
 +
            <!-- rooteo para el ejemplo de message -->
 +
            <prop key="{http://dosideas.com/oc/schemas}ConsultaPrecioRequest">consultaPrecioMessageEndpoint</prop>
 +
        </props>
 +
    </property>
 +
    <property name="interceptors">
 +
        <list>
 +
            <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
 +
            <ref bean="validatingInterceptor"/>
 +
        </list>
 +
    </property>
 +
</bean>
 +
</code>
 +
 
 +
=== Interceptor de validaciones ===
 +
El interceptor de validaciones es una herramienta muy útil, que nos permite verificar tanto el request como el response del web service. Verifica tanto el esquema de los xmls como las restricciones que tiene cada uno de los elementos de los xsd. Si alguna de las validaciones no se cumple devuelve un fault si es un request, y genera un warning en el log si es un response.
 +
 
 +
Ejemplo:
 +
<code xml>
 +
<bean id="validatingInterceptor"
 +
    class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
 +
    <property name="xsdSchemaCollection" ref="schemaCollection" />
 +
    <property name="validateRequest" value="true"/>
 +
    <property name="validateResponse" value="true"/>
 +
</bean>
 +
</code>
 +
Este bean hace referencia a los esquemas(.xsd) y al interpretarlos puede saber la estructura y restricciones de los request/response.
 +
 
 +
Ejemplo:
 +
 
 +
<code xml>
 +
<bean id="schemaCollection"
 +
    class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
 +
    <property name="xsds">
 +
        <list>
 +
            <value>/WEB-INF/contratoDeDatos.xsd</value>
 +
        </list>
 +
    </property>
 +
    <property name="inline" value="true"/>
 +
</bean>
 +
</code>
 +
 
 +
Si el servicio tiene más de una operación, existirán varios xsd. Actualmente existe un error al querer ingresar más de un xsd(el interceptor ve solo el primero). Para salvar este error se aconseja generar un nuevo .xsd que incluya los definidos para cada operación.
 +
 
 +
Ejemplo de contratoDeDatos.xsd agrupador:
 +
 
 +
<code xml>
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
<xsd:schema targetNamespace="http://www.dosideas.com.ar/ws/schema/cater"
 +
elementFormDefault="qualified"
 +
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 +
xmlns="http://www.dosideas.com.ar/ws/schema/cater">
 +
 
 +
    <xsd:include schemaLocation="validarCentroEmisor.xsd"></xsd:include>
 +
    <xsd:include schemaLocation="agenteUsuario.xsd"></xsd:include>
 +
    <xsd:include schemaLocation="validarFactura.xsd"></xsd:include>
 +
</xsd:schema>
 +
</code>
 +
 
 +
 
 +
==== Extendiendo interceptor de validaciones ====
 +
 
 +
Si quisieramos que el interceptor de validaciones devolviera un fault como respuesta, podemos extender de la clase de Spring-WS ('''PayloadValidatingInterceptor''') y sobreescribir el metodo '''handleResponseValidationErrors''' con el mismo código que utiliza Spring-WS para manejar los errores en los requests, que quedaría de la siguiente forma:
 +
 
 +
<code java>
 +
 
 +
@Override
 +
    protected boolean handleResponseValidationErrors(MessageContext messageContext, SAXParseException[] errors) {
 +
        for (int i = 0; i < errors.length; i++) {
 +
            logger.warn("XML validation error on response: " + errors[i].getMessage());
 +
        }
 +
 
 +
        if (messageContext.getResponse() instanceof SoapMessage) {
 +
            SoapMessage response = (SoapMessage) messageContext.getResponse();
 +
            SoapBody body = response.getSoapBody();
 +
            SoapFault fault = body.addClientOrSenderFault(getFaultStringOrReason(), getFaultStringOrReasonLocale());
 +
            if (getAddValidationErrorDetail()) {
 +
                SoapFaultDetail detail = fault.addFaultDetail();
 +
                for (int i = 0; i < errors.length; i++) {
 +
                    SoapFaultDetailElement detailElement = detail.addFaultDetailElement(getDetailElementName());
 +
                    detailElement.addText(errors[i].getMessage());
 +
                }
 +
 
 +
            }
 +
        }
 +
        return false;
 +
    }
 +
</code>
 +
 
 +
=== Interceptor para el manejo de errores ===
 +
Para resolver las excepciones lanzada por los endpoint´s Spring-WS nos provee la clase SimpleSoapExceptionResolver.  Al generar un bean que extienda de SimpleSoapExceptionResolver, Spring-ws sobreescribe su propia clase que intercepta las excepciones y delega el manejo al bean, por lo tanto no requiere que se declare ningún interceptor.
 +
 
 +
Ejemplo:
 +
 
 +
El siguiente ejemplo muestra como personalizar un fault mediante el uso de castor. Se toma como referencia un artículo del blog de [http://janvanbesien.blogspot.com/2008/03/soap-faults-with-spring-ws-and-jaxb-20.html Jan Van Besien's blog]
 +
 
 +
Definición del fault en xsd:
 +
<code xml>
 +
<?xml version="1.0" encoding="ISO-8859-1"?>
 +
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 +
        elementFormDefault="qualified"
 +
        targetNamespace="http://www.dosideas.com/ws/schema/tutorial/servicios"
 +
        xmlns:tns="http://www.dosideas.com/ws/schema/tutorial/servicios">
 +
    <xsd:element name="servicioFault">
 +
        <xsd:complexType>
 +
            <xsd:all>
 +
                <xsd:element name="codigo" type="xsd:integer"/>
 +
                <xsd:element name="descripcion" type="xsd:string"/>
 +
            </xsd:all>
 +
        </xsd:complexType>
 +
    </xsd:element> 
 +
</xsd:schema>
 +
</code>
 +
 
 +
Definición de Castor:
 +
<code xml>
 +
  <class name="com.dosideas.exception.ClienteInexistenteException">
 +
      <map-to xml="servicioFault"
 +
      ns-prefix="dosideas"
 +
      ns-uri="http://www.dosideas.com/ws/schema/tutorial/servicios" />
 +
      <field name="codigo" type="java.lang.Integer">
 +
          <bind-xml name="codigo" node="element"/>
 +
      </field>
 +
      <field name="descripcion" type="java.lang.String">
 +
          <bind-xml name="descripcion" node="element"/>
 +
      </field>
 +
  </class>
 +
</code>
 +
 
 +
Definción de la excepción:
 +
<code java>
 +
public class ClienteInexistenteException extends Exception {
 +
 
 +
    private static final long serialVersionUID = 1L;
 +
 
 +
    private static final Integer CODIGO_ERROR = new Integer(1);
 +
 
 +
    /**
 +
    * Constructor de la excepcion sin mensaje requerido por castor.
 +
    *
 +
    */
 +
    public ClienteInexistenteException() {
 +
        super();
 +
    }
 +
 
 +
    public ClienteInexistenteException(String mensaje) {
 +
        super(mensaje);
 +
    }
 +
 
 +
    public Integer getCodigo() {
 +
        return CODIGO_ERROR;
 +
    }
 +
 
 +
    public String getDescripcion() {
 +
        return this.getMessage();
 +
    }
 +
}
 +
</code>
 +
 
 +
Definición del bean:
 +
<code xml>
 +
  <bean class="com.dosideas.exception.handler.ServicioExceptionResolver" />
 +
</code>
 +
 
 +
Definición de la clase ServicioExceptionResolver:
 +
<code java>
 +
 
 +
public class ServicioExceptionResolver extends SimpleSoapExceptionResolver {
 +
 
 +
    /**
 +
    * {@inheritDoc}.
 +
    */
 +
    @Override
 +
    protected void customizeFault(MessageContext messageContext, Object endpoint, Exception exception, SoapFault fault) {
 +
        super.customizeFault(messageContext, endpoint, exception, fault);
 +
        AbstractMarshallingPayloadEndpoint marshallingendEndpoint = (AbstractMarshallingPayloadEndpoint) endpoint;
 +
 
 +
        Result result = fault.addFaultDetail().getResult();
 +
 
 +
        try {
 +
            marshallingendEndpoint.getMarshaller().marshal(exception, result);
 +
        } catch (IOException e) {
 +
            throw new MarshallingFailureException("Se produjo un error al tratar de parsear la siguiente excepción: " + exception.getClass(), e);
 +
        }
 +
    }
 +
}
 +
</code>
  
Dos ideas cuenta con un curso para exponer y consumir webservices con SpringWS. Haga click  para ingresar.
+
<b>Nota:</b> En el ejemplo expuesto, el manejo de las excepciones Runtime no esta contemplado.
  
 
== Ver también ==
 
== Ver también ==
 +
* [[MTOM con Spring Web Services]]
 +
* [[Web Service Con Spring]]
 
* [http://www.dosideas.com/cursos/course/view.php?id=10 Curso de Spring-WS de DosIdeas]
 
* [http://www.dosideas.com/cursos/course/view.php?id=10 Curso de Spring-WS de DosIdeas]
* [[Endpoint - Interceptores]]
+
* [http://static.springsource.org/spring-ws/sites/1.5/ Web oficial de Spring-WS]
 +
* [http://janvanbesien.blogspot.com/2008/03/soap-faults-with-spring-ws-and-jaxb-20.html Jan Van Besien's blog]
  
[[Category:Java]]
+
[[Category:Spring Framework]]
 
[[Category:Web Service]]
 
[[Category:Web Service]]
[[Category:Spring-WS]]
 

Revisión actual del 20:14 21 sep 2011

Spring Web Services (Spring-WS) es un producto de Spring Framework para facilitar la creación de servicios web basados en el intercambio de documentos (document driven).

Spring-WS Se basa en servicios en los cuales primero se establece un contrato y luego se implementan (contract first), evitando atar al contrato como sucede en los casos en los cuales se genera el mismo a partir de las clases java.

Servicios Web con Spring-WS
Visitá el taller donde encontrarás más información, ejemplos y prácticas sobre este tema.

Las principales características de SpringWS

Facilita aplicar las mejores practicas para la creación de servicios web Facilidad para distribuir los pedidos xml a través de diferentes tipos de mapeos Soporte para varias librerias de manejo de XML (DOM, SAX, StAX, JDOM, dom4j, XDOM) Soporte para mapeo de xml a objetos (Castor, JiBX, JAXB, XStream).

Interceptores

Si usamos org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping para hacer el binding de los endpoints podemos agregar varios interceptores. Solo hay que definirlos dentro de la lista de interceptores que tiene como opción la clase.

Ejemplo: <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">

   <property name="mappings">
       <props>
           <prop key="{http://dosideas.com/oc/schemas}OrdenDeCompraRequest">ordenDeCompraMarshallingEndPoint</prop>
           <prop key="{http://dosideas.com/oc/schemas}ConsultaPrecioRequest">consultaPrecioMessageEndpoint</prop>
       </props>
   </property>
   <property name="interceptors">
       <list>
           <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
           <ref bean="validatingInterceptor"/>
       </list>
   </property>

</bean>

Interceptor de validaciones

El interceptor de validaciones es una herramienta muy útil, que nos permite verificar tanto el request como el response del web service. Verifica tanto el esquema de los xmls como las restricciones que tiene cada uno de los elementos de los xsd. Si alguna de las validaciones no se cumple devuelve un fault si es un request, y genera un warning en el log si es un response.

Ejemplo: <bean id="validatingInterceptor"

   class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
   <property name="xsdSchemaCollection" ref="schemaCollection" />
   <property name="validateRequest" value="true"/>
   <property name="validateResponse" value="true"/>

</bean> Este bean hace referencia a los esquemas(.xsd) y al interpretarlos puede saber la estructura y restricciones de los request/response.

Ejemplo:

<bean id="schemaCollection"

   class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
   <property name="xsds">
       <list>
           <value>/WEB-INF/contratoDeDatos.xsd</value>
       </list>
   </property>
   <property name="inline" value="true"/>

</bean>

Si el servicio tiene más de una operación, existirán varios xsd. Actualmente existe un error al querer ingresar más de un xsd(el interceptor ve solo el primero). Para salvar este error se aconseja generar un nuevo .xsd que incluya los definidos para cada operación.

Ejemplo de contratoDeDatos.xsd agrupador:

<?xml version="1.0" encoding="UTF-8"?> <xsd:schema targetNamespace="http://www.dosideas.com.ar/ws/schema/cater" elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.dosideas.com.ar/ws/schema/cater">

   <xsd:include schemaLocation="validarCentroEmisor.xsd"></xsd:include>
   <xsd:include schemaLocation="agenteUsuario.xsd"></xsd:include>
   <xsd:include schemaLocation="validarFactura.xsd"></xsd:include>

</xsd:schema>


Extendiendo interceptor de validaciones

Si quisieramos que el interceptor de validaciones devolviera un fault como respuesta, podemos extender de la clase de Spring-WS (PayloadValidatingInterceptor) y sobreescribir el metodo handleResponseValidationErrors con el mismo código que utiliza Spring-WS para manejar los errores en los requests, que quedaría de la siguiente forma:

@Override

   protected boolean handleResponseValidationErrors(MessageContext messageContext, SAXParseException[] errors) {
       for (int i = 0; i < errors.length; i++) {
           logger.warn("XML validation error on response: " + errors[i].getMessage());
       }
       if (messageContext.getResponse() instanceof SoapMessage) {
           SoapMessage response = (SoapMessage) messageContext.getResponse();
           SoapBody body = response.getSoapBody();
           SoapFault fault = body.addClientOrSenderFault(getFaultStringOrReason(), getFaultStringOrReasonLocale());
           if (getAddValidationErrorDetail()) {
               SoapFaultDetail detail = fault.addFaultDetail();
               for (int i = 0; i < errors.length; i++) {
                   SoapFaultDetailElement detailElement = detail.addFaultDetailElement(getDetailElementName());
                   detailElement.addText(errors[i].getMessage());
               }
           }
       }
       return false;
   }

Interceptor para el manejo de errores

Para resolver las excepciones lanzada por los endpoint´s Spring-WS nos provee la clase SimpleSoapExceptionResolver. Al generar un bean que extienda de SimpleSoapExceptionResolver, Spring-ws sobreescribe su propia clase que intercepta las excepciones y delega el manejo al bean, por lo tanto no requiere que se declare ningún interceptor.

Ejemplo:

El siguiente ejemplo muestra como personalizar un fault mediante el uso de castor. Se toma como referencia un artículo del blog de Jan Van Besien's blog

Definición del fault en xsd: <?xml version="1.0" encoding="ISO-8859-1"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

       elementFormDefault="qualified"
       targetNamespace="http://www.dosideas.com/ws/schema/tutorial/servicios"
       xmlns:tns="http://www.dosideas.com/ws/schema/tutorial/servicios">
   <xsd:element name="servicioFault">
       <xsd:complexType>
           <xsd:all>
               <xsd:element name="codigo" type="xsd:integer"/>
               <xsd:element name="descripcion" type="xsd:string"/>
           </xsd:all>
       </xsd:complexType>
   </xsd:element>   

</xsd:schema>

Definición de Castor:

 <class name="com.dosideas.exception.ClienteInexistenteException">
     <map-to xml="servicioFault"
      ns-prefix="dosideas"
      ns-uri="http://www.dosideas.com/ws/schema/tutorial/servicios" />
     <field name="codigo" type="java.lang.Integer">
         <bind-xml name="codigo" node="element"/>
     </field>
     <field name="descripcion" type="java.lang.String">
         <bind-xml name="descripcion" node="element"/>
     </field>
 </class>

Definción de la excepción: public class ClienteInexistenteException extends Exception {

   private static final long serialVersionUID = 1L;
   private static final Integer CODIGO_ERROR = new Integer(1);
  
   /**
    * Constructor de la excepcion sin mensaje requerido por castor.
    * 
    */
   public ClienteInexistenteException() {
       super();
   }
   public ClienteInexistenteException(String mensaje) {
       super(mensaje);
   }
   public Integer getCodigo() {
       return CODIGO_ERROR;
   }
   public String getDescripcion() {
       return this.getMessage();
   }

}

Definición del bean:

 <bean class="com.dosideas.exception.handler.ServicioExceptionResolver" />

Definición de la clase ServicioExceptionResolver:

public class ServicioExceptionResolver extends SimpleSoapExceptionResolver {

   /**
    * {@inheritDoc}.
    */
   @Override
   protected void customizeFault(MessageContext messageContext, Object endpoint, Exception exception, SoapFault fault) {
       super.customizeFault(messageContext, endpoint, exception, fault);
       AbstractMarshallingPayloadEndpoint marshallingendEndpoint = (AbstractMarshallingPayloadEndpoint) endpoint;
       Result result = fault.addFaultDetail().getResult();
       try {
           marshallingendEndpoint.getMarshaller().marshal(exception, result);
       } catch (IOException e) {
           throw new MarshallingFailureException("Se produjo un error al tratar de parsear la siguiente excepción: " + exception.getClass(), e);
       }
   }

}

Nota: En el ejemplo expuesto, el manejo de las excepciones Runtime no esta contemplado.

Ver también