August 14, 2010

ApacheAxis & WSC Partner WSDL Sobject compared !

This post compares “com.sforce.soap.partner.sobject.SObject” generated by compiling Partner WSDL with WSC, to SObject class generated with Java libs like Apache Axis. This might be of interest for those developers who compiled Partner WSDL with other libs like Apache CXF etc, because this post explains the beauty of APIs exposed by WSC-SObject, so developers can compare with other compiled versions of Sobject too.

Apache Axis SObject vs WSC SObject

We will compare both on different criteria

Criteria 1: Creating SObject for a type+id and setting some field values

Here we will implement a simple API to create SObject with some Type, Id and fields using both WSC and Apache Axis. Here is the API signature/header

/**
 * Creates {@link SObject} for the given params
 * @param id SFDC Record ID
 * @param type Type of SObject 
 * @param fieldValues Map of field values to set in SObject. Here Map:Key is field name and Map:Value is the field value
 * @return The created Sobject with provided type,id and fieldValues.
 */
public static SObject createSObject(String id, String type,
      Map<String, Object> fieldValues) {
// .. 
// .. Code for Axis and WSC will go here
// ..      
}

Apache Axis Implementation

public static SObject createSObject(String id, String type,
      Map<String, Object> fieldValues) {
   SObject sObject = new SObject();
   sObject.setId(id);
   sObject.setType(type);
   List<MessageElement> messageElements = new ArrayList<MessageElement>();      
   for (String fieldName : fieldValues.keySet()) {   
      messageElements.add(newMessageElement(fieldName, fieldValues
            .get(fieldName)));
   }
   sObject.set_any(messageElements.toArray(new MessageElement[] {}));
   return sObject;
}

/*
 *  Helper method to create a single field value for Axis Sobject
*/
public static MessageElement newMessageElement(String name, Object value) {
   MessageElement me = new MessageElement("", name);
   try {
      me.setObjectValue(value);
      Element e = me.getAsDOM();
      e.removeAttribute("xsi:type");
      e.removeAttribute("xmlns:ns1");
      e.removeAttribute("xmlns:xsd");
      e.removeAttribute("xmlns:xsi");
      me = new MessageElement(e);
      return me;
   } catch (Exception e1) {}
   return null;
}

WSC Implementation

public static SObject createSObject(String id, String type,
      Map<String, Object> fieldValues) {
   SObject sobj = new SObject();
   sobj.setId(id);
   sobj.setType(type);
   for (String fieldName : fieldValues.keySet()) {
      sobj.setField(fieldName, fieldValues.get(fieldName));
   }
   return sobj;
}

Winner

For sure WSC with just 4 lines of code as compared to what we need to do with Apache Axis. With Apache Axis you have to do so much complex stuff. Even I have faced OutOfMemory error with Axis, when creating many SObjects all together, fix for that OOM error explained here

Criteria 2: Fetching field values from SObject already loaded via Partner.query(SOQL) call

Here we will use same SOQL query with both Axis and WSC, so that we can compare the ease of fetching attributes or relationships

SOQL :

static String SOQL = "Select FirstName, LastName, Account.Name, "
   + "AccountId, (Select Id, Title, Body From Notes) From Contact "
   + "where AccountId  != null and Id = 'your-contact-id'";

Apache Axis Implementation

public static void main(String[] args) {
  // .. create partner binding here
  // Assuming  you have already created a Partner Stub for Axis
  QueryResult qr = partnerStub.query(SOQL);
  // Contact Sobject
  SObject conSObj = qr.getRecords(0);
  // Axis gives Sobject fields in form of MessageElement instances
  MessageElement[] conFields = conSObj.get_any();
  System.out.println(" FirstName: " + parseText(conFields, "FirstName"));
  System.out.println(" LastName: " + parseText(conFields, "LastName"));

  // Load the Account lookup sobject from Contact.
  SObject accountSobj = (SObject) parseNestedSObj(conFields, "Account");
  MessageElement[] accFields = accountSobj.get_any();
  System.out.println(" Account-Name: " + parseText(accFields, "Name"));
  
  // For child relationships you get QueryResult again
  QueryResult notes = (QueryResult) parseNestedSObj(conFields, "Notes");
  for (SObject note : notes.getRecords()) {
    MessageElement[] noteFields = note.get_any();
    System.out.println("Note Id : " + note.getId());
    System.out.println("Note Title :" + parseText(noteFields, "Title"));
    System.out.println("Note Body :" + parseText(noteFields, "Body"));
  }

}

public static String parseText(MessageElement[] elements, String name) {
  for (MessageElement e : elements)
    if (name.equals(e.getName()))
      return e.getValue();
  return null;
}

public static Object parseNestedSObj(MessageElement[] elements,
    String parentName) {
  Object so = null;
  for (MessageElement e : elements)
    if (parentName.equals(e.getName()))
      so = e.getObjectValue();
  return so;
}

WSC Implementation

// Assuming Partner Connection Already created till here
QueryResult query = stub.query(SOQL);
SObject conSobj = query.getRecords()[0];
// getField() method is key to get anything from Sobject
System.out.println(" FirstName: " + conSobj.getField("FirstName"));
System.out.println(" LastName: " + conSobj.getField("LastName"));

XmlObject account = (XmlObject) conSobj.getField("Account");
System.out.println(" Account-Name: " + account.getField("Name"));

XmlObject notes = (XmlObject) conSobj.getField("Notes");
Iterator<XmlObject> notesC = notes.getChildren();
while (notesC.hasNext()) {
   XmlObject nextChild = notesC.next();
   if (!nextChild.getName().getLocalPart().equals("records")) continue; 
   System.out.println("Note Id : " + nextChild.getField("Id"));
   System.out.println("Note Title :" + nextChild.getField("Title"));
   System.out.println("Note Body :" + nextChild.getField("Body"));
}

Winner

For sure again is WSC, with just a single SObject->getField() method from WSC’s SObject, one can seamlessly work with every field like primitive, lookup and child relationships. With Axis you for sure need to create a utility class, that will have methods similar to parseText(), parseNestedObj() etc

Conclusion

You guys can easily say WSC is for sure a clear winner, with super easy to use API. This really saves life of a developer from writing wrapper classes or utils for working over Sobject. The behavior/methods exposed by SObject are really good. For ex. I really felt comfortable as developer in using the methods like

  • “SObject->setField("<FieldName>", <Value>)”  : Simply sets an attribute in Sobject.
  • “SObject->getField("<FieldName>")” : Retrieves the primitive values, lookups/references and child relationships of an Sobject field.