Connecting to the Salesforce REST APIs with Spring Boot and Java

Broadly speaking there are two types of integrations with Salesforce, either a system-to-system integration or a user interface integration. One of the primary ways to do these integrations is by using the Salesforce REST API. When using the Salesforce REST API you need to obtain an access token that identifies who is making the requests. OAuth 2 provides an HTTP interface to obtain a Salesforce access token.

When using the Salesforce OAuth 2 API there are three options for obtaining an access token:

  1. Use the Web Server Flow where a Salesforce user in a traditional web app is asked to authorize a third party application which then allows the web server to obtain an access token.
  2. Use the User Agent Flow where a Salesforce user in a mobile or JavaScript web app is asked to authorize a third party application which then allows the client side application (native mobile, JavaScript, etc) to obtain an access token.
  3. Use the Username and Password Flow where stored credentials for a system-to-system integration user are exchanged for an access token.

The OAuth Web Server Flow can seem tricky to implement by hand because there are a number of necessary steps but luckily this flow is pretty standard so many libraries have done the hard work for us. In the case of a Java Spring application the hard work has all been done by the Spring Security OAuth library. And Spring Boot makes it super easy to setup a new application that has everything needed for the OAuth Web Server Flow.

Let’s walk through the different pieces of a simple web application which has the OAuth stuff, fetches Accounts from the Salesforce REST API, and renders them in a web page via JavaScript. (Note: You can skip straight to the completed project if you just want to deploy the app on Heroku or get it all running locally.)

First we need a build system to manage dependencies, run the app, and assemble the app for deployment. I’ve chosen Gradle so here is my build.gradle:

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        maven {
            url 'https://plugins.gradle.org/m2/'
        }
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.2.RELEASE'
    }
}
 
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
 
repositories {
    mavenLocal()
    mavenCentral()
}
 
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web:1.4.2.RELEASE'
    compile 'org.springframework.boot:spring-boot-devtools:1.4.2.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-security:1.4.2.RELEASE'
    compile 'org.springframework.security.oauth:spring-security-oauth2:2.0.12.RELEASE'
    compile 'org.webjars:salesforce-lightning-design-system:2.1.4'
    compile 'org.webjars:jquery:3.1.1'
}

There you can see dependencies on the Spring Boot stuff, the Spring Security OAuth library and a few WebJars for the UI.

Now we need to set some properties for the Spring Security stuff. There are a few different ways to set these and you should make sure that your OAuth client id and secret don’t end up in SCM. At a minimum you will need these settings:

security.oauth2.client.client-authentication-scheme = form
security.oauth2.client.authentication-scheme = header
security.oauth2.client.grant-type = authorization_code
security.oauth2.client.access-token-uri = https://login.salesforce.com/services/oauth2/token
security.oauth2.client.user-authorization-uri = https://login.salesforce.com/services/oauth2/authorize
security.oauth2.resource.user-info-uri = https://login.salesforce.com/services/oauth2/userinfo
security.oauth2.client.client-id = YOUR_SALESFORCE_CONNECTED_APP_CLIENT_ID
security.oauth2.client.client-secret = YOUR_SALESFORCE_CONNECTED_APP_CLIENT_SECRET

I put all of the settings except the client id and secret in a src/main/resources/application.properties file. For the other settings you can use environment variables or config settings on Heroku. Spring Boot automatically will look for the client id and secret in environment variables named SECURITY_OAUTH2_CLIENT_CLIENT_ID and SECURITY_OAUTH2_CLIENT_CLIENT_SECRET.

Next we need a Spring component that will handle the REST communication with Salesforce including the query for Accounts and deserialization from JSON. Here is my src/main/java/com/example/Force.java code:

package com.example;
 
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Component;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@Component
public class Force {
 
    private static final String REST_VERSION = "35.0";
 
    @Bean
    private OAuth2RestTemplate oAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
        return new OAuth2RestTemplate(resource, context);
    }
 
    @Autowired
    private OAuth2RestTemplate restTemplate;
 
    @SuppressWarnings("unchecked")
    private static String restUrl(OAuth2Authentication principal) {
        HashMap<String, Object> details = (HashMap<String, Object>) principal.getUserAuthentication().getDetails();
        HashMap<String, String> urls = (HashMap<String, String>) details.get("urls");
        return urls.get("rest").replace("{version}", REST_VERSION);
    }
 
    public List<Account> accounts(OAuth2Authentication principal) {
        String url = restUrl(principal) + "query/?q={q}";
 
        Map<String, String> params = new HashMap<>();
        params.put("q", "SELECT Id, Name, Type, Industry, Rating FROM Account");
 
        return restTemplate.getForObject(url, QueryResultAccount.class, params).records;
    }
 
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Account {
        public String Id;
        public String Name;
        public String Industry;
        public String Rating;
    }
 
    @JsonIgnoreProperties(ignoreUnknown = true)
    private static class QueryResult<T> {
        public List<T> records;
    }
 
    private static class QueryResultAccount extends QueryResult<Account> {}
 
}

There is some plumbing to setup the Spring REST Template which makes it easy to make the REST requests. The accounts method takes an OAuth2Authentication so that the URL can be determined. Finally there are some classes to represent the results from the query and the Account, which are created from the JSON data returned from the REST API.

For Spring Boot we need an application which will be run on the web server (or local machine for development). In this example the application will also contain our REST API implementation which will be used by a JavaScript UI. Here is the code for the src/main/java/com/example/SpringSalesforceApplication.java file:

package com.example;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.List;
 
@SpringBootApplication
@RestController
@EnableOAuth2Sso
public class SpringSalesforceApplication {
 
