CXF ws client, dynamic endpoint and loading WSDL from the classpath

When you allow CXF to create the client code for a webservice (based on the WSDL), this creates code like the following.

@WebServiceClient( name = "IdentityService", targetNamespace = "http://www.prolixproject.org/is",
                   wsdlLocation = "file:/home/joachim/apps/java/ca/pxclient/src/main/wsdl/IdentityService.wsdl" )
public class IdentityService_Service
    extends Service
{
    public final static URL WSDL_LOCATION;
    public final static QName SERVICE = new QName( "http://www.prolixproject.org/is", "IdentityService" );
    public final static QName IdentityServiceImplPort =
        new QName( "http://www.prolixproject.org/is", "IdentityServiceImplPort" );

    static
    {
        URL url = null;
        try
        {
            url = new URL( "file:/home/joachim/apps/java/ca/pxclient/src/main/wsdl/IdentityService.wsdl" );
        }
        catch ( MalformedURLException e )
        {
            System.err.println(
                "Can not initialize the default wsdl from file:/home/joachim/apps/java/ca/pxclient/src/main/wsdl/IdentityService.wsdl" );
            // e.printStackTrace();
        }
        WSDL_LOCATION = url;
    }

    public IdentityService_Service( URL wsdlLocation )
    {
        super( wsdlLocation, SERVICE );
    }

    public IdentityService_Service( URL wsdlLocation, QName serviceName )
    {
        super( wsdlLocation, serviceName );
    }

    public IdentityService_Service()
    {
        super( WSDL_LOCATION, SERVICE );
    }

    /** @return returns IdentityService */
    @WebEndpoint( name = "IdentityServiceImplPort" )
    public IdentityService getIdentityServiceImplPort()
    {
        return super.getPort( IdentityServiceImplPort, IdentityService.class );
    }

    /**
     * @param features A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the features parameter will have their default values.
     * @return returns IdentityService
     */
    @WebEndpoint( name = "IdentityServiceImplPort" )
    public IdentityService getIdentityServiceImplPort( WebServiceFeature... features )
    {
        return super.getPort( IdentityServiceImplPort, IdentityService.class, features );
    }
}

This code represents two challenges.

  • The WSDL is loaded from the filesystem. While the location may be fine during development. It will likely fail once deployed.
  • The service endpoint is fixed. Depending on the deployment, this should be configurable (typically it will be different for testing and production).

The first problem seems weird. As the WSDL was needed to create the classes to access the service, why is it still needed at runtime? Apparently, this can be used for handling additional information which is allowed to change, typically extra header information like authentication.

As not all services provide the WSDL online, and you don’t want the WSDL to be searched on the file system (as this would require putting it in a specific location and assuring the location is also known in the application), the classpath sounds like the normal place to put this. If only “classpath:” URL’s would be supported. There is a solution using the classloader to supply the URL though (see code).

The next problem is that the WSDL defined the service endpoint location, but deployment may require this to be different in some cases. This is fixed by allowing the endpoint location to be overridden by a system property.

The client code (with some cleanup) then looks like this.

@WebServiceClient( name = "IdentityService", targetNamespace = "http://www.prolixproject.org/is",
                   wsdlLocation = "file:/home/joachim/apps/java/ca/pxclient/src/main/wsdl/IdentityService.wsdl" )
public class ProlixSso
    extends Service
{
    public final static URL WSDL_LOCATION;
    public final static QName SERVICE = new QName( "http://www.prolixproject.org/is", "IdentityService" );
    public final static QName IDENTITY_SERVICE_IMPL_PORT =
        new QName( "http://www.prolixproject.org/is", "IdentityServiceImplPort" );

    static
    {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if ( null == cl ) cl = ProlixSso.class.getClassLoader();
        WSDL_LOCATION = cl.getResource( "be/progs/ca/pxclient/wsdl/IdentityService.wsdl" );
    }

    public ProlixSso()
    {
        super( WSDL_LOCATION, SERVICE );
    }

    /** @return returns IdentityService */
    @WebEndpoint( name = "IdentityServiceImplPort" )
    public IdentityService getIdentityServiceImplPort()
    {
        IdentityService proxy = super.getPort( IDENTITY_SERVICE_IMPL_PORT, IdentityService.class );
        ( (BindingProvider) proxy ).getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, getUrl() );
        return proxy;
    }

    /**
     * @param features A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the features parameter will have their default values.
     * @return returns IdentityService
     */
    @WebEndpoint( name = "IdentityServiceImplPort" )
    public IdentityService getIdentityServiceImplPort( WebServiceFeature... features )
    {
        IdentityService proxy = super.getPort( IDENTITY_SERVICE_IMPL_PORT, IdentityService.class, features );
        ( (BindingProvider) proxy ).getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, getUrl() );
        return proxy;
    }

    private static String getUrl()
    {
        String res = System.getProperty( "ca.prolix.identityservice.url" );
        if ( null == res ) res = "http://testportal.prolix-dev.de/prolixservices/jbi/IdentityService/";
        return res;
    }
}

