Salesforce.com Partner SOAP API JAX-WS Tutorial Part 4

This is Part 4 in a series. Read Part 1, Part 2 and Part 3 to catch up. Update: I’ve released an open source library that presents a better interface to the Partner API (as well as other Salesforce APIs).

Writing new and changed objects to Salesforce.com

There are three different API calls for writing data: create(), update() and upsert(). create() is like a SQL INSERT, update() is like a (surprise!) SQL UPDATE, and upsert() can either create or update a record depending on whether or not the object is detected as existing alraedy. We’ll stick with create() for the examples; you can check the API docs for the details on the different semantics for the other two calls.

Preparing SObjects for create()

For efficiency’s sake, create() takes an sObject[] parameter, which JAX-WS translates to a List<SObject> in the generated code. At most 200 objects can be created in each API call, though, so keep that in mind if you have large numbers of objects to create. As usual, we need to create a parameter object for the API call. We can then use that object to store the list of SObjects to create.

Create createParam = new Create();
List<sobject> stubSObjects = createParam.getSObjects();
// iterate over the data you want to create SObjects for and add to the list

Just like how we had to go to a little more trouble to extract data from the <any> data when reading results from a query() in Part 3, we have to take extra steps to format our object data so that it can be submitted as <any> data. We had to extract data from org.w3c.dom.Element objects to read it, so it stands to reason we need to do the inverse here. However, Element objects are created from a Document, which is created from a DocumentBuilder, which is created from a DocumentBuilderFactory. (The reason it’s so complicated is that it’s designed to allow alternate implementations of the DOM interfaces to be used without having to change code.) That’s quite a pile of classes, but it’s workable once there’s a starting point.

DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();

Creating a DocumentBuilderFactory is fairly slow since the aforementioned flexibility in choosing implementations requires reading various properties files to figure out which implementation to use, so you’ll want to cache that instance, or better yet, use it once to create a DocumentBuilder, and then cache that DocumentBuilder.

DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

Once we have the DocumentBuilder, we can get a Document easily.

Document doc = docBuilder.newDocument();

Note that the newDocument() method isn’t guaranteed to be thread-safe, so make sure to synchronize appropriately if you’re using the DocumentBuilder in a multi-threaded context.

Now that we can create Document objects, we have what we need to prepare field data in an SObject for the create() call. Setting other information (like the type) is self-explanatory, so I’m not showing how to do that in the example code.

Document doc = docBuilder.newDocument();
SObject stub = new SObject();
// set the type and other SObject data
...
// JAX-WS provides a getter to a mutable collection object instead of
// a setAny(), so we get that list and then modify it
List<object> stubFields = stub.getAny();
 
// loop over every field you want to have in the created SObject
String fieldName = ...
String fieldValue = ...
 
try {
    Element fieldElt = doc.createElement(fieldName);
    Node valueNode = doc.createTextNode(fieldValue);
 
    fieldElt.appendChild(valueNode);
} catch (DOMException e) {
    // handle the exception
}
 
stubFields.add(fieldElt);

The DOM operations can fail (thus throwing DOMException) if you try and do anything that can’t be turned into valid XML (e.g. use field names that cannot be valid XML tag names like ‘<&’). You can see a sample of what the eventual XML should look like on the Sample SOAP Messages wiki page.

Now that we know how to prepare one SObject, it’s simple to apply the same procedure to each SObject that needs to be submitted to create().

CreateResponse response = port.create(createParam);
List<saveResultType> stubResults = response.getResult();

For each SObject that was created, a SaveResultType object is returned that contains information about the success or failure when creating its corresponding SObject. (The same SaveResultType class is also used by update().) Each save result has three pieces of information:

  • The id of the object
  • A boolean representing success or failure for the corresponding SObject
  • A list of errors that have occurred

