Saturday, May 31, 2008

Webservice endpoint

The Java JAX-RPC implementation used to create simple to use web service clients, where developer can easily change the web service endpoint as shown below-

MyWsImpl stub = new MyWsImplService_Impl().getXXX();
((Stub) stub)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, "http://myHost/myWs/myWs");

See this entry for details about building JAX-RPC web services.

The JAX-WS has changed the way it generates the clients. As a general practice people keep the WSDL along with their code and create the client side stubs at the build time. The JAX-WS hard codes the WSDL path in the client stub. See the one such generated code below-
package cache;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceClient;

@WebServiceClient(name = "MyServiceImplService", targetNamespace = "http://c.b.a/", wsdlLocation = "file:/D:/Test/temp/wsdl/MyServiceImplService.wsdl")
public class MyServiceImplService extends Service {

private final static URL MYSERVICEIMPLSERVICE_WSDL_LOCATION;
private final static Logger logger = Logger
.getLogger(MyServiceImplService.class.getName());

static {
URL url = null;
try {
URL baseUrl;
baseUrl = MyServiceImplService.class.getResource(".");
url = new URL(baseUrl,
"file:/D:/Test/temp/wsdl/MyServiceImplService.wsdl");
} catch (MalformedURLException e) {
logger.warning("Failed to create URL for the wsdl Location: 'file:/D:/Test/temp/wsdl/MyServiceImplService.wsdl', retrying as a local file");
logger.warning(e.getMessage());
}
MYSERVICEIMPLSERVICE_WSDL_LOCATION = url;
}

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

public MyServiceImplService() {
super(MYSERVICEIMPLSERVICE_WSDL_LOCATION, new QName("http://c.b.a/",
"MyServiceImplService"));
}
}
If one tries to create an instance of client using the default constructor then it looks for the WSDL file at the location hard coded in the class and might fail with FileNotFoundException. The workaround for this problem is to call the another consturcutor, passing the endpoint URL and QName. Creating this QName is quite annoying and also that may change in future changes to WSDL. Then what is the easy wayout?

If you look closely at the web service client, it has an annotation @WebServiceClient, which has all the information to create the QName dynamically.
WebServiceClient ann = MyServiceImplService.class
.getAnnotation(WebServiceClient.class);
MyServiceImplService service = new MyServiceImplService(
new URL("ENDPOINT URL OF MY SERVICE"),
new QName(ann.targetNamespace(), ann.name()));

Now it looks pretty simple, is not it?

20 Comments: