tags:

views:

267

answers:

3

We are using Axis2 in our enterprise currently. Our project involves making call to multiple webservices for fault diagnostics. The problem is that very frequently we have changes in WSDLs (which are non-life threatening as in new data-types and services just get added, very rarely is something dropped) due to which we have to upgrade our application as well. Basically, we have a to get a new copy of the WSDL, run it through WSDL2java and use the new jars, run our unit tests and package the new jars and install on production.

Although the capability of generating the stubs at compile team gives us xmlbeans with which we can work easily in our java DAO layer code, this compile-deploy cycle due to WSDLs consume the team's time. I was wondering whether there is any change possible? Is there any Java APIs which can generate stubs at run-time or provide stub-less web service invocation, but still give us ability to work with java objects rather than handcrafting request documents? Something like this soap test site. I should just be able to specify a WSDL location and I should just get an object with which I can access the document (both request and response) and also be able to change the WSDL location at run-time?

Not sure whether this type of run-time behavior is possible in Java since objects generated at run-time would have different types etc?? not sure... I have seen some Groovy examples which come close to what I want but using Groovy means an architectural change for us.. and that's a bit tough... Is there any Java library/API available?

+1  A: 

The Eclipse Web Service Explorer builds a UI client of an arbitrary WSDL on the fly. So I guess that what you want to do is possible if you're prepared for your app to use a bit of reflection to invoke the services.

As Eclipse is open source you may be able to study how the explorer is implemented and use ideas?

On the other hand, can you automate your build with ant so that if a new WSDL arrives it just gets processed automatically, benign changes would just be silently absorbed. My instict is that this is a better approach.

djna
A: 

Not entirely stubless, but a bit more independent Spring's JaxWsPortProxyFactoryBean. You supply the service interface with the methods your code actually uses. You can supply the URL to connect to using a property. If the service's location changes, or more methods are added (that your code does not use yet), no regeneration, compile or packaging is needed.

If you get a new version of the WSDL that you want to change your code to use the new operations just re-run wsdl2java like you do today to get new beans and a new service interface. So you only need to do that work when you are changing your code anyway use new features.

Fried Hoeben
A: 
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;

/**
 * This class creates SOAP clients from WDSL and a Java interface.
 * See http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JAXRPC5.html
 * <p>
 * Consider the following sample usage:
 * <pre>
    URL            wsdl = new URL("http://localhost:8080/calc/calc?wsdl");
    String  serviceName = "CalculatorWSService";
    String nameSpaceURI = "http://calculator.me.org/";
    String     portName = "CalculatorWSPort";
    Calculator     calc = SOAPClient.newInstance(wsdl, nameSpaceURI, serviceName, portName, Calculator.class);
 * </pre>
 * @author Curt
 */
public final class SOAPClient {

    /**
     * Create a new SOAPClient, given the specified parameters.
     * @param url where the WSDL is
     * @param nameSpaceUri
     * @param serviceName
     * @param portName
     * @param face interface to use
     * @return an object that implements the interface and is connected to the server
     */
    public static <T> T newInstance(
        URL url, String nameSpaceUri, String serviceName,
        String portName, Class<T> face)
        throws RemoteException
    {
        try {
            QName portQname    = new QName(nameSpaceUri, portName);
            QName serviceQname = new QName(nameSpaceUri, serviceName);
            Service service = Service.create(url, serviceQname);
            T remote = service.getPort(portQname,face);
            T proxy = face.cast(remote);
            return proxy;
        } catch (Throwable t) {
            String message =
                "Connecting to URL=" + url +
                " name space URI= "+ nameSpaceUri +
                " service name=" + serviceName +
                " interface=" + face +
                " port=" + portName;
            throw new RemoteException(message,t);
        }
    }

    /**
     * Don't specify the portName and trust that the service will do it.
     */
    public static <T> T newInstance(
        URL url, String nameSpaceUri, String serviceName, Class<T> face)
        throws MalformedURLException, RemoteException
    {
        QName serviceQname = new QName(nameSpaceUri, serviceName);
        Service service = Service.create(url, serviceQname);
        T remote = service.getPort(face);
        T proxy = face.cast(remote);
        return proxy;
    }
}

The underdocumented Service class might provide what you're looking for.

Curt Cox
Do you know if that works when new fields, which are not represented in Java, are added to a response message?
Christian Strempfer