Quick summary of how to get started with the Enterprise WSDL API via Java’s JAXB

With JAX-WS and JAXB part of Java 6, using JAXB to handle the marshalling needed to access an org through the Enterprise WSDL API makes sense because no JARs in addition to the JRE are needed. I found this excellent series of articles Salesforce.com Partner SOAP API JAX-WS Tutorial by Marshall Pierce that covered most of what I needed (including the use of compression – very nice).

There were only two extra things to deal with:

  • The technique for setting the session id didn’t work for me so I used the wsimport tool’s -XadditionalHeaders option that allows SOAP headers (including the SessionHeader that holds the session id) to be passed in the method calls.
  • Unlike the Partner WSDL where a generic interface is used to access the SObject fields, using the Enterprise WSDL results in a class with getters and setters being generated for each SObject (strongly typed access). By default the setters and getters for primitive values like Strings were using a JAXBElement generic making for tedious coding and a crazily large ObjectFactory. Setting a generateElementProperty=”false” customization at generation time resulted in the more normal direct setters and getters.

Here is the summary part; there are just three pieces of more or less boilerplate needed…

One – the code generation (here just invoking the wsimport command-line tool through Ant):

<target name="wsimport" description="JAXB code generation">
    <exec executable="/usr/bin/wsimport">
        <arg value="-XadditionalHeaders"/>
        <arg value="-Xnocompile"/>
        <arg value="-b"/>
        <arg value="jaxb-customization.xml"/>
        <arg value="-verbose"/>
        <arg value="-d"/>
        <arg value="src-generated-jaxb"/>
        <arg value="-p"/>
        <arg value="com.sforce.soap.enterprise"/>
        <arg value="src/com/claimvantage/force/client/xml/enterprise.wsdl"/>
    </exec>
</target>

Two – the jaxb-customization.xml customization file used by the code generation:

<bindings 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:enterprise.soap.sforce.com']">
		<jaxb:globalBindings
		      underscoreBinding="asCharInWord"
		      generateElementProperty="false"
		      />
		<jaxb:schemaBindings>
			<jaxb:nameXmlTransform>
				<jaxb:typeName suffix="Type" />
			</jaxb:nameXmlTransform>
		</jaxb:schemaBindings>
	</bindings>
	<enableWrapperStyle>false</enableWrapperStyle>
	<enableAsyncMapping>false</enableAsyncMapping>
</bindings>

Three – an example of how to login and use the API (imports left out);

public class Processor {
    private Soap soap;
    private SessionHeader sessionHeader;
    private QueryOptions queryOptions;
    public Processor(String un, String pw, String st) throws Exception {
        connect(un, pw, st);
    }
    public void process() throws Exception {
        // Implement your logic here. In general case use Query/QueryMore to handle large sets of data
        List<SObject> sobs = soap.query(createQuery("select Birthdate from Contact where LastName = 'Smith'"),
                sessionHeader, queryOptions, null, null).getResult().getRecords();
        for (SObject sob : sobs) {
            Contact contact = (Contact) sob;
            System.out.println(contact.getBirthdate());
        }
    }
    private Query createQuery(String soql) {
        Query query = new Query();
        query.setQueryString(soql);
        return query;
    }
    private void connect(String un, String pw, String st) throws Exception {
        // WSDL must be packaged with code
        String wsdlPath  = "enterprise.wsdl";
        URL url = this.getClass().getResource(wsdlPath);
        if (url == null) {
            throw new Exception("Could not access WSDL from path " + wsdlPath);
        }
        // This triggers a lot of class loading
        SforceService service = new SforceService(url, new QName("urn:enterprise.soap.sforce.com", "SforceService"));
        soap = service.getSoap();
        Login login = new Login();
        login.setUsername(un);
        login.setPassword(pw + st);
        LoginResponse loginResult = soap.login(login, new LoginScopeHeader());
        BindingProvider b = (BindingProvider) soap;
        b.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, loginResult.getResult().getServerUrl());
        // Use compression for speed
        Map<String, List> httpHeaders = new HashMap<String, List>();
        httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
        httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
        b.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);
        // This must be passed with each call
        sessionHeader = new SessionHeader();
        sessionHeader.setSessionId(loginResult.getResult().getSessionId());
        // Up to this many will be returned but may be less
        queryOptions = new QueryOptions();
        queryOptions.setBatchSize(2000);
    }
}
About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s