:index-group: Web Services
:jbake-type: page
:jbake-status: status=published
= Mantenedores @WebService com @HandlerChain

Neste exemplo nós vemos um básico componente `@WebService` JAX-WS usar uma 
cadeia de mantenedores para alterar mensagens SOAP enviadas e recebidas. 
Mantenedores SOAP são parecidos com Filtros Servlet ou Interceptadores EJB/CDI.

Em alto nível, as etapas envolvidas são: 

[arabic]
. Crie mantenedores implementando `javax.xml.ws.handler.soap.SOAPHandler`
. Declare e ordene-os em um arquivo xml via `<handler-chain>`
. Associe o arquivo xml com um componente `@WebService` via `@HandlerChain`

== O @HandlerChain
Primeiro, começaremos com nosso simples bean `@WebService`, chamado
`Calculator`, que é anotado com `@HandlerChain`

[source,java]
----
@Singleton
@WebService(
        portName = "CalculatorPort",
        serviceName = "CalculatorService",
        targetNamespace = "http://superbiz.org/wsdl",
        endpointInterface = "org.superbiz.calculator.wsh.CalculatorWs")
@HandlerChain(file = "handlers.xml")
public class Calculator implements CalculatorWs {

    public int sum(int add1, int add2) {
        return add1 + add2;
    }

    public int multiply(int mul1, int mul2) {
        return mul1 * mul2;
    }
}
----

Aqui vemos `@HandlerChain` apontando para um arquivo chamado `handlers.xml`.
Esse arquivo pode ser chamado de qualquer coisa, mas deve estar no mesmo jar e
pacote java como nosso componente `Calculator`.

== O arquivo <handler-chains>

Nosso serviço `Calculator` está no pacote `org.superbiz.calculator.wsh`, 
o que significa que o arquivo xml da cadeia de mantenedores
deve estar em `org/superbiz/calculator/wsh/handlers.xml` em nosso
classpath do aplicativo ou o arquivo não será encontrado e nenhum mantenedor
será usado.

No maven, conseguimos isso colocando nosso handlers.xml em
`src/main/resources` igual a:

* `src/main/resources/org/superbiz/calculator/wsh/handlers.xml`

Com este arquivo nós declaramos e *ordenamos* nossa cadeia mantenedora.

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
  <handler-chain>
    <handler>
      <handler-name>org.superbiz.calculator.wsh.Inflate</handler-name>
      <handler-class>org.superbiz.calculator.wsh.Inflate</handler-class>
    </handler>
    <handler>
      <handler-name>org.superbiz.calculator.wsh.Increment</handler-name>
      <handler-class>org.superbiz.calculator.wsh.Increment</handler-class>
    </handler>
  </handler-chain>
</handler-chains>
----

A ordem como você pode suspeitar é:

* `Inflate`
* `Increment`

== A implementação SOAPHandler

Nosso manipulador `Inflate` tem o trabalho de monitorar _retornos_ para as
operações 'sum' e 'multiply' e fazê-las 1000 vezes melhores.
A manipulação da mensagem é feita andando pelo `SOAPBody` e 
editando os nódulos. O método `handleMessage` é invocado para ambos, 
requisições e retornos, por isso é importante para verificar o `SOAPBody` 
antes de tentar navegar os nódulos.

[source,java]
----
import org.w3c.dom.Node;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.util.Collections;
import java.util.Set;

public class Inflate implements SOAPHandler<SOAPMessageContext> {

    public boolean handleMessage(SOAPMessageContext mc) {
        try {
            final SOAPMessage message = mc.getMessage();
            final SOAPBody body = message.getSOAPBody();
            final String localName = body.getFirstChild().getLocalName();

            if ("sumResponse".equals(localName) || "multiplyResponse".equals(localName)) {
                final Node responseNode = body.getFirstChild();
                final Node returnNode = responseNode.getFirstChild();
                final Node intNode = returnNode.getFirstChild();

                final int value = new Integer(intNode.getNodeValue());
                intNode.setNodeValue(Integer.toString(value * 1000));
            }

            return true;
        } catch (SOAPException e) {
            return false;
        }
    }

    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    public void close(MessageContext mc) {
    }

