jueves, 22 de noviembre de 2012

JAX-WS - Manejo de Excepciones

Un tutorial interesante es:

http://wiki.fluxit.com.ar/display/PUBLIC/SOAP+Faults+y+excepciones+customizadas+en+JAX-WS+(Apache+CXF)

Y este otro (conceptos teóricos necesarios)

http://www.ibm.com/developerworks/webservices/library/ws-tip-jaxrpc/index.html
http://www.ibm.com/developerworks/webservices/library/ws-jaxws-faults

All mapped JAX-WS exceptions contain a private faultInfo instance variable. The type of this faultInfo is derived from the schema, which the fault's wrapper element refers to; in this case it's Fault.. This instance is a java bean (POJO) that is created when we create a WS's client.

In the SOAP Web services world, a fault flows from the server to the client in the form of SOAP fault. A SOAP fault consists of the faultcode, faultstring, and optional fault actor and detail. The JAX-WS specification defines rules about how to map from a Java exception to a SOAP fault (server side) and from the SOAP fault back to the Java exception (client side).

Pero básicamente a la hora de hacer excepciones con CXF versión 2.2.3, fue tan simple como lo siguiente:

NOTA: Checked, user-defined exception (mapped from the WSDL's wsdl:fault construct)

En mi WS interface:

...
...

@WebResult(name="terminal")
Terminal getNumber(@WebParam(name="num") String num) throws ExampleApiSOAPException;
...
...

En mi WS implementacion:


...
...
Terminal getNumber(@WebParam(name="num") String num) throws ExampleApiSOAPException{
...
...
}
...
...

Y la excepción propiamente dicha


@WebFault(name="ExampleApiSOAPFault")
public class ExampleApiSOAPException extends Exception{

/**

*/
private static final long serialVersionUID = 1L;

private String faultCode;
private String faultMessage;

public ExampleApiSOAPException () {

super();
}

public ExampleApiSOAPException (String faultCode, String message) {

super(message);
this.faultMessage = message;
this.faultCode = faultCode;
}

public String getFaultCode() {

return faultCode;
}

public void setFaultCode(String faultCode) {

this.faultCode = faultCode;
}

public String getFaultMessage() {

return faultMessage;
}

public void setFaultMessage(String faultMessage) {

this.faultMessage = faultMessage;
}
}


Y luego con el SOAP UI

SOAP REQUEST:


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.example.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ws:getNumber>
         <num>1221</num>
      </ws:getNumber>
   </soapenv:Body>
</soapenv:Envelope>



SOAP RESPONSE:


<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Server</faultcode>
         <faultstring>Exception getting number... hasnt got capabilities or num does not exist in the platform</faultstring>
         <detail>
            <ns1:ExampleApiSOAPFault xmlns:ns1="http://ws.example.com/">
               <faultCode xmlns:ns2="http://ws.example.com/">500</faultCode>
               <faultMessage xmlns:ns2="http://ws.example.com/">Exception getting number... hasnt got capabilities or num does not exist in the platform</faultMessage>
            </ns1:ExampleApiSOAPFault>
         </detail>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>


IMPORTANT:

Also note that the SOAP fault does not carry the exception stack trace as you normally expect for the Java exception; therefore, a Web services client should not expect to see the stack trace originating from the server side.


Cuando hagamos el cliente (por ejemplo utilizando wsimport) podremos ver entre otras las siguientes clases:


@WebFault(name = "ExampleApiSOAPFault", targetNamespace = "http://ws.example.com/")
public class ExampleApiSOAPException
    extends Exception
{

    /**
     * Java type that goes as soapenv:Fault detail element.
     * 
     */
    private ExampleApiSOAPFault faultInfo;
...
...
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ExampleApiSOAPFault", propOrder = {
    "faultCode",
    "faultMessage"
})
public class ExampleApiSOAPFault {

    @XmlElement(required = true, nillable = true)
    protected String faultCode;
    @XmlElement(required = true, nillable = true)
    protected String faultMessage;

..
...
}


