Search code examples
javaweb-servicesjakarta-eejax-wssoap-client

How is an object which does not subclass BindingProvider be casted explicitly into a BindingProvider object?


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
    ;

}

Solution

  • There are several ways to answer your question.

    From a high level perspective

    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 ?

    From the JAX WS specification

    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.

    What is a 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 SearchDocumentand 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.

    Back to your case

    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.