August 23, 2010

Salesforce Winter’11 Release – More guess work on whats coming next !

Salesforce releases are exciting for me, I am always eager to know whats coming next in this release. While writing some other blog post on “Chatter for Winter’11”, I was going thru Quinton’s blog post  on Salesforce blog. On a screen shot on this blog post, I noticed a few interesting things (might be related to Winter’11 release), those are highlighted in red circles in picture below

quinton2

In the above image following are some noticeable changes in user interface

  • The most significant change seems to be in sidebar search, its intelligently moved next to LOGO section in the page. I liked this change, sidebar search is mostly lost on custom Visualforce pages that demand huge horizontal layout/width. So having search section at top like this makes it more accessible for sure.
  • The user name “Quinton Wall” next to search section, is having a small pull down arrow next to it. I guess this will open some intelligent options around a user like
    • “Viewing profile” :  This will for sure save some horizontal space at top and even will negate necessity of a dedicated tab to view profile.
    • Setup and System log can be other two menu options in this pull down.
  • Change in look and feel of “Available apps” drop down in the top right. Its beautified to look something more than a normal dropdown.
  • No side bar search : If search section is at top next to logo, then side bar can be used for many other things.

Whats coming in Salesforce Chatter - Winter11 Release !

As a developer,  “Chatter” is something I love most, apart from other goodies like Sites etc given by force.com platform. So can’t resist my temptation to digg what’s next with Chatter in coming Winter’11 release. Regarding this I got good insights from

This post is brief of my guess work done from the above two pointers, regarding Winter’11 release for Chatter.

Winter’11 Chatter for Developers

New Visualforce Components

As indicated in blog post by Quinton, developers will get some cool visual force chatter components(“<chatter:……/>” ). I was thinking to develop some of these coming chatter components on my own, but they are not really straight forward to develop. This is because

  • The HTML/CSS/JS markup on top of which native chatter is running, is not that simple to create/hack/copy and convert to custom visualforce component.
  • Even if developed, we need to back chatter user actions with some Apex code like
    • Loading chatter feeds, this might go complex if you want to render all tracked changes and other updates correctly.
    • Creating new comments
    • Creating new posts.

Its really cool, that we don’t need to code or even think of this any more now, as with just one line of visualforce code below, we will get a chatter powerhouse embeded on the page.

<chatter:feedwithfollowers entityid="{!contact.id}" />

Even their are many other VF components and updates to existing VF components too like

  • <chatter:feeds> : Displays the Chatter feed on a record
  • <chatter:feedWithFollowers> : An integrated UI component that displays the Chatter feed on a record, as well as its list of followers.
  • <chatter:follow> : Renders a button for a user to follow and unfollow a Chatter record
  • <chatter:followers> : displays a list of Chatter followers for a record.
  • <apex:detail> : This component has been updated with a new “showChatter” attribute, which specifies whether the record should render as a Chatter record (display the show/hide bar etc) or not.
Idea - API Access to Profile Picture Version Parameter

This idea on idea-exchange is maturing to a feature in Winter’11 release. This idea is around a common mess created for developers if a user updates his profile photo. All versions of a user’s photo are maintained by salesforce and can be accessed by providing a version parameter to the profile image url, like v=1 as shown below

 <apex:image value="/userphoto?u={!userid}&v=1&s=T" height="{!height}" width="{!width}" title="{!name}" /> 

Here in the above url, parameter v=1 says “get me the version 1 of the photo”.  The things goes really dirty when a user uploads a new photo, now the version 1, that is usually the default image given by salesforce is no more valid. The native chatter interface picks the correct photo version, but interfaces rendered by visualforce code breaks and shows the old default image only. Even their is no API to know the latest version of a user photo.

So this idea heals all this, hopefully salesforce will give a UserInfo method to access the latest photo version. I guess the default behavior should be not to take any version in profile image URL and just get the latest. Its a very very rare requirement to get access to some older version of profile URL.

Winter’11 Chatter for Administrators (who are hopefully not coding :-D)

Their are a couple of cool ideas maturing to features like

  • Ability to package SObject history tracking fields: Currently one can enforce field history tracking support on an object, but there's no way for a application publisher to pick what fields are tracked. So it requires Admins to follow an install guide for a package and setup history tracking manually. So with maturing of this idea, this hassle is no more required.
  • Enable chatter feed tracking via package install : This idea is some what similar to the one above. Ability to package the feed tracking changes on Sobject’s fields, would again reduce manual intervention/effort by Admins after package installation.

 

