#2 Spring: Manipulating Salesforce objects

In Part 1 of this topic, we saw how to do OAuth authentication with Salesforce  using Spring. In this part, we’ll see how to fetch and update records  and upload documents to Salesforce using Force.com REST API.

Using Force.com REST API from Spring Web application – Part 2: Manipulating Salesforce objects

Force.com  REST API Resources are used to manipulate objects, execute queries,  display API information etc. Each resource is tied with a specific resource URI which is used to read, create or update record. All URIs  have common base URI: “http://domain/services/data”. Domain is usually instance URL retrieved during authentication, or a domain pointing to some Salesforce instance.

API Resources are listed in the following table. Link under the resource  name will take you to official API documentation for the resource.

URI column defines Resource URI that is appended to a base URI described above. vXX.X represents the version of the API you want to use.

Resource Name URI Description
Versions / Lists summary information about each Salesforce version currently available, including the version, label, and a link to each version’s root.
Resources by Version /vXX.X/ Lists available resources for the specified API version, including resource name and URI.
Describe Global /vXX.X/sobjects/ Lists the available objects and their metadata for your organization’s data.
SObject Basic Information /vXX.X/sobjects/SObject/ Describes the individual metadata for the specified object. Can also be used to create a new record for a given object.
SObject Describe /vXX.X/sobjects/SObject/describe/ Completely describes the individual metadata at all levels for the specified object.
SObject Rows /vXX.X/sobjects/SObject/id/ Accesses  records based on the specified object ID. Retrieves, updates, or  deletes records. This resource can also be used to retrieve field  values.
SObject Rows by External ID /vXX.X/sobjects/SObject/fieldName/fieldValue Creates new records or updates existing records (upserts records) based on the value of a specified external ID field.
SObject Blob Retrieve /vXX.X/sobjects/SObject/id/blobField Retrieves the specified blob field from an individual record.
SObject User Password
/vXX.X/sobjects/User/user id/password
/vXX.X/sobjects/SelfServiceUser/self service user id/password
Set, reset, or get information about a user password.
Query /vXX.X/query/?q=soql Executes the specified SOQL query.
Search /vXX.X/search/?s=sosl Executes the specified SOSL search. The search string must be URL-encoded.
Search Scope and Order /vXX.X/search/scopeOrder Returns an ordered list of objects in the default global search  scope of a logged-in user. Global search keeps track of which objects  the user interacts with and how often and arranges the search results  accordingly. Objects used most frequently appear at the top of the list.

REST Resource usage sample

In  this sample, we’ll list all Account names on Salesforce using Query  resource. From the table above, we can see how to form URI: “http://instanceURI/services/data/v20.0/query/?q=QUERY“.

QUERY is written using SOQL (Salesforce Object Query Language). To retrieve Account information we’ll use the following SOQL query: “SELECT name from Account“. Finally our URI will look like this: “http://instanceURI/services/data/v20.0/query/?q=SELECT+name+from+Account“.

Salesforce  response will be in either JSON or XML format, depending on the content  type set in request. JSON response for the given query will look like this:

{
    "done" : true,
    "totalSize" : 14,
    "records" :
    [
        { 
            "attributes" :
            {   
                "type" : "Account",   
                "url" : "/services/data/v20.0/sobjects/Account/001D000000IRFmaIAH" 
            }, 
            "Name" : "Test 1"
        },
        { 
            "attributes" :
            {   
                "type" : "Account",   
                "url" : "/services/data/v20.0/sobjects/Account/001D000000IomazIAB" 
            }, 
            "Name" : "Test 2"
        },
        ...
    ]
}

We can see that response contains an array of Account  objects data. We can see that there is the Name property specified in  the query and the Resource URL which can be used to retrieve complete  Account object.

Java Implementation

To help us leverage Force.com REST API we’ll use Apache Commons HttpClient library.

Getting Accounts from Salesforce

To  retrieve Account list, we’ll use the resource described in the previous  sample and execute it as GET method. Parts of code, like JSON parsing,  are left out for brevity.

