I have quite a simple method, which I use in WS API by JAX-WS annotations:
@WebMethod
public MyResponse sendSingle2(
@WebParam(name="username") String username,
@WebParam(name="password") String password,
@WebParam(name="newParam") String newParam) {
// the code
}
Now I want newParam to be optional. I mean I want the method still to work not only when parameter is empty in passed xml:
<ws:sendSingle2>
<username>user</username>
<password>pass</password>
<newParam></newParam>
</ws:sendSingle2>
but also when it is absent:
<ws:sendSingle2>
<username>user</username>
<password>pass</password>
</ws:sendSingle2>
I need it not to break existing API, which works without the new param.
@WebParam maps a message part to a parameter, and parts can't be optional. See Optional Message Parts in WSDL. Therefore the short answer is that precisely what you're asking can't be done. But if you can refactor this method, you can use one of the approaches described below.
Usually the optionality of a parameter is set via the schema minOccurs=0
. Furthermore, instead of using multiple parameters you could define one Request parameter in your schema which you define as parameter for your WebMethod
. The optionality is now encapsulated within the parameter and the same method is invoked for a call with or without the optional parameter(s).
I prefer defining the contract first instead of relying on automatically generated files. Once you figured out how XSD, SOAP and WSDL play in-together, you hardly want to use annotation/code-first based definitions any longer as you are more flexible the other way around.
Code-Example:
<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>
In your WSDL file you define the message like that:
<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>
The WSDL contract here defines to use style: document/literal
and with the help of the schema the actual SOAP message will be document/literal wrapped
which is furthermore WS-I compliant.
Your method will therefore change to public MyResponse sendSinge2(MyRequest request)
where request
now encapsulates username
, passowrd
and newParam
. In case newParam
was not send with the SOAP request it simply will return null
, so better check if first before you use it.
If you stick to the code-first approach, you will need to define your MyRequest
class first which you use as request parameter instead of those 2 or 3 values.
@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;
...
}
The same should be done for MyResult
if you haven't done it yet. The web method may now look something like that:
@WebMethod(operationName = "sendSingle2")
@WebResult(name = "sendSingle2Response", targetNamespace = "http://your.namespace.com")
public MyResult sendSingle2(@WebParam(name = "sendSingle2Request") MyRequest request)
{
...
}
Again, request
encapsulates the 3 parameters where you fist should check if the optional parameters are null.
HTH
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With