Preloader image

Com SOAP é possivel retornar multiplos valores em uma única requisição. Isto é impossível em Java pois um método pode retornar somente um objeto.

JAX-WS resolve esse problema com o conceito de Holders. Um jakarta.xml.ws.Holder é um simples wrapper objeto que pode ser passado para o método @WebService como um parâmetro. A aplicação atribui o valor do mantenedor durante a requisição e o servidor enviará valor de volta como um parâmetro OUT.

Usando @WebParam e jakarta.xml.ws.Holder

A anotação @WebParam nos permite declarar os Holders soma e multiplicação como parâmetros WebParam.Mode.OUT. Como mencionado, esses mantenedores são simplesmente cestas vázias, a aplicação pode preencher com dados para poder enviar para o cliente. O servidor irá passar então, em não inicializado.

@Stateless
@WebService(
        portName = "CalculatorPort",
        serviceName = "CalculatorService",
        targetNamespace = "http://superbiz.org/wsdl",
        endpointInterface = "org.superbiz.ws.out.CalculatorWs")
public class Calculator implements CalculatorWs {

    public void sumAndMultiply(int a, int b,
                               @WebParam(name = "sum", mode = WebParam.Mode.OUT) Holder<Integer> sum,
                               @WebParam(name = "multiply", mode = WebParam.Mode.OUT) Holder<Integer> multiply) {
        sum.value = a + b;
        multiply.value = a * b;
    }
}

Se os Holders foram especificados como parâmetros WebParam.Mode.INOUT, então o cliente consegueria utilizá-los para enviar dados e a aplicação também. A instância Holder seria então inicializada com os dados que da requisição do cliente. A aplicação poderia verificar os dados antes eventualmente sobrescrevendo-os com os valores de resposta.

O WSDL

O componente JAX-WS @WebService acima resulta no seguinte WSDL que será criado automaticamente. Aviso o tipo complexo sumAndMultiplyResponse retorna dois elementos. Estes correspondem as declarações @WebParam e nossos dois parâmetros Holder<Integer>.

<?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="sumAndMultiply" type="tns:sumAndMultiply"/>
      <xsd:complexType name="sumAndMultiply">
        <xsd:sequence>
          <xsd:element name="arg0" type="xsd:int"/>
          <xsd:element name="arg1" type="xsd:int"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="sumAndMultiplyResponse" type="tns:sumAndMultiplyResponse"/>
      <xsd:complexType name="sumAndMultiplyResponse">
        <xsd:sequence>
          <xsd:element minOccurs="0" name="sum" type="xsd:int"/>
          <xsd:element minOccurs="0" name="multiply" type="xsd:int"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="sumAndMultiplyResponse">
    <wsdl:part element="tns:sumAndMultiplyResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="sumAndMultiply">
    <wsdl:part element="tns:sumAndMultiply" name="parameters"/>
  </wsdl:message>
  <wsdl:portType name="CalculatorWs">
    <wsdl:operation name="sumAndMultiply">
      <wsdl:input message="tns:sumAndMultiply" name="sumAndMultiply"/>
      <wsdl:output message="tns:sumAndMultiplyResponse" name="sumAndMultiplyResponse"/>
    </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="sumAndMultiply">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="sumAndMultiply">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="sumAndMultiplyResponse">
        <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:8080/webservice-holder/webservices/Calculator"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Testando os parâmetros OUT

Aqui nós vemos um cliente JAX-WS executando a operação sumAndMultiply. Duas instâncias vázias do Holder são criadas e passadas como parâmetros. Os dados da sumAndMultiplyResponse são colocados na instância Holder e então ficam disponíveis para o cliente depois da operação completa.

Os mantenedores não são realmente enviados na requisição, a menos que eles estejam configurados como parâmetros INOUT via WebParam.Mode.INOUT em @WebParam

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.xml.namespace.QName;
import jakarta.xml.ws.Holder;
import jakarta.xml.ws.Service;
import java.net.URL;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

@RunWith(Arquillian.class)
public class CalculatorTest {

    @ArquillianResource
    private URL base;

    @Deployment(testable = false)
    public static WebArchive war() {
        return ShrinkWrap.create(WebArchive.class, "webservice-holder.war")
                .addClasses(Calculator.class, CalculatorWs.class);
    }

    @Test
    public void outParams() throws Exception {
        final Service calculatorService = Service.create(
                new URL(base.toExternalForm() + "webservices/Calculator?wsdl"),
                new QName("http://superbiz.org/wsdl", "CalculatorService"));

        assertNotNull(calculatorService);

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

        final Holder<Integer> sum = new Holder<Integer>();
        final Holder<Integer> multiply = new Holder<Integer>();

        calculator.sumAndMultiply(4, 6, sum, multiply);

        assertEquals(10, (int) sum.value);
        assertEquals(24, (int) multiply.value);
    }
}

Analisando as mensagens

A execução acima resulta na seguinte mensagem SOAP.

requisição cliente SOAP sumAndMultiply

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

resposta do servidor SOAP sumAndMultiplyResponse

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:sumAndMultiplyResponse xmlns:ns1="http://superbiz.org/wsdl">
      <sum>10</sum>
      <multiply>24</multiply>
    </ns1:sumAndMultiplyResponse>
  </soap:Body>
</soap:Envelope>