Winter’11 Chatter for All

A few ideas make life easy for all i.e. developers, consultants and admins, like

  • Make Chatter Reportable : Having ability to report on Chatter activity can be super useful, specially for orgs where compliance and productivity are key measurables, there is a distinct need to be able to report on Chatter (by Feed, Object, profile, etc.).  This would allow us to maintain visibility into how the system is being used and provide documentation, whenever necessary, to regulatory bodies. 
  • Allow Chatter for Reports : Currently chatter is not available on reports, so any chats/discussions around a report are either in IMs, Emails etc. So having this ability will really make chatter useful, reports are really topic of debate and would be great if that debate can be done right on report via Chatter.

August 15, 2010

Salesforce Tolerado WSC on Google Code Now !

I am glad to announce that, this weekend I was able to wrap up the pending stuff of Tolerado-WSC integration. This project is now available on Google Code, link. Here are few useful pointers regarding Tolerado and WSC

 

In the end, I would like to express my sincere thanks to my wife Anjana, for giving me bandwidth on weekends and let me work on WSC research and Tolerado integration in family time.

August 14, 2010

WSC SObject handling Lookup, Child relationships and XPath queries !

This post explains how to best handle/access  primitives, lookups and child relationships, when working with Partner-Sobject compiled from Salesforce WSC WSDL complier.

For ease of explaining, a sample SOQL query that will include both a lookup and a child-relationship over Contact SObject will be used. Here is the query

/**
 * SOQL for loading contact with some fields, lookup and child relationship
*/
static String SOQL = "Select FirstName, LastName, Name, Account.Name, AccountId , (Select Id, Title, Body From Notes), (Select Id, Priority, CreatedBy.Name, CreatedById From Cases) From Contact where AccountId  != null and Id = 'recordid goes here'";

In above SOQL, we are accessing

  • Direct fields “FirstName” and “LastName”
  • Name field on “Account” lookup
  • Child relationship “Notes”
  • Child relationship “Cases” with next level child “CreatedBy” for Case

Next, will be showed a small code snippets to access each of the above via WSC Sobject.

Assumption : Before going to code snippets. I am assuming, you have already loaded SObject via Partner Query from your Java code. If you want to know how to do so, click here for samples. So I am assuming following java variable is created and correctly initialized

// WSC partnerConnection created with your credentials
QueryResult query = partnerConnection.query(SOQL);
SObject conSobj = query.getRecords()[0];

SObject->getField(“<FieldName>”) the ease accessor

The getField(“FieldName”) method is very useful method to load any of the direct attribute, lookup or child relationships. It returns Object instance, that can typically be casted for getting the correct results.

Accessing Direct Fields from SObject

Its simple direct method Sobject->getField(“<FieldName>”); does that straight. This method returns Object, so cast it to String.

String firstName = (String) conSobj.getField("FirstName");
String lastName = (String) conSobj.getField("LastName");
System.out.println(" FirstName: " + firstName);
System.out.println(" LastName: " + lastName);

Accessing Lookups from Sobject

// Again getField() is called, but this time it will return an XmlObject
// that will be used later to fetch the account name
XmlObject account = (XmlObject) conSobj.getField("Account");
// XmlObject has  getField() method to fetch further
String accountName = (String) account.getField("Name");
System.out.println(" Account-Name: " + accountName);

Accessing Child Relation Ships from Sobject

Child relationships are different from lookups, as they can have more than 1 record, so accessing them becomes little tricky. Again Sobject->getField(“RelationShipName”) can be used to get handle over the bunch of items in the child-relation. But its tricky because with WSC SObject implementation, a couple of extra items are returned with actual child(Note/Case record). These extra items are fields like “done”, “queryLocator” and “size”.

So a couple of different approaches can be used to access child relationships i.e. Notes or Cases from Contact SObject.

Accessing “Notes” Code Snippet – Approach 1

XmlObject notes = (XmlObject) conSobj.getField("Notes");
Iterator<XmlObject> notesItr = notes.getChildren();
while (notesItr.hasNext()) {
   XmlObject nextChild = notesItr.next();
	// Only children with "records" contain the real Note records, 
	// so skip rest like done, queryLocator and size etc
	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"));
}

Accessing “Notes” Code Snippet – Approach 2

