Wednesday, April 8, 2015

Generate a Java SOAP web service client using netbeans 8.0 and JAXB

The following post details the steps I followed when creating a web service client I used to test a remote web service. The web service basically is an exposed Oracle Service Bus proxy service.  

1. Start by creating a new netbeans 8.0 project without any specifics.
2. Identify the location of the remote wsdl. In my case I'll will use a service I developed before in OSB (Oracle Service Bus) located at http://10.112.107.23:11001/EllieMaeNetwork_v1/CreditService/Proxy/CreditService_ps?WSDL
3. Within your project folder right click, go to new and then to web service client.



4. Fill in the WSDL URL and click finish. This will generate the custom stubs for your java client. However if there are issues in the schemas referenced in the remote WSDL the tool wsimport will throw an error. Some examples of these issues are duplicated xml schema types. If all was successful then continue on the section CODING THE REQUEST, otherwise continue on point (5).


5. In my case I got an error when generating the web service client stubs.



There are multiple approaches to resolve these conflicts being the easiest to modify the original XML schemas and make sure names are not duplicated.  But sometimes you can't do that cause you are not the owner of the service.  However you can either create custom binding files or add custom jaxb annotation elements to the xml schemas.  This jaxb annotations are not being added to the original XML schemas but to the imported XML schemas that live locally where netbeans is and/or your project is.  Netbeans uses a folder named xmlresources within the project folder to place the referenced WSDL and XSD schemas.

Examples of the changes I did to the XML schemas are as follows


Original schema header


Modified schema header


Note the jaxb header added on the modified header.


Second Example

Original enumerated type


Modified enumerated type


Note the added element jaxb:typesafeEnumclass element. This will tell XJC to use specific class names when generating JAXB objects.


Third Example

Original complex type


Modified complex type


Note the jaxb:class element being used to tell wsimport to generate the java class as ADOONTypeRequest opposed to the default name CREDITREPORTINGADDONType.

6. Once you have added your custom JAXB elements within the XML schemas you are ready to refresh the web service client in order to generate the proper java classes. While doing it make sure to uncheck the option "Also replace local wsdl file with original located at:



Now in the output window you will see a result similar to the following.



CODING THE REQUEST

1. On the web service references locate the operation of the remote WSDL (web service) you would like to invoke. In my case this was labeled as requestCredit . See below.


2. Click on the operation and drag it to the code area. This will generate the required default methods. In this case the method requestCredit got generated as follows.


3. The only left task is to create an instance of the object (CREDITREPORTINGREQUESTGROUPType for my case) used in the request and call the generated method (requestCredit for my case).



ADD A CUSTOM HEADER (this is optional)

This is not a mandatory step but sometimes some web services require the client to pass additional information in the SOAP header.  Examples of these items might be security tokens or elements needed when calling asynchronous web services.  In my case I'm calling an asynchronous web service therefore following the ws-addressing header definitions two items are being sent.  See below.


Note:  I won't discuss ws-addressing concepts in this post, at the moment all you need to know is how to add these elements to the SOAP header being sent.

To add these custom SOAP header we need to add a handler class and a handlerResolver class.  The handler will create the header elements and the HandlerResolver will link the handler to the original request.

Implemented java interfaces are
- javax.xml.ws.handler.soap.SOAPHandler for the handler and
- javax.xml.ws.handler.HandlerResolver for the resolver.

SOAPHandler:

HandlerResolver:

Finally add the handler resolver to the original request.  Below I show the original request code and just below the modified with the handler resolver added.

Original as
private static CREDITREPORTINGRESPONSEGROUPType requestCredit(com.elliemae.emvps.credit.CREDITREPORTINGREQUESTGROUPType input) {
        com.elliemae.emvps.credit.CreditBindingQSService service = new com.elliemae.emvps.credit.CreditBindingQSService();
        com.elliemae.emvps.credit.CreditEndPoint port = service.getCreditBindingQSPort();
        return port.requestCredit(input);
    }

Modified as

private static CREDITREPORTINGRESPONSEGROUPType requestCredit(com.elliemae.emvps.credit.CREDITREPORTINGREQUESTGROUPType input) {
        com.elliemae.emvps.credit.CreditBindingQSService service = new com.elliemae.emvps.credit.CreditBindingQSService();
//add the following to support the wsa05 addressing header
        HeaderHandlerResolver resolver = new HeaderHandlerResolver();
        service.setHandlerResolver(resolver);
        com.elliemae.emvps.credit.CreditEndPoint port = service.getCreditBindingQSPort();
        return port.requestCredit(input);
    }