First we’ll retrieve Access Token (#1) and Instance URL (#2) from session. These attributes are put in session during authentication with Salesforce. For details, refer to Part 1.

Next, we initialize the Apache HttpClient (#3) and create new GET method (#4) using obtained Instance URL and REST API Resource URI used to execute SOQL Query.

Now it is time to set Access Token to request header of the created GET method. It needs to be set using “Authorization” key, with value “OAuth <accessToken>” (#5).

SOQL query is set as query parameter (#6).

Everything is now prepared and we can execute the created GET method using HttpClient (#7).
We can now read the response (#8) and parse it manually or using some of the JSON parsing libraries (json.org, Jackson…).

private static final String SF_QUERY_URL = "/services/data/v20.0/query";

public List<Account> getAccounts(final HttpServletRequest request) throws HttpException, IOException, ServletException, SalesForceAuthException {

        String accessToken = (String) request.getSession().getAttribute(OAuthServlet.ACCESS_TOKEN); //#1
        String instanceUrl = (String) request.getSession().getAttribute(OAuthServlet.INSTANCE_URL); //#2
        List<Account> res = new ArrayList<Account>();
        HttpClient httpclient = new HttpClient(); //#3
        GetMethod get = new GetMethod(instanceUrl + SF_QUERY_URL); //#4

        // set the token in the header
        get.setRequestHeader("Authorization", "OAuth " + accessToken); //#5

        // set the SOQL as a query param
        NameValuePair[] params = new NameValuePair[1];
        params[0] = new NameValuePair("q", "SELECT Id, Name from Account LIMIT 100"); //#6

        get.setQueryString(params);

        try {
            httpclient.executeMethod(get); //#7
            if (get.getStatusCode() == HttpStatus.SC_OK) {
                InputStreamReader reader = new InputStreamReader(get.getResponseBodyAsStream()); //#8
                ...
            } else if(get.getStatusCode() == HttpStatus.SC_UNAUTHORIZED){ //#9
                throw new SalesForceAuthException();
            }
        } finally {
            get.releaseConnection();
        }
        return res;

    }

Reauthorization

It  is possible that the session on Salesforce expires and Access Token is  no longer valid. If this happens, Salesforce will return HTTP status 401 Unauthorized.  We check for this status after executing the request (#9 in the code  above) and throw custom exception which can be handled to reinitialize  authorization and retrieve new Access Token.

Uploading attachments to Salesforce objects

Working  with Salesforce Resources is similar to the code displayed above. On  the other hand, attaching files to some Salesforce object is somewhat  different, and we’ll see it in detail. The API Resource we are targeting  is, guess what – Attachment. Therefore, URI we are looking for will look like this: http://instanceURI/services/data/v20.0/sobjects/Attachment/

For attaching file to certain object, we’ll need following information:

  • Object ID – id of the object (e.g. Account) to which the file will be attached to,
  • Name – attachment name,
  • Description – attachment description,
  • Attachment file – the file that is being attached.

In the code below, these values are passed as method parameters.

To help us with the boilerplate code, we’ll again use Apache Commons HttpClient library to handle requests, Apache Commons Codec library to handle Base64 encoding and Apache Commons IO library to handle file manipulation. We’ll also use json.org classes to help us create request content.

First, we’ll create a byte array from file using apache IOUtils class ( #1).

We  will then create JSONObject (#2) which will be used as request content.  We’ll use it to store required parameters we are sending to Salesforce.  The attachment name is stored under “Name” key (#3), attachment description under “Description” key (#4). The file byte array needs to be encoded in Base64 scheme, and stored in content object using key “Body” (#5). Encoding to Base64 is done using already mentioned Apache Commons Codec  library. Finally, we need to tell the Salesforce to which object  attachment belongs, and this is done by specifying Object ID using “ParentId” key (#6).

The request content is now prepared, so we create new PostMethod using  Instance URI and API Resource URI (#7). We set Access Token (#8) same  as in previous example.

Finally, we set the request content using setRequestEntity method as StringRequestEntity using String representation of content created above and specifying content type as “application/json” (#9).

Everything is now ready, so we can execute created POST method (#10).

If the file is successfully uploaded, response status code is “201 Created” and response content will contain JSON containing “success” and “id” parameters. Id parameter contains ID of the created attachment (#11).

private static final String SF_ADD_ATTACHMENT = "/services/data/v20.0/sobjects/Attachment/";

public String uploadAttachment(final String objId, final String name, final String description, final File file, final HttpServletRequest request) throws FileNotFoundException, IOException, JSONException, SalesForceAuthException {

        String accessToken = (String) request.getSession().getAttribute(OAuthServlet.ACCESS_TOKEN); //#1
        String instanceUrl = (String) request.getSession().getAttribute(OAuthServlet.INSTANCE_URL); //#2

        // Read the file
        byte[] data = IOUtils.toByteArray(new FileInputStream(file)); //#1
        JSONObject content = new JSONObject(); //#2
        if (name != null) {
            content.put("Name", name); //#3
        }
        if (description != null) {
            content.put("Description", description); //#4
        }       

        content.put("Body", new String(Base64.encodeBase64(data))); //#5
        content.put("ParentId", objId); //#6
        PostMethod post = new PostMethod(instanceUrl  + SF_ADD_ATTACHMENT); //#7
        post.setRequestHeader("Authorization", "OAuth " + accessToken); //#8
        post.setRequestEntity(new StringRequestEntity(content.toString(), "application/json", null)); //#9
        String contentId = null;
        HttpClient httpclient = new HttpClient();

        try {
            httpclient.executeMethod(post); //#10
            if (post.getStatusCode() == HttpStatus.SC_CREATED) {
                JSONObject response = new JSONObject(new JSONTokener(new InputStreamReader(post.getResponseBodyAsStream())));
                if (response.getBoolean("success")) {
                    contentId = response.getString("id"); //#11
                }
            } else if(post.getStatusCode() == HttpStatus.SC_UNAUTHORIZED){
                throw new SalesForceAuthException();
            }
        } finally {
            post.releaseConnection();
        }

        return contentId;

    }

Conclusion

Using  Force.com REST API from Java/Spring application is not straight  forward, you do have to write not a small amount of boilerplate code,  but if you know how (and now you do 🙂 ) it shouldn’t be any problem to leverage the REST API, which provides really powerful way of manipulating Salesforce objects.

Tech Team

Author: Tech Team

When a couple of our Devs and TLs come together magic happens!

Leave a Reply

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