This approach does the same using XPATH via SObject.evaluate(“XPATH”) method. Note we reduced if condition a bit. 

Iterator<XmlObject> children = conSobj.evaluate("Notes/records");
while (children.hasNext()) {
   // Note we don't need to skip anything, we got just what we need
   XmlObject nextChild = children.next();
   System.out.println("Note Id : " + nextChild.getField("Id"));
   System.out.println("Note Title :" + nextChild.getField("Title"));
   System.out.println("Note Body :" + nextChild.getField("Body"));
}

Accessing “Cases” and Case->CreatedBy User, Code Snippet – Approach 1

Iterator<XmlObject> children = conSobj.evaluate("Cases/records");
while (children.hasNext()) {
   XmlObject nextChild = children.next();   
   System.out.println(" CaseId " + nextChild.getField("Id"));
   System.out.println(" Case Priority :" + nextChild.getField("Priority"));
   // Note we again accessed the child(CreatedBy) of a child(Case)
   XmlObject caseCreator = (XmlObject) nextChild.getField("CreatedBy");
   // Access the name of second level child directly
   System.out.println(" CaseCreator " + caseCreator.getField("Name"));
}

XPaths for everything, Code Snippet – Approach 3

This approach shows how you can use XPATH, to dig into any level on an Sobject. For ex. xpath: "OpportunityContactRoles/records/Contact/LastName" this will list all LastNames.

// You can create Xpath to dig upto any level in Object graph. 
// So if you just need to Case Ids, its an easy xpath shortcut to that.
Iterator<XmlObject> allCaseIds = conSobj.evaluate("Cases/records/Id");
while (allCaseIds.hasNext()) {
   String caseId = (String) allCaseIds.next().getValue();
   System.out.println(" CaseId " + caseId);
}

WARNING about XPATH – SObject.evaluate() !

Using the SObject->evaluate() method canhurt performance of your application for large number of records in general or in child relation ships. This is because,as per current SObject code of evaluating xpath, its not caching the results i.e. all the object graph from root to leaf is scanned on every call.

Also, full xpath syntax is not supported yet on the SObject->evaluate() method. So path like Cases/records[1] etc will not work.

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.

August 5, 2010

Salesforce WSC ApacheAxis Session Sharing

Yesterday’s blog post explained how to best use Apache Axis and WSC stubs in parallel on same JVM or Project. This post reveals another small trick for developers using both WSC and Apache Axis WSDL2Java stubs together in a project/jvm. The trick focuses on reducing the number of login() call via Partner or Enterprise Stubs. This post might be of interest for those developers also, who have generated WSDL2Java stubs using other options like Apache CXF etc.

Secret sauce behind SFDC Session sharing

In general, the basic trick to reduce login calls, share sfdc session and use that session in prolonged way is to keep hold of two key SFDC artifacts i.e.

  • SESSION ID
  • SERVER URL

Both these are returned in “LoginResult” when you successfully complete a login() call via Partner or Enterprise stubs. It doesn’t matters using which tool you have compiled the salesforce wsdls, the key is to keep reusing these two artifacts.

These two artifacts are real beauty of salesforce web services too, I love the way you can share these artifacts across stubs, platforms and languages. For ex.

  • You have a PHP page that creates a SFDC session via login() call.
  • This PHP page delegates the complex processing stuff to some Java process.
  • Now, the PHP guy can safely share the same sessionid/server-url with Java process for re-use. Java process can start making web service calls without requiring a new login.

So, this is beauty because PHP guy, didn’t passed username/password to Java process.

OK, enough talking on basics. Now time for action and some code

Code Snippet - Reusing Apache Axis Session in WSC

NOTE: If you haven’t changed the java packaging during WSC WSDL compilation as suggested in this blog post, the sample below will not work because of package conflicts/ClassCast. I am also assuming you have included the WSDL2Java classes and required other jar’s in classpath. Didn’t included this intentionally to keep this post focused on the core topic.

First, the usual Apace Axis login() call using Partner.

// LoginResult from Apache Axis WSDL 2 JAVA stubs
SforceServiceLocator servLocator = new SforceServiceLocator();
SoapBindingStub axisStub = (SoapBindingStub) servLocator.getSoap();
// LoginResult is WSDL2Java class from Apache Axis.
LoginResult loginResult = axisStub.login(userName, password);

Now how to re-use the Apache Axis LoginResult created above in WSC