    @Autowired
    private Force force;
 
    @RequestMapping("/accounts")
    public List<Force.Account> accounts(OAuth2Authentication principal) {
        return force.accounts(principal);
    }
 
    public static void main(String[] args) {
        SpringApplication.run(SpringSalesforceApplication.class, args);
    }
 
}

This does all the magic to create a Spring Boot web application which uses the Spring Security OAuth stuff, the Force component, and a REST controller. There is a single REST controller method in this application that handles requests to /accounts, does the query to Salesforce using the Force component, deserializes the results, then reserializes them as JSON. In this little example we aren’t seeing why we’d want to proxy the Salesforce REST API in this way since we are not doing any aggregation or transformation of data. But that would be straightforward to do if needed.

The final piece of this application is a web UI that uses the Spring Boot app’s /accounts REST method to get and then display the accounts. In this case I’m using jQuery to do that. Here is the code for the src/main/resources/static/index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="x-ua-compatible" content="ie=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
 
    <title>Hello Spring Salesforce</title>
 
    <link rel="stylesheet" type="text/css" href="webjars/salesforce-lightning-design-system/2.1.4/assets/styles/salesforce-lightning-design-system.min.css"/>
 
    <script type="text/javascript" src="webjars/jquery/3.1.1/jquery.min.js"></script>
    <script>
        $(function() {
          $.get("/accounts", function(data) {
            $("tbody").empty();
            $.each(data, function(i, account) {
              var tr = $("<tr>");
              tr.append($("<td>").text(account.Id));
              tr.append($("<td>").text(account.Name));
              tr.append($("<td>").text(account.Industry));
              tr.append($("<td>").text(account.Rating));
              $("tbody").append(tr);
            });
          });
        });
    </script>
</head>
<body>
    <header class="slds-global-header_container">
        <div class="slds-global-header slds-grid slds-grid--align-spread">
            <div class="slds-global-header__item">
                <div class="slds-text-heading--large">Hello Spring Salesforce</div>
            </div>
        </div>
    </header>
 
    <div class="slds-container--center slds-container--medium" style="margin-top: 60px;">
        <div class="slds-text-heading--medium">Salesforce Accounts</div>
 
        <table class="slds-table slds-table--bordered slds-table--cell-buffer">
            <thead>
                <tr class="slds-text-title--caps">
                    <th scope="col">
                        <div class="slds-truncate" title="Id">Id</div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="Id">Name</div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="Id">Industry</div>
                    </th>
                    <th scope="col">
                        <div class="slds-truncate" title="Id">Rating</div>
                    </th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    </div>
</body>
</html>

With this application running locally when I visit http://localhost:8080 in my browser, Spring Security notices that I don’t yet have an access token so it walks through the OAuth Web Server Flow with Salesforce and once complete renders the index.html page. The JavaScript on that page makes the REST request to /accounts which does the query to the Salesforce REST APIs and then returns the JSON back to the browser. Finally, the data is rendered in the table on the web page. Here is what it looks like:

In total there were only five pretty small files needed to have an end-to-end Salesforce REST API integration with a web application. Hopefully that gets you started! For full instructions on Heroku deployment of this application or to get the app running locally, check out the complete project on GitHub.

Let me know how it goes!

  • Raja VIVEKANANDHAN

    Hi James,
    I tried deploying the code in my local. I was getting 400 error with following cause
    Caused by: org.springframework.web.client.HttpClientErrorException: 400 TLS 1.1 or higher required
    I neither made changes on gradle config nor code. Do you know what I need to do to fix this issue ?

    • Raja VIVEKANANDHAN

      I got it figured. I was using JDK 1.7. I upgraded to JDK 8 and it resolved the issue

  • Ram

    Hi I have a question here. Is there anyway to skip that page asking allow/denay and directly show the accounts upon hitting the url. .?
    because i want to hide the app-salesforce interaction from the user UI. (willing to default it to Allow or use my custom page instead) .. please let me know how to achieve it .. Thank you
    -Ram

    • The OAuth workflow is useful when your users are Salesforce users. It sounds like you want to use a single integration user for all of the users of your app instead. In this case you can use a machine-to-machine integration with the OAuth username and password flow. Here is an example app for that: https://github.com/jamesward/salesforce-rest-starter

      Alternatively you can use Heroku Connect.

  • That exciting post, to put into practice, exists so much to do …. greetings cordial …

  • PA

    I am getting the below error

    java.lang.NoSuchMethodError: org.springframework.jndi.JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()Z
    at org.springframework.web.context.support.StandardServletEnvironment.customizePropertySources(StandardServletEnvironment.java:86)
    at org.springframework.core.env.AbstractEnvironment.(AbstractEnvironment.java:122)
    at org.springframework.core.env.StandardEnvironment.(StandardEnvironment.java:54)
    at org.springframework.web.context.support.StandardServletEnvironment.(StandardServletEnvironment.java:44)
    at org.springframework.web.servlet.HttpServletBean.createEnvironment(HttpServletBean.java:137)
    at org.springframework.web.servlet.HttpServletBean.getEnvironment(HttpServletBean.java:126)
    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:158)
    at javax.servlet.GenericServlet.init(GenericServlet.java:158)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1183)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1099)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:989)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4931)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5241)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

    • Hmm… What are the steps you took to get this?

      • PA

        Hey, I just compiled the code and deployed it to tomcat. While deploying I got the error shown above.

        • Ah, I did see some reports on StackOverflow of that. Can you just use the containerless Spring Boot instead of deploying in Tomcat?

          • PA

            I don’t see any main method to kick off the run. Could you please confirm ?