    public boolean handleFault(SOAPMessageContext mc) {
        return true;
    }
}
----

O mantenedor `Increment` é identico no código e portanto não é mostrado.
Ao invés de multiplicar por 1000, este simplesmente adiciona 1.

== O Caso de Teste

Nós usamos a API JAX-WS para criar um cliente Java para o nosso serviço web 
`Calculator` e o usamos para chamar as operações` sum` e `multiply`.
Observemos o uso inteligente da matemática, para afirmar tanto a existência e 
a ordem de nossos mantenedores. Se 'Inflate' e 'Increment' forem invertidos, 
as respostas seriam 11000 e 13000, respectivamente.

[source,java]
----
public class CalculatorTest {

    @BeforeClass
    public static void setUp() throws Exception {
        Properties properties = new Properties();
        properties.setProperty("openejb.embedded.remotable", "true");
        EJBContainer.createEJBContainer(properties);
    }

    @Test
    public void testCalculatorViaWsInterface() throws Exception {
        final Service calculatorService = Service.create(
                new URL("http://127.0.0.1:4204/Calculator?wsdl"),
                new QName("http://superbiz.org/wsdl", "CalculatorService"));

        assertNotNull(calculatorService);

        final CalculatorWs calculator = calculatorService.getPort(CalculatorWs.class);

        // we expect our answers to come back 1000 times better, plus one!
        assertEquals(10001, calculator.sum(4, 6));
        assertEquals(12001, calculator.multiply(3, 4));
    }
}
----

== Executando o exemplo

Simplesmente execute `mvn clean install` e você verá resultado semelhante o seguinte:

