SOAP WS - сделайте @WebParam необязательным

У меня есть довольно простой метод, который я использую в WS API по аннотациям JAX-WS:

@WebMethod
public MyResponse sendSingle2(
    @WebParam(name="username") String username,
    @WebParam(name="password") String password,
    @WebParam(name="newParam") String newParam) {
        // the code
    }

Теперь я хочу, чтобы newParam был необязательным. Я хочу сказать, что я хочу, чтобы метод по-прежнему работал не только, когда параметр пуст в переданном xml:

<ws:sendSingle2>
    <username>user</username>
    <password>pass</password>
    <newParam></newParam>
</ws:sendSingle2>

но также, когда он отсутствует:

<ws:sendSingle2>
    <username>user</username>
    <password>pass</password>
</ws:sendSingle2>

Мне нужно, чтобы он не разбивал существующий API, который работает без нового параметра.

Ответ 1

Обычно опциональность параметра устанавливается через схему minOccurs=0. Кроме того, вместо использования нескольких параметров вы можете определить один параметр Request в своей схеме, который вы определяете как параметр для WebMethod. Опциональность теперь инкапсулируется внутри параметра, и тот же метод вызывается для вызова с или без необязательных параметров.

Я предпочитаю сначала определять контракт, а не полагаться на автоматически создаваемые файлы. Как только вы выяснили, как XSD, SOAP и WSDL играют вместе, вы вряд ли захотите использовать определения, основанные на аннотациях/кодах, больше, так как вы более гибки в обратном направлении.

код-Пример:

<xs:schema
    targetNamespace="http://your.namespace.com"
    xmlns:tns="http://your.namespace.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFromDefault="qualified"
    attributeFromDefault="qualified">

...

<xs:element name="MyRequest" type="tns:MyRequestType" />
<xs:element name="MyResponse" type="tns:MyResponseType" />

<xs:complexType name"MyRequestType">
    <xs:sequence>
        <xs:element name="username" type="xs:string" minOccurs="1" maxOccurs="1" />
        <xs:element name="password" type="xs:string" minOccurs="1" maxOccurs="1" />
        <xs:element name="newParam" type="xs:string" minOccurs="0" maxOccurs="1" />
    </xs:sequence>
</xs:complexType>

...

</xs:schema>

В вашем файле WSDL вы определяете такое сообщение:

<wsdl:definitions
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:msg="http://your.namespace.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    targetNamespace="http://your.namespace.com">

    <wsdl:types>
        <xs:schema>
            <!-- either import the externalized schema -->
            <xs:import namespace="http://your.namespace.com"
                       schemaLocation="someDir/yourMessageSchema.xsd" />
        </xs:schema>
        <!-- or define the schema within the WSDL - just copy the schema here -->
        <xs:schema
            targetNamespace="http://your.namespace.com"
            xmlns:tns="http://your.namespace.com"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            elementFromDefault="qualified"
            attributeFromDefault="qualified">
                ...
        </xs:schema>
    </wsdl:types>

    ...

    <wsdl:message name="sendSingle2Request">
        <wsdl:part name="in" element="msg:MyRequest" />
    </wsdl:message>

    <wsdl:message name="sendSingle2Response">
        <wsdl:part name="out" element="msg:MyResponse" />
    </wsdl:message>

    ...

    <wsdl:portType name="YourServiceEndpoint">
        <wsdl:operation name="sendSingle2">
            <wsdl:input message="tns:sendSingle2Request" />
            <wsdl:output message="tns:sendSingle2Response" />
        </wsdl:operation>
        ...
    </wsdl:portType>

    <wsdl:binding name="YourServiceBinding" type="YourServiceEndpoint">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name=""sendSingle2">
            <soap:operation soapAction="http://your.namespace.com/SendSingle2" style="document" />
            <wsdl:input>
                <soap:body parts="in" use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body parts="out" use="literal" />
            </wsdl:output>
        </wsdl:operation>
        ...
    </wsdl:binding>

    <wsdl:service name="YourService">
        <wsdl:port name="YourServicePort binding="tns:YourServiceBinding">
            <soap:address location="http://your.server:port/path/to/the/service" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

В контракте WSDL здесь используется стиль: document/literal, и с помощью схемы фактическое сообщение SOAP будет document/literal wrapped, которое, кроме того, совместимо с WS-I.

Поэтому ваш метод будет изменен на общедоступный MyResponse sendSinge2(MyRequest request), где request теперь инкапсулирует username, passowrd и newParam. В случае, если newParam не был отправлен с запросом SOAP, он просто вернет null, поэтому лучше проверите, прежде чем использовать его.

Если вы придерживаетесь подхода, основанного на кодах, вам нужно будет сначала определить свой класс MyRequest, который вы используете как параметр запроса, вместо тех значений 2 или 3.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "MyRequest", namespace="http://your.namespace.com")
public class MyRequest implements Serializable
{
   @XmlElement(name = "username", required = true)
   protected String username;
   @XmlElement(name = "password", required = true)
   protected String password;
   @XmlElement(name = "newParam", required = false)
   protected String newParam;
   ...
}

То же самое должно быть сделано для MyResult, если вы еще этого не сделали. Теперь веб-метод может выглядеть примерно так:

@WebMethod(operationName = "sendSingle2")
@WebResult(name = "sendSingle2Response", targetNamespace = "http://your.namespace.com")
public MyResult sendSingle2(@WebParam(name = "sendSingle2Request") MyRequest request)
{
   ...
}

Опять же, request инкапсулирует 3 параметра, в которых вы должны проверить, являются ли дополнительные параметры нулевыми.

НТН

Ответ 2

Все зависит от вашего класса реализации, в котором вы используете эти параметры. В интерфейсе конечной точки просто добавьте этот параметр в качестве webparam.

Убедитесь, что в вашем классе реализации. Если вы используете этот параметр в любом месте, добавьте альтернативный код (Else part) для выполнения вашей операции или выполнения без этого параметра.

Я думаю, что все параметры обрабатываются как необязательные, пока вы не подтвердите их или не примените их в своем классе реализации, как рекомендовано.