// Fetch the Server URL and Session from the Apache Axis login results
// Create new WSC Partner Connection
ConnectorConfig config = new ConnectorConfig();
config.setManualLogin(true);
// NOTE: We got server url from Apache Axis LoginResult
config.setServiceEndpoint(loginResult.getServerUrl());
// NOTE: We got sessionId from Apache Axis LoginResult
config.setSessionId(loginResult.getSessionId());
PartnerConnection partnerConn = Connector.newConnection(config);

Now putting all code snippets together, a complete fully functional sample code, that creates a Contact using WSC based on SFDC session created by Apache Axis. Please notice the import statements.

import com.sforce.soap.partner.LoginResult;
import com.sforce.soap.partner.SforceServiceLocator;
import com.sforce.soap.partner.SoapBindingStub;
import com.sforce.soap.partner.sobject.wsc.SObject;
import com.sforce.soap.partner.wsc.Connector;
import com.sforce.soap.partner.wsc.PartnerConnection;
import com.sforce.soap.partner.wsc.SaveResult;
import com.sforce.ws.ConnectorConfig;

public class AxisWSCTest {

public static void main(String[] args) throws Exception {
	// Set your user/pass here
	String userName = "";
	String password = "";

	// LoginResult from Apache Axis WSDL 2 JAVA stubs
	SforceServiceLocator servLocator = new SforceServiceLocator();
	SoapBindingStub axisStub = (SoapBindingStub) servLocator.getSoap();
	// LoginResult is WSDL2Java class from Apache Axis.
	LoginResult loginResult = axisStub.login(userName, password);

	// Fetch the Server URL and Session from the Apache Axis login results
	// Create new WSC Partner Connection
	ConnectorConfig config = new ConnectorConfig();
	config.setManualLogin(true);
	// NOTE: We got server url from Apache Axis LoginResult
	config.setServiceEndpoint(loginResult.getServerUrl());
	// NOTE: We got sessionId from Apache Axis LoginResult
	config.setSessionId(loginResult.getSessionId());
	PartnerConnection partnerConn = Connector.newConnection(config);

	// Create a new Contact
	SObject contact = new SObject();
	contact.setType("Contact");
	contact.setField("FirstName", "Abhinav");
	contact.setField("LastName", "Gupta");
	SaveResult[] create = partnerConn.create(new SObject[] { contact });
	for (SaveResult saveResult : create) {
		if (!saveResult.isSuccess()) {
			throw new RuntimeException(
					"Failed to save contact via Partner WSDL");
		}
	}
	System.out.println("Partner-WSDL : Save successfully done using WSC");

}

}

Thats it guys, any ideas/issues on using this. Please share !

August 4, 2010

WSC Apache Axis ClassCastException Issue Fixed !

This post is specially for those, having a Java Application(Production) working with SFDC web services via Apache-Axis stubs(WSDL2Java), but because of known advantages from WSC, you want to add some new functionality, like Bulk API as mentioned in SFDC blog here.

When we try to use both WSC and Apache Axis WSDL2Java stuff together in the same JVM or Project, we face Java Namespace(Package) clashes. You will certainly get Class-Cast errors like following on first run.

Exception in thread "main" java.lang.ClassCastException:
com.sforce.soap.partner.LoginResult cannot be cast to
com.sforce.ws.bind.XMLizable
	at com.sforce.ws.bind.TypeMapper.readSingle(TypeMapper.java:618)
	at com.sforce.ws.bind.TypeMapper.readObject(TypeMapper.java:495)
	at
com.sforce.soap.partner.LoginResponse_element.loadFields(LoginResponse_element.java:68)
	at
com.sforce.soap.partner.LoginResponse_element.load(LoginResponse_element.java:59)
	at com.sforce.ws.bind.TypeMapper.readSingle(TypeMapper.java:619)
	at com.sforce.ws.bind.TypeMapper.readObject(TypeMapper.java:495)
	at com.sforce.ws.transport.SoapConnection.bind(SoapConnection.java:154)
	at com.sforce.ws.transport.SoapConnection.receive(SoapConnection.java:128)
	at com.sforce.ws.transport.SoapConnection.send(SoapConnection.java:103)
	at com.sforce.soap.partner.PartnerConnection.login(PartnerConnection.java:791)
	at
