I have a web service where I'm manipulating the SOAP headers using the service interface object in the client API.
I needed to type cast the port object into a BindingProvider
object. But my port object does not directly subclass that class. So how is it possible that the JVM doesn't complain?
And it works too. No runtime error of ClassCastException
Code snippet:
public SearchDocument getSearchDocumentService(String wsdlUri, AuthBean auth){
SearchDocument_Service serv = null;
serv = SearchDocument_Service.getServiceInstance(wsdlUri);
SearchDocument searchDoc = serv.getSearchDocument();
populateAuthAndHandlerInfo((BindingProvider)searchDoc, auth);//how is it that jvm doesn't complain here
return searchDoc;
}
private void populateAuthAndHandlerInfo(BindingProvider port, AuthBean auth) {
Binding binding = port.getBinding();
List<Handler> handlerList = binding.getHandlerChain();
handlerList.add(new EDMSSoapAuthHandler());
binding.setHandlerChain(handlerList);
Map<String, Object> context = port.getRequestContext();
context.put("clientAuthInfo", auth);
}
SearchDocument.java:
@WebService(name = "SearchDocument", targetNamespace = "http://services.abc.com/Technology/SearchDocument/service/v1")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
ObjectFactory.class
})
public interface SearchDocument {
/**
*
* @param body
* @return
* returns com.abc.technology.search.document.client.v1.SearchOnMetadataResponse
* @throws AbcServiceException
* @throws AbcInvalidMessageException
* @throws AbcProducerApplicationException
*/
@WebMethod(action = "http://services.abc.com/Technology/SearchDocument/service/v1/soap11/SearchDocument/searchOnMetadata")
@WebResult(name = "SearchOnMetadataResponse", targetNamespace = "http://services.abc.com/Technology/SearchDocument/contract/v1", partName = "body")
public SearchOnMetadataResponse searchOnMetadata(
@WebParam(name = "SearchOnMetadata", targetNamespace = "http://services.abc.com/Technology/SearchDocument/contract/v1", partName = "body")
SearchOnMetadataRequest body)
throws AbcInvalidMessageException, AbcProducerApplicationException, AbcServiceException
;
}
There are several ways to answer your question.
But my port object does not directly subclass that class. So how is it possible the JVM doesn't complain ?
Well... SearchDocument
is an interface. So, when you call
SearchDocument searchDoc = serv.getSearchDocument();
Then searchDoc
is an instance of a given type that implements the interface, but it is nowhere specified what this concrete class is. It could be anything, including a concrete class that implements both SearchDocument
and BindingProvider
, because both are interfaces and any given type is free to implement multiple interfaces at the same time.
So this kind of must be what is happening here, right ?
Looking at the JAXWS spec may enlighten you further. you can download it at https://jcp.org/en/jsr/detail?id=224, but I'll quote a few nuggets for you, at chapter 4.2.3 :
Proxies provide access to service endpoint interfaces at runtime without requiring static generation of a stubclass. See java.lang.reflect.Proxy for more information on dynamic proxies as supported by the JDK
...
Conformance (Implementing BindingProvider): An instance of a proxy MUST implement javax-.xml.ws.BindingProvider
...
A proxy is created using the getPort methods of a Service instance: T getPort(Class sei) Returns a proxy for the specified SEI
Now it's easy to bring the pieces back together.
java.lang.reflect.Proxy
thing.In the JVM, there is an API for creating proxy objects. Proxy
objects (instances) are a special kind of beast in the JVM, that can be created dynamically, at runtime, and each proxy can be made to conform to any given interface. It's like creating a Class
at runtime, not at compile time, and without having to write its java source file. Of course, there are all kind of limitations, but also many possibilities.
One is to say to the JVM : "hey, give me a Proxy
object that implements the interfaces SearchDocument
and BindingProvider
at the same time". And so does the JVM. It gives you back an object, whose concrete class is Proxy
(usually Proxy$x where x is a number), and is specially crafted to implement both interface.
If you wonder if this is of any use at this point, then no it is not, because creating a type that has no implementation is kind of pointless. But there's a way to provide an implentation and behaviour, through what is called an InvocationHandler
that you program (exactly how is another discussion).
So, at this point, what we get from the specification is that if we call getPort
on a javax.xml.ws.Service
then, the result MUST be a JDK proxy, that MUST also implement BindingProvider
.
Which, I bet, is exactly what happens when you call : SearchDocument_Service
, it ultimately calls a getPort
method, and makes sure that the result implements BindingProvider
, because JAXWS says it must do so, and your SearchDocument
"business" interface, because it is what is usefull to you.