Y cuando querramos utilizar el método del WS, del lado cliente, vamos a tener que hacer algo así:

....
....

public static void main(String[] args) {

WSImplService helloService = new WSImplService();
                WS ws = helloService.getWSImplPort();

try {
ws.getNumber("321");
} catch (ExampleApiSOAPException e) {

ExampleApiSOAPFault faultInfo = e.getFaultInfo();
System.out.println("faultCode: " + faultInfo.getFaultCode());
System.out.println("faultMessage: " + faultInfo.getFaultMessage());
}
}

NOTA: No lanzar RUNTIME exceptions ya que del lado del cliente no podrán ser catcheadas. Van a llegar como SOAPFaultException. 

En caso de las excepciones no checkeadas:

What happens when an unmodeled fault occurs? For example, what happens when the service above throws an exception other thansample2.fault.Fault (for instance, NullPointerException)? What happens on the client? The answer to that depends on the messaging protocol. For instance, when communicating via SOAP/HTTP, the server-side SOAP engine creates a SOAP message containing a SOAP fault (see Listing 9) with information relevant to the problem in the faultcode and faultstring fields. Because a SOAP fault is returned to the client, JAX-WS has defined an exception named SOAPFaultException. When the service throws an unmodeled fault, the client receives a SOAPFaultException.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <soapenv:Fault>
      <faultcode>soapenv:Server</faultcode>
      <faultstring>java.lang.NullPointerException</faultstring>
      <detail/>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

There are two other exceptions to note: WebServiceException and ExecutionException.

Summary:

Normally a RuntimeExceptions and subclasses of these are converted to a „Fault“ without any further details (except the exception message). The original class name of the exception and any extra properties are lost. Checked exceptions and their properties are described in the WSDL. When they occur, the exception type all properties are added as detail to the soap fault. On the client side the correct exception is initialized, filled with the original properties and thrown.

Example of an unchecked exception:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 
   <soap:Body> 
     <soap:Fault> 
        <faultcode>soap:Server</faultcode> 
        <faultstring>Invalid credentials. Access denied.</faultstring> 
     </soap:Fault> 
   </soap:Body> 
</soap:Envelope>

Example of checked exception:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body> 
     <soap:Fault>
       <faultcode>soap:Server</faultcode> 
       <faultstring>Invalid credentials. Access denied.</faultstring> 
       <detail> <ns1:ServiceException xmlns:ns1="http://admin.service.ika.de/">
             <errorCode>LOGIN_FAILED</errorCode> </ns1:ServiceException> 
       </detail> 
     </soap:Fault> 
   </soap:Body> 
</soap:Envelope>

En este tutorial vimos como se mapean las excepciones de java a WSDL Soap Faults y luego vimos otras excepiones:
  • SOAPFaultException and its relatives, used when the client receives an unmodeled fault
  • WebServiceException, used in the JAX-WS APIs
  • ExecutionException, used in asynchronous client program

3 comentarios:

  1. Hola muy buen aporte!!!

    Oye soy nuevo en esto de los WS y estoy desarrollando unos con JAX-WS, pero me gustaria saber como puedo hacer la interfaz y la implementación de un WS.

    Tu podrías mostrarme algún ejemplo sencillo de un WS, por ejemplo el "HolaMundo" en donde tenga su interfaz y su implementación?

    Te agradecería mucho tu ayuda.

    ResponderEliminar
    Respuestas
    1. Por cierto los estoy haciendo con el IDE NetBeans y los quiero publicar con Tomcat.

      Eliminar
    2. Hola,

      Te dejo una URL que te sera de gran utilidad para hacer un ejemplo utilizando JAX-WS utilizando maven:
      http://www.luckyryan.com/2013/06/15/apache-cxf-with-spring-integration/

      En este mismo blog, puedes hacer clic en la etiqueta llamada WEB SERVICES y ahí encontraras mucha información relacionada a web services

      Saludos.-

      Eliminar