Category Archives: web services

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;
    }
}

CXF simple frontend, allow all SSL certificates and set basic authentication credentials

CXF is a wonderful web services framework. It is mostly configured using spring, however, this falls short when trying to assure that all SSL certificates are accepted. In this case, programmatic configuration is needed.

In the case where I needed this, SSL was used only to assure that the communication is encrypted at the transport level. Though the server certificate is normally used to assure the that it cannot be replaced without being noticed, this was not our concern. Specifically, self signed certificates are used, and there is no guarantee that they will not be changed.

In CXF the configuration of the transport is done by the conduit. The following snippet indicates how this can be accessed for the simple frontend.

        ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
        factory.setServiceClass( PingService.class );
        factory.setAddress( "https://localhost:8443/ca/pxws/1.0/ping" );
        PingService client = (PingService) factory.create();

        Client proxy = ClientProxy.getClient( client );
        HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
        TLSClientParameters tcp = new TLSClientParameters();
        tcp.setTrustManagers( new TrustManager[]{ new TrustAllX509TrustManager() } );
        conduit.setTlsClientParameters( tcp );

Similarly, the conduit can also be used to set the credentials which may be needed when the service is secured using basic authentication (as can be configured in web.xml).

The full code for the test is


package example.ws10.test;

import example.ws10.PingService;
import junit.framework.TestCase;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.AuthorizationPolicy;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.frontend.ClientProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.equanda.util.security.SslUtil;

import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;

/**
 * Test the Ping service
 *
 * @author <a href="mailto:joachim@progs.be">Joachim Van der Auwera</a>
 */
public class PingTest
    extends TestCase
{
    public void testPingService()
        throws Exception
    {
        ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
        factory.setServiceClass( PingService.class );
        factory.setAddress( "https://localhost:8443/ca/pxws/1.0/ping" );
        PingService client = (PingService) factory.create();
        Client proxy = ClientProxy.getClient( client );

        HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
        TLSClientParameters tcp = new TLSClientParameters();
        tcp.setTrustManagers( new TrustManager[]{ new SslUtil.TrustAllX509TrustManager() } );
        conduit.setTlsClientParameters( tcp );
        AuthorizationPolicy auth = conduit.getAuthorization();
        if ( null == auth ) auth = new AuthorizationPolicy();
        auth.setUserName( "local" );
        auth.setPassword( "local" );

        String res = client.getPing();
        assertTrue( res.startsWith( "Ping back @" ) );
    }

    /**
     * This class allow any X509 certificates to be used to authenticate the remote side of a secure socket, including
     * self-signed certificates.
     */
    public static class TrustAllX509TrustManager
        implements X509TrustManager
    {

        /** Empty array of certificate authority certificates. */
        private static final X509Certificate[] acceptedIssuers = new X509Certificate[]{ };

        /**
         * Always trust for client SSL chain peer certificate chain with any authType authentication types.
         *
         * @param chain the peer certificate chain.
         * @param authType the authentication type based on the client certificate.
         */
        public void checkClientTrusted( X509Certificate[] chain, String authType )
        {}

        /**
         * Always trust for server SSL chain peer certificate chain with any authType exchange algorithm types.
         *
         * @param chain the peer certificate chain.
         * @param authType the key exchange algorithm used.
         */
        public void checkServerTrusted( X509Certificate[] chain, String authType )
        {}

        /**
         * Return an empty array of certificate authority certificates which are trusted for authenticating peers.
         *
         * @return a empty array of issuer certificates.
         */
        public X509Certificate[] getAcceptedIssuers()
        {
            return ( acceptedIssuers );
        }
    }
}
Consolidation debt loan personal unsecured
Unsecured credit card debt consolidation
Compare consolidation loan student
California equity home loan mortgage
Bad credit home loan score
Personal loan for college student
Bank personal loan for bad credit
Application california home loan mortgage
Personal loan
Sallie mae student loan
New york home loan
South carolina debt consolidation
Free debt consolidation company
Maryland debt consolidation loan
Va home loan
Card consolidation credit debt unsecured
Michigan debt consolidation
Equity georgia home loan rate
Consolidation debt fee free no
Consolidating private student loan
Ten dollar payday loan
Bad credit equity home loan no
Credit card depot and debt consolidation
Equity loan home improvement
Bad credit home loan washington
Bad credit personal loan uk
Mortgage calculator home loan refinance
Fixed home equity loan
Iowa student loan liquidity corporation
D ford loan student william
California equity home loan southern
Federal direct student loan consolidation
Debt consolidation affiliate program
California home loan veteran
1000 loan no payday telecheck
Home improvement loan hud
Citibank student loan consolidation
Best consolidation debt service
Debt consolidation loan without owning a home
Loan repayment student
No telecheck payday loan
Beneficial personal loan
Poor credit georgia home loan
Easy online payday loan
Personal debt consolidation loan uk
Bad credit debt consolidation mortgage
Student loan for people with bad credit
Home equity loan refinance credit
Va home loan interest rate
Online payday cash loan
Washington mutual bank home loan
North carolina home equity loan
Canada loan student
Debt consolidation help
Application loan online personal
Card debt consolidation
Student loan consolidation program
Current home interest loan rate
Bank federal finance home loan office
Consolidation credit debt loan
Advance cash loan military payday
Personal car loan
Bad colorado credit home loan
Loan mae sallie signature student
Card consolidation credit debt management
Citibank student loan
Card consolidation credit debt debt
Consolidation debt uk
Advance cash fast loan payday
Consolidation debt loan uk unsecured
Acs student loan consolidation
Advance cash loan payday
Broker consolidation debt lead mortgage
Kansas city home equity loan
New york mortgage home loan
Advice consolidation loan student
Card consolidation credit debt loan relief