com.sforce.soap.partner.PartnerConnection.<init>(PartnerConnection.java:315)
	at BulkExample.getRestConnection(BulkExample.java:171)
	at BulkExample.runSample(BulkExample.java:43)
	at BulkExample.main(BulkExample.java:34)

 

What creates this Namespace/Package hassle ?

When one has compiled the SFDC WSDLs using Apache Axis, Java classes get package names like  “com.sforce.soap.partner.*” for Partner WSDL. Similarly for WSC WSDL compilation we get the same package names for the WSDLs i.e. “com.sforce.soap.partner.*”. This happens because one has generated/compiled WSDLs using WSC with default options i.e.

java -classpath wsc.jar com.sforce.ws.tools.wsdlc wsdl jar.file

Nothing fails on compile time, but when you try running the code in parallel you get ClassCastException, I found one such issue posted on sfdc-wsc google code project.

Solution to this Package/Namespace conflict !

The solution to this issue is to specify your own prefix to Java packages when generating/compiling the WSDLs using WSC. So instead of using the default command above, just it to something similar below.

java -Dpackage-prefix=wsc -classpath wsc.jar com.sforce.ws.tools.wsdlc wsdl jar.file

Please note that I just added “-Dpackage-prefix=wsc” after the java command. Specifying this prefix will hint WSC WSDL2Java complier to generate all the Java classes with package name appended with “wsc”. So instead of java package “com.sforce.soap.partner.*”, new package name would be “com.sforce.soap.partner.wsc.*”. This will remove all namespace conflicts with Axis code in your project, now both Axis > LoginResult and WSC > LoginResult can both live happily together in the same JVM :)

In the similar fashion, I would suggest generating rest of the stubs for other WSDLs like Enterprise, Apex and Metadata. So that there are no chances on java package namespace conflicts.

Why change Packaging structure for WSC only ?

I suggested to change packaging with WSC only, though you can do vice versa too i.e. change packaging for Apache Axis generated classes. This was done because, I am assuming following scenario

  • You are maintaining and enhancing a Java App using SFDC Web Services in production.
  • So you have already done WSDL2Java for your Apache Axis code, and your code has several dependencies on the generated Axis code.
  • You just want to add some new features like Bulk API on that application using SFDC WSC.
  • So rather breaking existing Axis code, its better to bring new WSC WSDL2Java stuff with corrected non-conflicting namespaces.

In case you are just starting with your java application, I suggest use only WSC. Its really fast and easy to use.

 

References

August 3, 2010

Handling SFDC Managed Package Namespace Prefix with Web Service Clients !

This post tries to cover a common night mare while developing solutions on top of Salesforce using Partner/Enterprise WSDL web services. So this post can be super helpful for developers working with following technologies with Salesforce Partner/Enterpise WSDLs.

  • Java/J2EE/Spring, .NET, PHP, Perl Clients
  • A Flex application rendered inside a Visualforce page.
  • An Adobe Air desktop application

What is this Web Service issue ?

We (developers) usually develop our Java, PHP, Flex or .NET client code to make web service calls on the development org. But once the product comes close to release, we usually create a “Managed package” for it. Once managed package is created  all the CUSTOM Sobjects, Fields, etc gets a unique “PREFIX”.

From here (after getting the “prefix”) starts the real pain, all your “web service client code” that was working fantastically stable, bursts up on the very first and every consecutive call. Only solution to this problem is the web service client code needs to add the package prefix before every Sobject and Field names. But the equation becomes complex if the same code is running across a scenario where you can work with both prefixed and un-prefixed orgs. For ex.

  • Your development org is not prefixed / un-managed. So everything is available to web service client code un-prefixed. 
  • You push/deploy code(using Eclipse/ANT) to a QA org or STAGING org for testing, that is un-managed. But this org has an org wide prefix “qaorg__”. So this will prefix everything with “qaorg__”. Lets name this org as “QA” 
  • You create a managed package with prefix “mpac__”. This package will of course get  installed to client orgs or managed package testing orgs. So here everything will get prefixed by “mpac__”. Lets name this org as “MANAGED”
  • Now your web service stack has to deal with each of these orgs. So you can get any of these prefixes i.e. “no prefix”, “org prefix” or “managed package prefix”.  

 

How to crack this PREFIX problem ?

