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:4204/Calculator?wsdl"/>
    </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.junit.BeforeClass;
import org.junit.Test;

import jakarta.ejb.embeddable.EJBContainer;
import javax.xml.namespace.QName;
import jakarta.xml.ws.Holder;
import jakarta.xml.ws.Service;
import java.net.URL;
import java.util.Properties;

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

public class CalculatorTest {

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

    @Test
    public void outParams() 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);

        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>