How to use Salesforce REST API with your JavaServer Pages
Abstract: This tutorial gives an example of a JSP and how to integrate it with the Salesforce REST API. We will walk through the stepbystep process of creating an external client to manage your data with Force.com,while using HTTP(S) and JSON.
In this example, I am using Mac OS X 10.9.2 with Apache Tomcat 7 server and Java 1.7. Eclipse Java EE edition is the IDE used for development and testing. The instructions given in this tutorial should work with minor modifications for other platforms as well.
If you want to access the entire sample code from this tutorial, you can access it here: github.com/seethaa/force_rest_example
All code is updated to work with the httpclient 4.3 libraries.
What Is REST?
REST stands for Representational State Transfer, and is a stateless clientserver communications protocol over HTTP.
Why and When To Use A REST API in Java for Your JSP
A REST API is well suited for browser applications which require a lot of interaction, and uses synchronous communication to transfer data. The Salesforce REST API provides a programming interface for simple web services to interact with Force.com, and supports both XML and JSON formats. The Salesforce REST API works well for mobile applications or dynamic websites to retrieve or update records quickly on your web server. While bulk record retrieval should be reserved for the BulkAPI, this lightweight REST API can be used for common server pages which involve quick updates and frequent user interactions, for example updating a single user record.
Setting Up Your Development Account and Prerequisites
You will need the following:
- Go to https://developer.salesforce.com/signup and register for your Free DE account. For the purposes of this example, I recommend sign up for a Developer Edition even if you already have an account. This ensures you get a clean environment with the latest features enabled.
- Java application Server. I created mine using Apache Tomcat 7 on Mac OS X and Eclipse as the IDE. There is also a free Eclipse plugin at http://developer.salesforce.com/page/Force.com_IDE but the original Eclipse setup was used in this tutorial.
- Configure SSL on your Tomcat server using http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html. If you are developing in Eclipse, make sure to add the Connector piece in server.xml file in your Eclipse environment, e.g.:1
<
Connector
SSLEnabled
=
"true"
clientAuth
=
"false"
keystoreFile
=
"/Users/seetha/.keystore"
keystorePass
=
"password"
maxThreads
=
"200"
port
=
"8443"
protocol
=
"HTTP/1.1"
scheme
=
"https"
secure
=
"true"
sslProtocol
=
"TLS"
/>
- Add the required jar files to WebContent/WEBINF/lib. You will need commons-codec-1.6.jar, httpclient4.3.3.jar, httpcore-4.3.2.jar, commons-logging-1.1.3.jar, and java-json.jar. For Eclipse, I also had to make sure that all jars were added to the build path (Right click Project → Build Path → Configure build path → Select Libraries tab → Click Add Jars → Select the Jar files from the WEBINF/lib folder.
Create a Connected App
- Back in your Force.com DE, create a new Connected App through the console. Click on Setup → Build → Create → Apps. Scroll down to the Connected Apps section and click on the New button.
- Ensure that the callback URL is http://localhost:8080/<your_app_context_path>/oauth/_callback
(You can find the app context path by going back to Eclipse: Right clicking on Project → Properties → Web Project Settings → Context root)
- Check “Enable OAuth Settings” checkbox
- The required OAuth scopes for this tutorial (see Figure 1) are “Access and manage your data (api)” and “Provide access to your data via the Web (web)”, but these scopes should be changed as per your requirement.
- Save
Figure 1: Creating new Connected App
- Ensure that the callback URL is http://localhost:8080/<your_app_context_path>/oauth/_callback
- Copy the ClientID and Client Secret (see Figure 2), because both of these will be used in the next step.
Figure 2: Connected App Example with Consumer Key and Secret Authentication
There are three files that need to be imported into your JSP project, given below:
index.html
01020304050607080910111213141516<!
DOCTYPE
html PUBLIC "//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<
html
>
<
head
>
<
meta
http
equiv
=
"ContentType"
content
=
"text/html; charset=UTF8"
>
<
title
>REST/OAuth Example</
title
>
</
head
>
<
body
>
<
script
type
=
"text/javascript"
language
=
"javascript"
>
if (location.protocol != "https:") {
document.write("OAuth will not work correctly from plain http. "+ "Please use an https URL.");
} else {
document.write("<
a
href=\"oauth\">Run Connected App demo via REST/OAuth.</
a
>");
}
</
script
>
</
body
>
</
html
>
OAuthConnectedApp.java
001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151import
java.io.IOException;
import
java.io.InputStream;
import
java.io.UnsupportedEncodingException;
import
java.net.URLEncoder;
import
java.util.ArrayList;
import
java.util.List;
import
javax.servlet.ServletException;
import
javax.servlet.annotation.WebInitParam;
import
javax.servlet.annotation.WebServlet;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.apache.http.Consts;
import
org.apache.http.HttpEntity;
import
org.apache.http.NameValuePair;
import
org.apache.http.client.entity.UrlEncodedFormEntity;
import
org.apache.http.client.methods.CloseableHttpResponse;
import
org.apache.http.client.methods.HttpPost;
import
org.apache.http.impl.client.CloseableHttpClient;
import
org.apache.http.impl.client.HttpClients;
import
org.apache.http.message.BasicNameValuePair;
import
org.json.JSONException;
import
org.json.JSONObject;
import
org.json.JSONTokener;
@WebServlet
(name =
"oauth"
, urlPatterns = {
"/oauth/*"
,
"/oauth"
}, initParams = {
// clientId is 'Consumer Key' in the Remote Access UI
//**Update with your own Client ID
@WebInitParam
(name =
"clientId"
, value =
"3MVG9JZ_r.QzrS7jzujCYrebr8kajDEcjXQLXnV9nGU6PaxOjuOi_n8EcUf0Ix9qqk1lYCa4_Jaq7mpqxi2YT"
),
// clientSecret is 'Consumer Secret' in the Remote Access UI
//**Update with your own Client Secret
@WebInitParam
(name =
"clientSecret"
, value =
"2307033558641049067"
),
// This must be identical to 'Callback URL' in the Remote Access UI
//**Update with your own URI
@WebInitParam
(name =
"redirectUri"
, value =
"http://localhost:8080/force_rest_example/oauth/_callback"
),
/**
* Servlet parameters
*
@author
seetha
*
*/
public
class
OAuthConnectedApp
extends
HttpServlet {
private
static
final
long
serialVersionUID = 1L;
private
static
final
String ACCESS_TOKEN =
"ACCESS_TOKEN"
;
private
static
final
String INSTANCE_URL =
"INSTANCE_URL"
;
private
String clientId =
null
;
private
String clientSecret =
null
;
private
String redirectUri =
null
;
private
String environment =
null
;
private
String authUrl =
null
;
private
String tokenUrl =
null
;
public
void
init()
throws
ServletException {
clientId =
this
.getInitParameter(
"clientId"
);
clientSecret =
this
.getInitParameter(
"clientSecret"
);
redirectUri =
this
.getInitParameter(
"redirectUri"
);
environment =
this
.getInitParameter(
"environment"
);
try
{
authUrl = environment
+
"/services/oauth2/authorize?response_type=code&client_id="
+ clientId +
"&redirect_uri="
+ URLEncoder.encode(redirectUri,
"UTF8"
);
}
catch
(UnsupportedEncodingException e) {
throw
new
ServletException(e);
}
tokenUrl = environment +
"/services/oauth2/token"
;
}
protected
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
String accessToken = (String) request.getSession().getAttribute(ACCESS_TOKEN);
//System.out.println("calling doget");
if
(accessToken ==
null
) {
String instanceUrl =
null
;
if
(request.getRequestURI().endsWith(
"oauth"
)) {
// we need to send the user to authorize
response.sendRedirect(authUrl);
return
;
}
else
{
System.out.println(
"Auth successful got callback"
);
String code = request.getParameter(
"code"
);
// Create an instance of HttpClient.
CloseableHttpClient httpclient = HttpClients.createDefault();
try
{
// Create an instance of HttpPost.
HttpPost httpost =
new
HttpPost(tokenUrl);
// Adding all form parameters in a List of type NameValuePair
List<NameValuePair> nvps =
new
ArrayList<NameValuePair>();
nvps.add(
new
BasicNameValuePair(
"code"
, code));
nvps.add(
new
BasicNameValuePair(
"grant_type"
,
"authorization_code"
));
nvps.add(
new
BasicNameValuePair(
"client_id"
, clientId));
nvps.add(
new
BasicNameValuePair(
"client_secret"
, clientSecret));
nvps.add(
new
BasicNameValuePair(
"redirect_uri"
, redirectUri));
httpost.setEntity(
new
UrlEncodedFormEntity(nvps, Consts.UTF_8));
// Execute the request.
CloseableHttpResponse closeableresponse=httpclient.execute(httpost);
System.out.println(
"Response Statusline:"
+closeableresponse.getStatusLine());
try
{
// Do the needful with entity.
HttpEntity entity = closeableresponse.getEntity();
InputStream rstream = entity.getContent();
JSONObject authResponse =
new
JSONObject(
new
JSONTokener(rstream));
accessToken = authResponse.getString(
"access_token"
);
instanceUrl = authResponse.getString(
"instance_url"
);
}
catch
(JSONException e) {
// TODO Autogenerated catch block e.printStackTrace();
e.printStackTrace();
}
finally
{
// Closing the response
closeableresponse.close();
}
}
finally
{
httpclient.close();
}
}
// Set a session attribute so that other servlets can get the access token
request.getSession().setAttribute(ACCESS_TOKEN, accessToken);
// We also get the instance URL from the OAuth response, so set it in the session too
request.getSession().setAttribute(INSTANCE_URL, instanceUrl);
}
response.sendRedirect(request.getContextPath() +
"/ConnectedAppREST"
);
}
}
ConnectedAppREST.java
001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314import
java.io.IOException;
import
java.io.InputStream;
import
java.io.PrintWriter;
import
java.net.URISyntaxException;
import
java.util.Iterator;
import
javax.servlet.ServletException;
import
javax.servlet.annotation.WebServlet;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.apache.http.HttpEntity;
import
org.apache.http.HttpStatus;
import
org.apache.http.client.methods.CloseableHttpResponse;
import
org.apache.http.client.methods.HttpDelete;
import
org.apache.http.client.methods.HttpGet;
import
org.apache.http.client.methods.HttpPost;
import
org.apache.http.client.utils.URIBuilder;
import
org.apache.http.entity.ContentType;
import
org.apache.http.entity.StringEntity;
import
org.apache.http.impl.client.CloseableHttpClient;
import
org.apache.http.impl.client.HttpClients;
import
org.json.JSONArray;
import
org.json.JSONException;
import
org.json.JSONObject;
import
org.json.JSONTokener;
@WebServlet
(urlPatterns = {
"/ConnectedAppREST"
})
/**
* Demo for Connect App/REST API
* @author seetha
*
*/
public
class
ConnectedAppREST
extends
HttpServlet {
private
static
final
long
serialVersionUID = 1L;
private
static
final
String ACCESS_TOKEN =
"ACCESS_TOKEN"
;
private
static
final
String INSTANCE_URL =
"INSTANCE_URL"
;
private
void
showAccounts(String instanceUrl, String accessToken,
PrintWriter writer)
throws
ServletException, IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet =
new
HttpGet();
//add key and value
httpGet.addHeader(
"Authorization"
,
"OAuth "
+ accessToken);
try
{
URIBuilder builder =
new
URIBuilder(instanceUrl+
"/services/data/v30.0/query"
);
builder.setParameter(
"q"
,
"SELECT Name, Id from Account LIMIT 100"
);
httpGet.setURI(builder.build());
CloseableHttpResponse closeableresponse = httpclient.execute(httpGet);
System.out.println(
"Response Status line :"
+ closeableresponse.getStatusLine());
if
(closeableresponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// Now lets use the standard java json classes to work with the results
try
{
// Do the needful with entity.
HttpEntity entity = closeableresponse.getEntity();
InputStream rstream = entity.getContent();
JSONObject authResponse =
new
JSONObject(
new
JSONTokener(rstream));
System.out.println(
"Query response: "
+ authResponse.toString(
2
));
writer.write(authResponse.getInt(
"totalSize"
) +
" record(s) returned\n\n"
);
JSONArray results = authResponse.getJSONArray(
"records"
);
for
(
int
i =
0
; i < results.length(); i++) {
writer.write(results.getJSONObject(i).getString(
"Id"
)
+
", "
+ results.getJSONObject(i).getString(
"Name"
)
+
"\n"
);
}
writer.write(
"\n"
);
}
catch
(JSONException e) {
e.printStackTrace();
throw
new
ServletException(e);
}
}
}
catch
(URISyntaxException e1) {
// TODO Autogenerated catch block
e1.printStackTrace();
}
finally
{
httpclient.close();
}
}
private
String createAccount(String name, String instanceUrl,
String accessToken, PrintWriter writer)
throws
ServletException, IOException {
String accountId =
null
;
CloseableHttpClient httpclient = HttpClients.createDefault();
JSONObject account =
new
JSONObject();
try
{
account.put(
"Name"
, name);
}
catch
(JSONException e) {
e.printStackTrace();
throw
new
ServletException(e);
}
HttpPost httpost =
new
HttpPost(instanceUrl+
"/services/data/v30.0/sobjects/Account/"
);
httpost.addHeader(
"Authorization"
,
"OAuth "
+ accessToken);
StringEntity messageEntity =
new
StringEntity( account.toString(), ContentType.create(
"application/json"
));
httpost.setEntity(messageEntity);
// Execute the request.
CloseableHttpResponse closeableresponse = httpclient.execute(httpost);
System.out.println(
"Response Status line :"
+ closeableresponse.getStatusLine());
try
{
writer.write(
"HTTP status "
+ closeableresponse.getStatusLine().getStatusCode() +
" creating account\n\n"
);
if
(closeableresponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
try
{
// Do the needful with entity.
HttpEntity entity = closeableresponse.getEntity();
InputStream rstream = entity.getContent();
JSONObject authResponse =
new
JSONObject(
new
JSONTokener(rstream));
System.out.println(
"Create response: "
+ authResponse.toString(
2
));
if
(authResponse.getBoolean(
"success"
)) {
accountId = authResponse.getString(
"id"
);
writer.write(
"New record id "
+ accountId +
"\n\n"
);
}
}
catch
(JSONException e) {
e.printStackTrace();
// throw new ServletException(e);
}
}
}
finally
{
httpclient.close();
}
return
accountId;
}
private
void
showAccount(String accountId, String instanceUrl,
String accessToken, PrintWriter writer)
throws
ServletException, IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet =
new
HttpGet();
//add key and value
httpGet.addHeader(
"Authorization"
,
"OAuth "
+ accessToken);
try
{
URIBuilder builder =
new
URIBuilder(instanceUrl +
"/services/data/v30.0/sobjects/Account/"
+ accountId);
httpGet.setURI(builder.build());
//httpclient.execute(httpGet);
CloseableHttpResponse closeableresponse = httpclient.execute(httpGet);
System.out.println(
"Response Status line :"
+ closeableresponse.getStatusLine());
if
(closeableresponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
try
{
// Do the needful with entity.
HttpEntity entity = closeableresponse.getEntity();
InputStream rstream = entity.getContent();
JSONObject authResponse =
new
JSONObject(
new
JSONTokener(rstream));
System.out.println(
"Query response: "
+ authResponse.toString(
2
));
writer.write(
"Account content\n\n"
);
Iterator iterator = authResponse.keys();
while
(iterator.hasNext()) {
String key = (String) iterator.next();
Object obj = authResponse.get(key);
String value =
null
;
if
(obj
instanceof
String) {
value = (String) obj;
}
writer.write(key +
":"
+ (value !=
null
? value :
""
) +
"\n"
);
}
writer.write(
"\n"
);
}
catch
(JSONException e) {
e.printStackTrace();
throw
new
ServletException(e);
}
}
}
catch
(URISyntaxException e1) {
// TODO Autogenerated catch block
e1.printStackTrace();
}
finally
{
httpclient.close();
}
}
private
void
updateAccount(String accountId, String newName, String city, String instanceUrl, String accessToken, PrintWriter writer)
throws
ServletException, IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
JSONObject update =
new
JSONObject();
try
{
update.put(
"Name"
, newName);
update.put(
"BillingCity"
, city);
}
catch
(JSONException e) {
e.printStackTrace();
throw
new
ServletException(e);
}
HttpPost httpost =
new
HttpPost(instanceUrl +
"/services/data/v30.0/sobjects/Account/"
+accountId+
"?_HttpMethod=PATCH"
);
httpost.addHeader(
"Authorization"
,
"OAuth "
+ accessToken);
StringEntity messageEntity =
new
StringEntity( update.toString(), ContentType.create(
"application/json"
));
httpost.setEntity(messageEntity);
// Execute the request.
CloseableHttpResponse closeableresponse = httpclient.execute(httpost); System.out.println(
"Response Status line :"
+ closeableresponse.getStatusLine());
try
{
writer.write(
"HTTP status "
+ closeableresponse.getStatusLine().getStatusCode() +
" updating account "
+ accountId +
"\n\n"
);
}
finally
{
httpclient.close();
}
}
private
void
deleteAccount(String accountId, String instanceUrl, String accessToken, PrintWriter writer)
throws
IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpDelete delete =
new
HttpDelete(instanceUrl +
"/services/data/v30.0/sobjects/Account/"
+ accountId);
delete.setHeader(
"Authorization"
,
"OAuth "
+ accessToken);
// Execute the request.
CloseableHttpResponse closeableresponse = httpclient.execute(delete);
System.out.println(
"Response Status line :"
+ closeableresponse.getStatusLine());
try
{
writer.write(
"HTTP status "
+ closeableresponse.getStatusLine().getStatusCode() +
" deleting account "
+ accountId +
"\n\n"
);
}
finally
{
delete.releaseConnection();
}
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
@Override
protected
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
PrintWriter writer = response.getWriter();
String accessToken = (String) request.getSession().getAttribute( ACCESS_TOKEN);
String instanceUrl = (String) request.getSession().getAttribute( INSTANCE_URL);
if
(accessToken ==
null
) {
writer.write(
"Error no access token"
);
return
;
}
writer.write(
"We have an access token: "
+ accessToken +
"\n"
+
"Using instance "
+ instanceUrl +
"\n\n"
);
showAccounts(instanceUrl, accessToken, writer);
String accountId = createAccount(
"My New Org"
, instanceUrl, accessToken, writer);
if
(accountId ==
null
) {
System.out.println(
"Account ID null"
);
}
showAccount(accountId, instanceUrl, accessToken, writer);
showAccounts(instanceUrl, accessToken, writer);
updateAccount(accountId,
"My New Org, Inc"
,
"San Francisco"
, instanceUrl, accessToken, writer);
showAccount(accountId, instanceUrl, accessToken, writer);
deleteAccount(accountId, instanceUrl, accessToken, writer);
showAccounts(instanceUrl, accessToken, writer);
}
}
- Change OAuthConnectedApp.java to replace Client ID, Client Secret, and Callback URI fields based on the Connected App configuration.
- Start Tomcat server in Eclipse (see Figure 3) or externally, and navigate to https://localhost:8443/<your_app_context_path>/
Figure 3: Running Tomcat Server in Eclipse
Figure 4: Retrieve Objects Screen - Clicking on the link above (see Figure 4) will not work unless it is through HTTPS, and SSL must be configured as endpoint for Tomcat.
If all configurations were done properly, you should see a salesforce.com login screen (see Figure 5). Go ahead and login with your salesforce.com credentials to authorize your web application to access resources.
Figure 5: Salesforce.com Login Screen for OAuth - Logging in will allow the ConnectedAppREST demo to execute methods to create, show, update, and delete records (see Figure 6).
Figure 6: Output from Connected App REST Demo
*Tips & Warnings
- Make sure you have a Developer Edition (DE) account, because there are slight differences between Professional, Enterprise, Developer, etc. The Developer edition is free and does not expire (unless unused after a year).
- The callback URL in the OAuthConnectedApp.java must be same as the URL added to the connected app.
- If you get HTTP 403 error, it means the resource you are requesting is “Forbidden” from being accessed. Check that the username/account you are accessing with has the appropriate permissions.
- Make sure index.html is directly under the WebContent directory.
Resources
For a comprehensive set or resources, check out: http://developer.salesforce.com/en/mobile/resources