If the object was created successfully, the id will be non-null, the boolean will be true, and the list of errors will be empty. If the object was not created successfully, the id will be null, the boolean will be false, and the list of errors should be non-empty. Each error has a status code, a message, and a list of associated fields. The list of fields is sometimes left empty, though, even when an error actually is associated with fields, so don’t rely on the list being non-empty.

  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Twitter
  • Google Bookmarks
  • DZone
  • HackerNews
  • LinkedIn
  • Reddit
  • http://www.nowhereatall.net Brian Kessler

    While this tutorial series seems quite good, it is a little bit abstract.

    As I am a tactile learner, I find the tutorials which work best for me are those which have me coding together creating a specific application so I can see how everything comes together (i.e., the Salesforce tutorials show the reader how to put together a recruiting application).

    If you are inspired to continue this series, I don’t think I’d be alone in appreciating if we could build some sort of “mini-app” with all this.

    But in any event, thanks for putting this tutorial together!

  • Marshall Pierce

    Brian,
    I also find it easier to learn from tutorials that actually end up with something concrete. Unfortunately, writing such a tutorial for a Salesforce integration is a truly epic undertaking. Salesforce is such a big, complex system that to do a thorough job that covers all the nuances of Salesforce’s data model (converted leads, person accounts, objects that can be undeleted vs those that can’t, visibility and permissions, etc), I’d have to rewrite a fair amount of what the Salesforce docs already provide, which means my version would also go out of date fairly quickly. It’s easy to provide tutorials for integrations that ignore corner cases, but industrial-strength integrations require so much attention to detail that to write up an all-encompassing example would mean I wouldn’t have time to work on the real thing anymore. :)

  • Dave

    Thanks for all the great info. Our app is fairly simple as it just reads schema information and data from salesforce and was surprised that Axis1 read the data (44,000 records) 2x faster than JAX-WS. I tried setting batch size, using gzip and every aspect seemed slower.

    Does this make any sense?


    Dave

    • Marshall Pierce

      Are you using the partner wsdl or the enterprise wsdl? The enterprise wsdl is usually significantly faster.

  • http://www.mapikcha.com/ Arte

    First of all have to say “thank you” for a great tutorial!
    Now I run into problems with Enterprise WSDL and jax-ws.
    I was able to generate stub classes – no problems here.
    Login into system. Create new accounts & contacts.
    Now I am trying to Update account and something isn’t working.

    Code I used to create and account (Worked):

    private final ObjectFactory soF = new ObjectFactory();
    ….
    Account account = new Account();

    account.setName(soF.createAccountName(company.getCompanyName()));
    account.setAnnualRevenueC(soF.createAccountAnnualRevenueC(company.getAnnualRevenue()));
    account.setDescription(soF.createAccountDescription(company.getDescription()));
    ….
    accounts.add(account);

    List saveResults = null;
    try {
    saveResults = port.create(accounts);

    } catch (Exception e) {
    resp.setSuccess(0);
    handleException(e);
    }

    Now pretty much the same thing for Account update:

    List accounts = new ArrayList();
    Account account = new Account();
    ..
    account.setId(soF.createAccountMasterRecordId(company.getSalesforceCompanyID()));
    account.setCurrencyIsoCode(soF.createAccountCurrencyIsoCode(company.getCurrencyCode()));
    account.setName(soF.createAccountName(company.getCompanyName()));

    accounts.add(account);

    List saveResults = null;
    try {
    saveResults = port.update(accounts);

    } catch (Exception e) {
    resp.setSuccess(0);
    handleException(e);
    }

    … and it’s giving me following error message:
    ERRORS:Id not specified in an update call

    Please help, I spend entire day already trying to find where the problem is :(

    Unfortunately not too many people are using JAX-WS for SalesForce…

    • Marshall Pierce

      In the partner API, things work quite differently so I’m not exactly sure what the problem is here, but here are some general debugging tips:

      Try taking a look at the wire logging output by setting this boolean before running your code:
      com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump = true;

      There are some outdated sample soap messages here:
      http://wiki.developerforce.com/index.php/Sample_SOAP_Messages_%2810.0_API%29
      For basic usage, though, they are probably good enough to compare against.

      Also take a look at the Account element in your org’s enterprise WSDL and double check that you’re providing the object id in the correct way to map to what the WSDL expects.

  • http://www.mapikcha.com/ Arte

    Well… problem solved ;) Thanks to an Enterprise sample from

    http://wiki.developerforce.com/index.php/JAX-WS_Quick_Start

    Set ID for any class (Account, Contact, Etc.) you can with following statement:

    errorAccount.setId(soFactory.createSObjectId(“SLFKJLFKJ”));

  • Pingback: A new Java Salesforce API Library | Team Lazer Beez Blog