Something gave me the weird idea to try the new features of JAX-WS on a current project. The main idea was, to get rid of the code generators that must be ran whenever some minor change is done in the web service.
So instead of designing the WSDL along with the matching schema definition for the connector classes, I decided to start of by designing the interfaces and the connector classes, then just create the implementation with the corresponding annotations on the server side, that will be deployed automatically. The WSDL is generated from the annotations and the connector classes, there is no need to write them manually.
Take the following example. The web service has a simple method to query a list of items, that can throw a simple exception when the database is unreachable.
import java.util.List; import dummy.ws.type.Item; public interface ItemService { public List<Item> getItemList() throws ItemException; }
with the implementation example like:
import java.util.List; import dummy.ws.type.Item; import javax.jws.WebMethod; import javax.jws.WebService; @WebService(serviceName = "ItemService", targetNamespace = "http://dummy/ws", portName = "ItemPortType") public class ItemServiceImpl implements ItemService { @WebMethod public List<Item> getItemList() throws ItemException { // TODO implementation comes here } }
can be invoked with the following simple code
... Service service = Service.create(new URL(URLBASE + "/ItemService?wsdl"), new QName( "http://dummy/ws", "ItemService")); ItemService itemService = service.getPort(new QName( "http://dummy/ws", "itemPortType"), ItemService.class); List<Item> itemList = itemService.getItemList(); ...
There are several problems with this, even though it would be great to have it working right away. It could actually work, if there were no Collections involved on the return value. So creating a simple wrapper class for the return value like:
package dummy.ws.types;
import java.util.List; public class ItemList {
private List<Item> content; public ItemList() { }
public ItemList(List<Item> content) { this.content = content; }
public List<Item> getContent() { return content; } public void setContent(List<Item> content) { this.content = content; } }
along with the modification of the interface and of course the implementation to use the newly created class should break the ice.
There is another shortcoming to this method yet. The thrown exception must contain a constructor with (String, Object) parameters, and an Object getFaultInfo method. Took me quite a time to figure it out, as there is a huge gap in the documentation when you try this. Once you’ve done that you are ready to deploy.
In the project we are using WebSphere Application Server Community Edition, which is an Apache Geronimo based quite stable and fast server. The WAS CE uses Axis2 for web service implementation. The rich client we implemented used the latest Axis2 libs.
The whole thing worked well until the middle of the project, when all of a sudden, without any major change that we could trace, the client suddenly started throwing JAXB exceptions (classname nor any of its super class is known to this context). This could only be cured by creating the jaxb.index in the ws.types package with all the connector classnames listed in it, along with an ObjectFactory that has a XXX createXXX () method for each ws.type class. Intrestingly however this class, while mandatory is never touched by the code, the class is not even loaded!
On the whole this approach provided a really quick and a bit dirty way of using the web services in a single project. However I would not dare to use it on a bigger scale. I still consider starting from the WSDL and the schema, then generating the interfaces and the connector classes from them the best approach.