[source,console]
----
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.superbiz.calculator.wsh.CalculatorTest
INFO - openejb.home = /Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers
INFO - openejb.base = /Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers
INFO - Using 'javax.ejb.embeddable.EJBContainer=true'
INFO - Cannot find the configuration file [conf/openejb.xml].  Will attempt to create one for the beans deployed.
INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
INFO - Creating TransactionManager(id=Default Transaction Manager)
INFO - Creating SecurityService(id=Default Security Service)
INFO - Beginning load: /Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers/target/test-classes
INFO - Beginning load: /Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers/target/classes
INFO - Configuring enterprise application: /Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers
INFO - Auto-deploying ejb Calculator: EjbDeployment(deployment-id=Calculator)
INFO - Configuring Service(id=Default Singleton Container, type=Container, provider-id=Default Singleton Container)
INFO - Auto-creating a container for bean Calculator: Container(type=SINGLETON, id=Default Singleton Container)
INFO - Creating Container(id=Default Singleton Container)
INFO - Configuring Service(id=Default Managed Container, type=Container, provider-id=Default Managed Container)
INFO - Auto-creating a container for bean org.superbiz.calculator.wsh.CalculatorTest: Container(type=MANAGED, id=Default Managed Container)
INFO - Creating Container(id=Default Managed Container)
INFO - Enterprise application "/Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers" loaded.
INFO - Assembling app: /Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers
INFO - Created Ejb(deployment-id=Calculator, ejb-name=Calculator, container=Default Singleton Container)
INFO - Started Ejb(deployment-id=Calculator, ejb-name=Calculator, container=Default Singleton Container)
INFO - Deployed Application(path=/Users/dblevins/work/all/trunk/openejb/examples/webservice-handlers)
INFO - Initializing network services
INFO - Creating ServerService(id=httpejbd)
INFO - Creating ServerService(id=cxf)
INFO - Creating ServerService(id=admin)
INFO - Creating ServerService(id=ejbd)
INFO - Creating ServerService(id=ejbds)
INFO - Initializing network services
INFO -   ** Starting Services **
INFO -   NAME                 IP              PORT
INFO -   httpejbd             127.0.0.1       4204
INFO - Creating Service {http://superbiz.org/wsdl}CalculatorService from class org.superbiz.calculator.wsh.CalculatorWs
INFO - Setting the server's publish address to be http://nopath:80
INFO - Webservice(wsdl=http://127.0.0.1:4204/Calculator, qname={http://superbiz.org/wsdl}CalculatorService) --> Ejb(id=Calculator)
INFO -   admin thread         127.0.0.1       4200
INFO -   ejbd                 127.0.0.1       4201
INFO -   ejbd                 127.0.0.1       4203
INFO - -------
INFO - Ready!
INFO - Creating Service {http://superbiz.org/wsdl}CalculatorService from WSDL: http://127.0.0.1:4204/Calculator?wsdl
INFO - Creating Service {http://superbiz.org/wsdl}CalculatorService from WSDL: http://127.0.0.1:4204/Calculator?wsdl
INFO - Default SAAJ universe not set
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.783 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
----

== Inspecionando as mensagens

A execução acima geraria as seguintes mensagens.

=== Calculator wsdl

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  name="CalculatorService" targetNamespace="http://superbiz.org/wsdl"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:tns="http://superbiz.org/wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
    <xsd:schema attributeFormDefault="unqualified" elementFormDefault="unqualified"
                targetNamespace="http://superbiz.org/wsdl" xmlns:tns="http://superbiz.org/wsdl"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="multiply" type="tns:multiply"/>
      <xsd:complexType name="multiply">
        <xsd:sequence>
          <xsd:element name="arg0" type="xsd:int"/>
          <xsd:element name="arg1" type="xsd:int"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="multiplyResponse" type="tns:multiplyResponse"/>
      <xsd:complexType name="multiplyResponse">
        <xsd:sequence>
          <xsd:element name="return" type="xsd:int"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="sum" type="tns:sum"/>
      <xsd:complexType name="sum">
        <xsd:sequence>
          <xsd:element name="arg0" type="xsd:int"/>
          <xsd:element name="arg1" type="xsd:int"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="sumResponse" type="tns:sumResponse"/>
      <xsd:complexType name="sumResponse">
        <xsd:sequence>
          <xsd:element name="return" type="xsd:int"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="multiplyResponse">
    <wsdl:part element="tns:multiplyResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="sumResponse">
    <wsdl:part element="tns:sumResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="sum">
    <wsdl:part element="tns:sum" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="multiply">
    <wsdl:part element="tns:multiply" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="CalculatorWs">
    <wsdl:operation name="multiply">
      <wsdl:input message="tns:multiply" name="multiply">
      </wsdl:input>
      <wsdl:output message="tns:multiplyResponse" name="multiplyResponse">
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="sum">
      <wsdl:input message="tns:sum" name="sum">
      </wsdl:input>
      <wsdl:output message="tns:sumResponse" name="sumResponse">
      </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="CalculatorServiceSoapBinding" type="tns:CalculatorWs">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="multiply">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="multiply">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="multiplyResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="sum">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="sum">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="sumResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CalculatorService">
    <wsdl:port binding="tns:CalculatorServiceSoapBinding" name="CalculatorPort">
      <soap:address location="http://127.0.0.1:4204/Calculator?wsdl"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
----

=== SOAP sum e sumResponse

Requisição:

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:sum xmlns:ns1="http://superbiz.org/wsdl">
      <arg0>4</arg0>
      <arg1>6</arg1>
    </ns1:sum>
  </soap:Body>
</soap:Envelope>
----

Retorno:

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:sumResponse xmlns:ns1="http://superbiz.org/wsdl">
      <return>10001</return>
    </ns1:sumResponse>
  </soap:Body>
</soap:Envelope>
----

=== SOAP multiply e multiplyResponse

Requisição:

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:multiply xmlns:ns1="http://superbiz.org/wsdl">
      <arg0>3</arg0>
      <arg1>4</arg1>
    </ns1:multiply>
  </soap:Body>
</soap:Envelope>
----

Retorno:

[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:multiplyResponse xmlns:ns1="http://superbiz.org/wsdl">
      <return>12001</return>
    </ns1:multiplyResponse>
  </soap:Body>
</soap:Envelope>
----