21 Comments

  1. jerrod says:

    Thx! Great article, helped me solve the dynamic endpoint issue I was having.

  2. Ted Burrett says:

    If you ever want to see a reader’s feedback 🙂 , I rate this post for four from five. Detailed info, but I have to go to that damn google to find the missed parts. Thanks, anyway!

  3. shiva says:

    why are you using
    String res = System.getProperty( “ca.prolix.identityservice.url” ); ?

    if I have other webservice what should I use to get res?

  4. joachim says:

    @shiva

    This gets the endpoint address. It is passed to the application on startup as a property (actually in my setup, in one of the deployed files in JBoss).

    You should replace the getUrl() method by something that suits your environment.

  5. Farrukh Masud says:

    Dear All,

    My problem is that, I have to dynamically Discover the web service (user will provide the URL for WSDL). Please help me as to how can I invoke such service using CXF. How will the classes be generated and used.

    Thanks and regards,
    Farrukh Masud

  6. joachim says:

    Dear Farrukh,

    You could generate at runtime and auto-deploy. However, how would you handle filling the parameters and digesting the results. I would guess it would be very difficult to handle generic web services for which the WSDL is not known in advance.

    If the wsdl has to comply to a given specification (only actual endpoint is different), the the solutions sketched here should work, possibly by extracting the endpoint address from the wsdl provided (should be possible with some (regex) search.

    Kind regards,
    Joachim

  7. Shailesh says:

    This was helpful thanks 🙂

  8. Seamus says:

    Im having a lot of trouble trying to get the url off the classpath instead of the file path. Anything I am missing? I am using CXF 2.2.7, JBoss 4.2.3, and JRE 1.5 and compiling it automagically with maven.

    Here is the error it throws:
    10:24:21,847 ERROR [STDERR] java.lang.ClassCastException: org.apache.cxf.jaxws.ServiceImpl
    10:24:21,847 ERROR [STDERR] at javax.xml.ws.Service.(Service.java:81)

  9. joachim says:

    @seamus

    You can put the wsdl file in src/main/resources and use an intialiser like the following

    
        static
        {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            if ( null == cl ) cl = ProlixSso.class.getClassLoader();
            WSDL_LOCATION = cl.getResource( "be/synergetics/ca/pxclient/wsdl/IdentityService.wsdl" );
        }
    

    That should work.

  10. Seamus says:

    does it have to be in src/main/resources? I currently have it in src/main/wsdl

  11. Seamus says:

    The wsdl location continues to look like this:
    file:/C:/jboss/jboss-4.2.3.GA/server/default/tmp/deploy/tmp23884mywar-exp.war/WEB-INF/classes/net/MyServiceSoap.wsdl

    (yes I am on windows, and I have my wsdl is src/main/resources/net) even using that initializer.

  12. FZ says:

    Hi,
    Thanks, this was invaluable. Was impossible to find the top level class in our project thanks to all the encapsulation.

  13. Thanks for some other great post. Where else could anyone get that kind of information in such an ideal method of writing? I’ve a presentation subsequent week, and I am at the search for such information.

  14. javaLearner says:

    Great! you saved my life dude!

  15. TXMaster says:

    I can’t read the post. Can someone help?

  16. Tejas says:

    Nice post. I got my classpath problem solved. But I am not getting the second problem. I am not able to find BindingProvider class. Do i need to add any jar to library?
    What is the fully qualified name of this class – BindingProvider ?

    (I’m using cxf wsdl2java 2.6.1)

    • james says:

      Axis 2 doesn’t have such problems. Simply generate the stub and pass in WSDL url and it works fine. Why CXF so complicated to use 😳

  17. sam says:

    I have followed the same steps, but getting the below error
    The Endpoint description validation failed to validate due to the following errors:
    :: Invalid Endpoint Interface :: :: The operation names in the WSDL portType do not match the method names in the SEI or Web service implementation class.

  18. misty says:

    I always get the
    javax.xml.ws.WebServiceException: Port not found. that’s why i came to here.

    I tried to follow the steps but still encountered the same error. 😥 Can someone help? 🙁

    Thanks in advance!

Leave a Reply

Your email address will not be published. Required fields are marked *

question razz sad evil exclaim smile redface biggrin surprised eek confused cool lol mad twisted rolleyes wink idea arrow neutral cry mrgreen

*