“simple” web services using CXF deployed in JBoss

I needed web services up and running, and after a short look at what looks like the best framework for implementing web services, I ended up choosing CXF.

Now though the standalone demo is pretty simple (as explained on the site), deploying this in JBoss is somewhat more difficult. This is partly caused by difficult to find documentation, and partly by possible clashes caused by the classloader (some libraries you really don’t want to have in your classpath twice).

Let’s start by running the demo, then explain what I did and why I made some of the choices I made.

I have a small project which implements this little demo that you can download. You have to fix some of the settings in the main pom.xml file. Specifically the details of your JBoss installation which can be found in the properties in the dev-example profile.
You also need a working JBoss instance, with cargocpc in it. One option is downloading this tuned configuration. I would recommend you to remove the “jbossws.sar” directory from deploy. This contains a different web services stack. However, the demo does not include the CXF libraries, so these need to be added in your deploy directory. The easiest way is to download and extract cxf-2.0.5.sar.tgz.

When these changes are done, thanks to maven, you can run and test the simple “ping” web service by typing
mvn -Dfulltest -Ddev=example install

The actual service and implementation are very simple

package cxfdemo.ws10;

public interface PingService
{
    String getPing();
}

package cxfdemo.ws10.impl;

import cxfdemo.ws10.PingService;

import java.sql.Date;

public class PingServiceImpl
    implements PingService
{
    public String getPing()
    {
        return "Ping back @" + new Date( System.currentTimeMillis() );
    }
}

To assure this works, the following web.xml is provided

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/cxfdemo/ws10/cxf.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <display-name>Prolix Web Services 1.0</display-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

This in turn refers to the following cxf.xml configuration file

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:simple="http://cxf.apache.org/simple"
       xmlns:soap="http://cxf.apache.org/bindings/soap"
       xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://cxf.apache.org/simple http://cxf.apache.org/schemas/simple.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <simple:server id="ping"
                   serviceClass="cxfdemo.ws10.PingService"
                   address="/ping"
        >
        <simple:serviceBean>
            <bean class="cxfdemo.ws10.impl.PingServiceImpl"/>
        </simple:serviceBean>
        <simple:dataBinding>
            <bean class="org.apache.cxf.aegis.databinding.AegisDatabinding"/>
        </simple:dataBinding>
    </simple:server>

</beans>

Running and testing this bean can be done using this simple program.

package cxfdemo.ws10.test;

import cxfdemo.ws10.PingService;
import junit.framework.TestCase;
import org.apache.cxf.frontend.ClientProxyFactoryBean;

public class PingTest
    extends TestCase
{
    public void testPingService()
        throws Exception
    {
        ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
        factory.setServiceClass( PingService.class );
        factory.setAddress( "http://localhost:8080/cxfdemo/ws/1.0/ping" );
        factory.getServiceFactory().setDataBinding( new AegisDatabinding() );
        PingService client = (PingService) factory.create();
        String res = client.getPing();
        assertTrue( res.startsWith( "Ping back @" ) );
    }
}

This all is very short and compact.

Why the projects is built like this.

  • The project does not include the libraries as JBoss can cause problems when a library is in the classpath more than once. “commons-logging” is a typical example of this.
    This also has the advantage that the deployments don’t need to include the libraries again.
  • The service interface is put in a separate module than the implementation. This allows easy distribution of the interfaces in java form (which for many people is more readable than wsdl files).
  • The project is built to allow compiling and installing to work without running the full tests. However, you can run them if you want to (using the fulltest profile).
  • There is a jboss web services implementation which uses CXF. However, this was not used here as this does not include the “simple” CXF interface, only the JAX-WS interface.
  • The web services module names, interface packages and URL includes a version identifier. This is part of the the way the project is built. When all the interfaces are in one module, then it should be possible to keep the services stable (same WSDL) even when the actual backend code changes. When incompatible changes are required, you can just create anoth set of modules (one interface, one implementation, and provide the changes there.