Salesforce JAXB: Using wsimport to generate client artifacts

The Salesforce developer documentation provides JAVA examples based on WSC. WSC is a nice tool that makes interfacing to SFDC simple and easy, providing an EnterpriseConnection hiding all the details in calling SFDC.

But, WSC might not be for you:

  • Proprietary, not standards based
  • You might not be allowed to add third-party libraries to your application stack; or the process is too cumbersome…
  • Support, maintenance?

Unfortunately the wsimport tool that comes with the JDK does not “out of the box” like the Enterprise WSDL generated from Salesforce. Running it on the Enterprise WSDL from my test setup gives below output:

[jesper@linux3 ~]$ wsimport -keep enterprise.wsdl
parsing WSDL...
 
[WARNING] src-resolve: Cannot resolve the name 'tns:ID' to a(n) 'type definition' component.
  line 31 of file:/home/jesper/enterprise.wsdl#types?schema1
 
[ERROR] A class/interface with the same name "com.sforce.soap.enterprise.DescribeLayout" is already in use. Use a class customization to resolve this conflict.
  line 5952 of file:/home/jesper/enterprise.wsdl
 
[ERROR] (Relevant to above error) another "DescribeLayout" is generated from here.
  line 5620 of file:/home/jesper/enterprise.wsdl
 
[ERROR] Two declarations cause a collision in the ObjectFactory class.
  line 5952 of file:/home/jesper/enterprise.wsdl
 
[ERROR] (Related to above error) This is the other declaration.
  line 5620 of file:/home/jesper/enterprise.wsdl


Basically wsimport does not like the wsdl, but we are not in control of how SFDC generates this.

The errors can be fixed by adding the -B-XautoNameResolution option to the command line:

[jesper@linux3 ~]$ wsimport -keep -B-XautoNameResolution enterprise.wsdl
parsing WSDL...
 
[WARNING] src-resolve: Cannot resolve the name 'tns:ID' to a(n) 'type definition' component.
  line 31 of file:/home/jesper/enterprise.wsdl#types?schema1
 
Generating code...
 
Compiling code...

This works alot better. Except almost all attributes are of type JAXBElement<T>, making the structures difficult to deal with, and gives an api which is much different from what is generated by similar WSC tool.

By specifying a bindings file like below (e.g. called bindings.xjb) the generated java code is almost identical to that of the WSC tool:

<?xml version="1.0" encoding="UTF-8"?>
<bindings
    wsdlLocation="enterprise.wsdl"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://java.sun.com/xml/ns/jaxws">
    <bindings node="//xsd:schema[@targetNamespace='urn:sobject.enterprise.soap.sforce.com']">
      <jaxb:globalBindings generateElementProperty="false" underscoreBinding="asCharInWord"/>
    </bindings>
</bindings>

Run the wsimport tool with the bindings file:

[jesper@linux3 ~]$ wsimport -b bindings.xjb -keep -B-XautoNameResolution enterprise.wsdl
parsing WSDL...
 
[WARNING] src-resolve: Cannot resolve the name 'tns:ID' to a(n) 'type definition' component.
  line 31 of file:/home/jesper/enterprise.wsdl#types?schema1
 
Generating code...
 
Compiling code...

Below is an example of how to login and create an Account similar to this sample:

SforceService service = new SforceService();
Soap port = service.getSoap();
 
WSBindingProvider bp = (WSBindingProvider) port;
 
// Insert username here and password + key here
LoginResult loginResult = port.login("jesperudby@testmail.com", "password" + "QKlNGRgHdxE2Qx5KLdHTIP9Q");
 
String url = loginResult.getServerUrl();
Map<String, Object> rc = bp.getRequestContext();
rc.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
 
SessionHeader sh = new com.sforce.soap.enterprise.ObjectFactory().createSessionHeader();
sh.setSessionId(loginResult.getSessionId());
 
JAXBContext jc = JAXBContext.newInstance("com.sforce.soap.enterprise");
bp.setOutboundHeaders(Headers.create((JAXBRIContext) jc, sh));
 
Account acc = new Account();
acc.setCustomer_Code__C("00001");
acc.setName("Test Account 00001");
 
User user = new User();
user.setUsername("test-test@testmail.com");
acc.setAccount_Manager__R(user);
 
List<SObject> accounts = new ArrayList<>();
accounts.add(acc);
List<UpsertResult> response = port.upsert("Customer_Code__c", accounts);

Alternatively, one can specify the -XadditionalHeaders command line argument to the wsimport command, which generates operations where the different headers are specified as part of the call. This API is somewhat more complicated than the one created by the WSC tool, but enables you to specify the headers without using ws-specific classes/methods.

E.g.:

[jesper@linux3 ~]$ wsimport -b bindings.xjb -keep -B-XautoNameResolution -XadditionalHeaders enterprise.wsdl
parsing WSDL...
 
 
[WARNING] src-resolve: Cannot resolve the name 'tns:ID' to a(n) 'type definition' component.
  line 31 of file:/home/jesper/enterprise.wsdl#types?schema1
 
 
Generating code...
 
 
Compiling code...

The sample code then becomes:

SforceService service = new SforceService();
Soap port = service.getSoap();
 
LoginResult loginResult = port.login("jesperudby@testmail.com", "password" + "QKlNGRgHdxE2Qx5KLdHTIP9Q", null);
 
String url = loginResult.getServerUrl();
((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
 
SessionHeader sh = new com.sforce.soap.enterprise.ObjectFactory().createSessionHeader();
sh.setSessionId(loginResult.getSessionId());
 
Account acc = new Account();
acc.setCustomer_Code__C("00001");
acc.setName("Test Account 00001");
 
User user = new User();
user.setUsername("test-test@testmail.com");
acc.setAccount_Manager__R(user);
 
List<SObject> accounts = new ArrayList<>();
accounts.add(acc);
List<UpsertResult> response = port.upsert("Customer_Code__c", accounts, sh, null, null, null, null, null, null, null, null, null, null);

Above is tested with JDK 1.7_07 and using Glassfish 3.1.2 as ws runtime; wsimport -version reports JAX-WS RI 2.2.4-b01

About Jesper Udby

I'm a freelance computer Geek living in Denmark with my wife and 3 kids. I've done professional software development since 1994 and JAVA development since 1998.
This entry was posted in Java, Salesforce and tagged , , , . Bookmark the permalink.

2 Responses to Salesforce JAXB: Using wsimport to generate client artifacts

  1. Madhuri Kulkarni says:

    Thanks Jesper for this post. I am a total newbie to SalesForce and was looking for some example to create enterprise jar without using WSC-22.jar.
    The reason I needed a cleanerjar was we want to put that jar in the maven repository and do our java spring application build. The jar I created using WSC has some other sources from google and mozilla and that broke the build. I hope using the generated code using the above approach and then packaging it in a jar should fix our build.

    Thanks,

Leave a Reply

Your email address will not be published. Required fields are marked *