A couple of solutions are possible, please suggest if you have any other ideas.

  1. Create a new release of your web service client app for each package prefix. So
    • Java guys can have some ANT build properties to release an EAR or WAR file for each package prefix. Though maintaining different versions of same application might be hard.
    • Flex/Air/PHP code, it might not be an easy solution, unless you have already TOKENIFY all your .as, .php files to have all SOQL/SObject/Fields some token like “TOKEN__” before Sobject/Field/SOQL access. So for release you can run some file string replacement tool to change tokens and create a build. But this doesn’t sounds good to even me, and it can be super buggy :)
    • I will give this solution 2/10 marks.
  2. Using Custom settings to have the package prefix
    • This custom setting will have only a single field like “prefix”
    • This custom setting will be packaged(managed) separately from the main product. So your product can depend or assume that prior installation you have the custom setting managed package installed always.
    • You web service client code will first query for this custom setting to know the right prefix for an Org. Once that prefix is queried, all SOQL, Sobject and field names should be prefixed by that.
    • I will give this solution 3/10 marks.
  3. The 9/10 marks solution: Using “describeGlobal()” call via Partner/Enterprise WSDL to figure out the prefix dynamically and using that for prefixing the SOQL & Sobject Name/Fields. This solution is elaborated in details below. Note : I didn’t gave full marks to this solution, as I want more feedback and better/dynamic ideas if any.

 

Using “describeGlobal()” to determine the correct package prefix !

describeGlobal() call gives you a list of available objects for an organization. So one can then iterate through this list and use describeSObjects() to obtain metadata about individual objects. So here is the recipe to fetch the correct prefix.

  1. Mark any of your custom Sobject, that is of course part of your product as TOKEN. Lets say my custom object name is “Chatter_Location__c”.
  2. Make a describeGlobal() call to get the org’s describe and then list all available Sobjects in this describe via describeSObjects().
  3. For each available Sobject in iteration try matching TOKEN value with the Sobject name.
  4. For all the 3 SFDC orgs mentioned above, we will receive Sobject names as follows on making describeGlobal() call.
    • DevOrg : Chatter_Location__c
    • QA Org : qaorg__Chatter_Location__c
    • Managed Package Org : mpac__Chatter_Location__c.
  5. On having a match sub-string the prefix, so you will get either
    • “” Empty String > Dev Org
    • “qaorg__” > QA Org
    • “mpac__” > Managed package org.
  6. So, that is it guys. Now we have correct prefix in hand. We can cache this somewhere forever(usually will not change) and prefix the SOQL + Sobject Name/Fields.

Here is the sample code that shows the same recipe as above.

// Assuming  you have the right partner/enterprise binding to make the call.
DescribeGlobalResult describeGlobal = getBinding().describeGlobal();
// Token Sobject Name, this can be any Custom Sobject name from your product.
String TOKEN_SOBJECT_NAME = "Chatter_Location__c";
// Pull the list of Sobjects from the Global Describe
DescribeGlobalSObjectResult[] describeResults = describeGlobal.getSobjects();
int indexOfToken = -1;
// This variable will have the namespace prefix
String namespsacePrefix = null;
for (DescribeGlobalSObjectResult descSobjResult : describeResults) {
    String sobjName = descSobjResult.getName();
    if (sobjName != null) {
        // Try matching each Sobject Name with the TOKEN Sobject
        indexOfToken = sobjName.indexOf(TOKEN_SOBJECT_NAME);
    }
    
    if (indexOfToken != -1) {
        // If match is success, then substring the namespace prefix
        namespsacePrefix = sobjName.substring(0, indexOfToken);
        break;
    }
}

// Here we go, the final namespace prefix is here.!!!
System.out.println(namespsacePrefix);

 

Have any more ideas thoughts, please share !

August 1, 2010

Tolerado WSC integration close to complete !

I mentioned few days back about starting integration of Tolerado with WSC. The latest updates on this integration are

  • Tolerado-WSC is code complete [beta* :)]. I am testing the API now.
  • Junit test cases : I missed Junit test cases in the first Tolerado release for Apache Axis. But this time I am writing Junit test cases for all the WSDLs like partner, metadata, apex and enterprise. This will keep me more confident on ensuring stability of new Tolerado releases. Will migrate the same tests on Tolerado for Axis.

If you want to take a peek into the code, here is the link to the SVN source. This source is in form of complete eclipse project that you can directly checkout into Eclipse. How to checkout, please check this getting started guide.

Please tell me any new ideas or review comments, if you have on the WSC Tolerado Code.

 

References

For more info on “Tolerado”, please follow these pointers