Consider the operation in the below wsdl file. It talks about a simple Port that has one operation - to update the data received. Nothing fancy about it. The service will return the updated and if necessary modified data.
When would a holder be useful ??
I spent some time traversing the net and found a useful explanation here. The reason to use holders is that Java is pass by value - always pass by value:
If the above API was written as:
To prevent this a holder object is used with the reference to INOUT parameter placed as field inside the holder. If this field is now updated to refer to a different object, the Servlet will be made aware of the change because the reference value inside the Holder will refer to the new location. Simple and yet to so powerful. Same is the case with OUT parameters.
Holder life cycle is also managed through interceptors only. In case of an inbound message:
Similarly for the outbound messages:
<?xml version="1.0" encoding = "UTF-8" ?> <wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:holder="http://ws.com/Service/xsd/holderws-schema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ws.com/Service/holderws-ns" targetNamespace="http://ws.com/Service/holderws-ns"> <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://ws.com/Service/xsd/holderws-schema"> <xs:element name="value"> <xs:complexType> <xs:all> <xs:element name="name" type="xs:string" maxOccurs="1" minOccurs="1" /> </xs:all> </xs:complexType> </xs:element> </xs:schema> </wsdl:types> <wsdl:message name="Data"> <wsdl:part element="holder:value" name="value" /> </wsdl:message> <wsdl:portType name="UpdateServicePortType"> <wsdl:operation name="execute"> <wsdl:documentation>update the object passed </wsdl:documentation> <wsdl:input message="tns:Data" /> <wsdl:output message="tns:Data" /> </wsdl:operation> </wsdl:portType> <wsdl:binding name="UpdateServicePortTypeSoapBinding" type="tns:UpdateServicePortType"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="execute"> <soap:operation soapAction="/Service/update" /> <wsdl:input> <!-- If no name in port type, then none allowed in binding either --> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="UpdateServicePort"> <wsdl:port binding="tns:UpdateServicePortTypeSoapBinding" name="UpdateServicePortType"> <soap:address location="simpleUpdate" /> </wsdl:port> </wsdl:service> </wsdl:definitions>I decided to execute a wsdltojava command on the wsdl and below is the interface created for the endpoint:
@WebService(targetNamespace = "http://ws.com/Service/holderws-ns", name = "UpdateServicePortType") @XmlSeeAlso({ ObjectFactory.class }) @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) public interface UpdateServicePortType { @WebMethod(action = "/Service/update") public void execute( @WebParam(partName = "value", mode = WebParam.Mode.INOUT, name = "value", targetNamespace = "http://ws.com/Service/xsd/holderws-schema") Holder<Value> value); }This is all similar to what we have seen yet. The only new class here is the Holder. I have seen it on certain occasions being generated. The javax.xml.ws.Holder is at its simplest a very simple class that holds data. The class has a generic field in which any type of data can be held.
When would a holder be useful ??
I spent some time traversing the net and found a useful explanation here. The reason to use holders is that Java is pass by value - always pass by value:
If the above API was written as:
public void execute(@WebParam(partName = "value", mode = WebParam.Mode.INOUT, name = "value", targetNamespace = "http://ws.com/Service/xsd/holderws-schema") Value value) { // the Value reference now refers to a different value Object. value = update(value); }The execute method is trying to replace the passed object with another. Now if this were the code in CXF Servlet:
public void executeServiceEndpoint() { // ... final Value v =;// value received from client final UpdateServicePortType updateServicePortType;// the endpoint updateServicePortType.execute(value); // ... // now to write back the modified value to the response }Problem here is the local value reference "v" points to a different location in memory. The parameter value in execute method has been changed to refer to a different object but the CXF Servlet is not aware of this. It would end up writing back the object referenced by v back to the calling client. This is wrong.
To prevent this a holder object is used with the reference to INOUT parameter placed as field inside the holder. If this field is now updated to refer to a different object, the Servlet will be made aware of the change because the reference value inside the Holder will refer to the new location. Simple and yet to so powerful. Same is the case with OUT parameters.
public void fetch( @WebParam(partName = "value", mode = WebParam.Mode.OUT, name = "value", targetNamespace = "http://ws.com/Service/xsd/holderws-schema") final Value value) { }In case of an OUT parameter the endpoint method must fill the OUT parameter and returns it to the client. Hence the need for the holder. Its inner value field will be null and the Endpoint will update it to refer to the correct value. I tested the above web service implementation:
@WebService(serviceName = "UpdateServicePort", portName = "UpdateServicePortType", targetNamespace = "http://ws.com/Service/holderws-ns", wsdlLocation = "holder.wsdl", endpointInterface = "com.ws.service.holderws_ns.UpdateServicePortType") public class UpdateServicePortTypeImpl implements UpdateServicePortType { private static final Logger LOG = Logger.getLogger(UpdateServicePortTypeImpl.class.getName()); public void execute(final Holder<Value> dataHolder) { LOG.info("Executing operation execute : value received - " + dataHolder.value); final Value value = dataHolder.value; value.setName(value.getName().toUpperCase()); } }The configuration:
<bean id="abstractLoggingInterceptor" abstract="true"> <property name="prettyLogging" value="true" /> </bean> <jaxws:endpoint id="holderService" implementor="com.ws.service.holderws_ns.UpdateServicePortTypeImpl" address="simpleUpdate"> <jaxws:inInterceptors> <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" parent="abstractLoggingInterceptor" /> </jaxws:inInterceptors> <jaxws:outInterceptors> <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" parent="abstractLoggingInterceptor" /> </jaxws:outInterceptors> </jaxws:endpoint>To test the above code I created a simple JAX-WS client:
public static void main(final String[] args) { final String endpointAddress = "http://localhost:8080/Holder/services/simpleUpdate"; final JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(UpdateServicePortType.class); factory.setAddress(endpointAddress); final UpdateServicePortType client = (UpdateServicePortType) factory.create(); final Value value = new Value(); value.setName("robin"); final Holder<Value> holder = new Holder<Value>(value); client.execute(holder); System.out.println("Server said: " + holder.value.getName()); }As seen here, there is no return type, the value from the holder element will point to the changed data received from server. The request and responses logged at the server are:
---------------------------- ID: 1 Address: http://localhost:8080/Holder/services/simpleUpdate Encoding: UTF-8 Http-Method: POST Content-Type: text/xml; charset=UTF-8 Headers: {Accept=[*/*], cache-control=[no-cache], connection=[keep-alive], Content-Length= [192], content-type=[text/xml; charset=UTF-8], host=[localhost:8080], pragma=[no-cache], SOAPAction=["/Service/update"], user-agent=[Apache CXF 2.7.4]} Payload: <?xml version="1.0" encoding="UTF-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <value xmlns="http://ws.com/Service/xsd/holderws-schema"> <name>robin</name> </value> </soap:Body> </soap:Envelope> -------------------------------------- ID: 1 Encoding: UTF-8 Content-Type: text/xml Headers: {} Payload: <?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <value xmlns="http://ws.com/Service/xsd/holderws-schema"> <name>ROBIN</name> </value> </soap:Body> </soap:Envelope>So if you are working with OUT or INOUT params, there is a very good chance you will come across Holders. With return data it is possible that the generated service will use a WebResult and Return type instead of an OUT parameter. But that will only work when your web service returns a single result. If there are multiple results, you are going to run into the OUT parameter and Holders. Heres an example of the scenario. With CXF we have seen that the entire handling or processing is done via interceptors.
Holder life cycle is also managed through interceptors only. In case of an inbound message:
109778 [http-bio-8080-exec-5] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Chain org.apache.cxf.phase.PhaseInterceptorChain@7eae1477 was created. Current flow: receive [PolicyInInterceptor, LoggingInInterceptor, AttachmentInInterceptor] pre-stream [CertConstraintsInterceptor] post-stream [StaxInInterceptor] read [WSDLGetInterceptor, ReadHeadersInterceptor, SoapActionInInterceptor, StartBodyInterceptor] pre-protocol [MEXInInterceptor, MustUnderstandInterceptor] post-protocol [CheckFaultInterceptor, JAXBAttachmentSchemaValidationHack] unmarshal [DocLiteralInInterceptor, SoapHeaderInterceptor] pre-logical [OneWayProcessorInterceptor] post-logical [WrapperClassInInterceptor] pre-invoke [SwAInInterceptor, HolderInInterceptor] invoke [ServiceInvokerInterceptor] post-invoke [OutgoingChainInterceptor]As seen here the HolderInInterceptor creates the Holders and puts them in the parameter list. It runs in the pre-invoke phase.
Similarly for the outbound messages:
109927 [http-bio-8080-exec-5] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Chain org.apache.cxf.phase.PhaseInterceptorChain@aca13df was modified. Current flow: setup [PolicyOutInterceptor] pre-logical [HolderOutInterceptor, SwAOutInterceptor, WrapperClassOutInterceptor, SoapHeaderOutFilterInterceptor] post-logical [SoapPreProtocolOutInterceptor] prepare-send [MessageSenderInterceptor] pre-stream [LoggingOutInterceptor, AttachmentOutInterceptor, StaxOutInterceptor] write [SoapOutInterceptor] marshal [BareOutInterceptor] write-ending [SoapOutEndingInterceptor] pre-stream-ending [StaxOutEndingInterceptor] prepare-send-ending [MessageSenderEndingInterceptor]The HolderOutInterceptor runs for the outbound message in the pre-logical phase. It pulls the values out of the JAX-WS Holder objects (created in HolderInInterceptor) and adds them to the param list for the out message.
Thanks!
ReplyDelete(Antonio)
Thanks, nice post
ReplyDeleteNice post Robin.
ReplyDeleteSome additional input:
http://tomee.apache.org/examples-trunk/webservice-holder/README.html
Hi. If we have a WebParam like this one: Holder> value, how should I set the value? I've got some troubles when the Web Service is trying to compose the XML in order to return a response.
ReplyDeleteThanks
Sorry, I mean, if the holder is a List
Delete