【Java】SalesforceのREST APIを利用してみる

2019年11月12日

本稿では、主に企業で利用されているであろうSalesforceというシステムから情報を取得したり更新したりするプログラムをJavaで書いてみたいと思います。

Salesforceは顧客管理などができるWebシステムです。詳細は「https://www.salesforce.com/jp/」をご覧ください。

REST APIの詳細はこちらからどうぞ。

※2019/11/12 各種サンプルのexecuteRequestメソッドでContentTypeをnullで送信してしまうとエラーになる(多分サーバ側が修正された)ので
動くプログラムに修正しなおしました。



REST APIでログインを行う

ログインを行うには「https://login.salesforce.com/services/oauth2/token」へアクセスします。

methodはPOSTを指定し必要な認証パラメータを設定します。

grant_type=password
client_id=「コンシューマ鍵」
client_secret=「コンシューマの秘密」
username=「ユーザー名」
password=「パスワード」+「セキュリティートークン」

必要な値はSalesforceへログインして取得できます。

ユーザー名とパスワードはわかると思いますが、コンシューマ鍵、コンシューマの秘密、セキュリティートークンは取得方法がわからない方もいると思います。
これらの取得手順は、こちらの記事で紹介しております。

値が取得できたらログイン処理を実行してみましょう。

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class SForceTest {
	public static void main(String[] args) {
		login();
	}
	static void login() {
		String url = "https://login.salesforce.com/services/oauth2/token";
		//String url = "https://test.salesforce.com/services/oauth2/token";//Sandbox環境の場合
		String method = "POST";
		String contentType = "application/x-www-form-urlencoded; charset=utf-8";

		StringBuilder sb =  new StringBuilder();

		//grant_typeはpassword
		sb.append("grant_type=password").append("&");

		//コンシューマ鍵
		sb.append("client_id=54LOAKJL+DJALH.0224D.__jSSAFfsfM__oKLJLKFJSLKJkjLKJSdkjklsjlkdfk253Ge5153213faADdegft133").append("&");

		//コンシューマの秘密
		sb.append("client_secret=LSFJHLS2313221SAD325FAD21321").append("&");

		//test@mail.co.jpなどSalesforceのユーザー名を指定
		sb.append("username=test%40mail.co.jp").append("&");

		//パスワード+セキュリティートークン
		sb.append("password=aaaasdkjLKJHGK23DA2dda");

		byte[] body = sb.toString().getBytes(StandardCharsets.UTF_8);
		byte[] result = executeRequest(url
			, method
			, contentType
			, null
			, body);

		if (result != null && result.length > 0) {
			System.out.println(new String(result,StandardCharsets.UTF_8));
		} else {
			System.out.println("レスポンスがないよ!!");
		}
	}

	static byte[] executeRequest(String url,String method,String contentType,String accessToken,byte[] body) {
		HttpURLConnection urlConn = null;
		try {
			// HTTP接続
			URL reqURL = new URL(url);
			urlConn = (HttpURLConnection) reqURL.openConnection();
			urlConn.setRequestMethod(method);
			if ( contentType != null ) urlConn.setRequestProperty("Content-Type", contentType);
			if ( accessToken != null ) urlConn.setRequestProperty("Authorization", "Bearer "+accessToken);
			urlConn.setDoOutput(true);

			urlConn.connect();

			// body送信
			if (body != null) {
				OutputStream out = urlConn.getOutputStream();
				try {
					out.write(body);
				} finally {
					out.close();
				}
			}

			//レスポンスの取得
			InputStream is = null;
			try {
				is = urlConn.getInputStream();
			}catch(Exception e) {
				is = urlConn.getErrorStream();
			}

			try (BufferedInputStream bis = new BufferedInputStream(is);
							ByteArrayOutputStream baos = new ByteArrayOutputStream();){
				byte[] bbb = new byte[1024];
				while(true) {
					int r = bis.read(bbb);
					if ( r == -1 ) {
						break;
					}
					if ( r == 0 ) {
						Thread.sleep(100);
						continue;
					}
					baos.write(bbb,0,r);
				}
				return baos.toByteArray();
			}finally {
				if ( is != null ) {
					is.close();
				}
			}

		}catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (urlConn != null) {
				// HTTP切断
				urlConn.disconnect();
			}
		}
		return null;
	}
}
実行結果コンソール(インデント付与済み)

{ "access_token": "000A000A0a0A0!QAKAJDSKLNDLAS5215153153AD21312A.LKJHSADLKJAL2465461256151111010000254", "instance_url": "https://app.salesforce.com", "id": "https://login.salesforce.com/id/00D0ED/00392HJGJH00XM000000", "token_type": "Bearer", "issued_at": "73268", "signature": "fkhjsakdfl/jksfdljk2/b1olCBVsdsDG+zZC76ad7889DFDJKY=" }

重要なのはaccess_tokenで次点でinstance_urlです。

これらはデータ取得や更新などをする際に必要な値となります。

access_tokenはログインした証となるものでデータ取得などをする際に必要となります。

instance_urlは実際にデータ取得などするときにアクセスするベースURLとなります。

インスタンスID(appの部分)は使用者によって変わると思います。

テストで必要であれば実際に取得できた値をメモっておきましょう。

この記事では、以降この結果をキーに設定したサンプルを掲載していきます。

REST APIでレコードを作成する

レコードを作成するには
「https://<インスタンスID>.salesforce.com/services/data/<APIバージョン>/sobjects/<オブジェクト名>」
にアクセスし、POSTを指定してリクエストします。

作成したいデータはXMLやJSONなどが使用できるみたいですが、一番簡単だと個人的に思ってるJSONでやります。

それでは取引先にレコード追加してみましょう。

取引先のオブジェクト名はAccountです。

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class SForceTest {

	public static void main(String[] args) {
		create();
	}

	static void create() {
		String url = "https://app.salesforce.com/services/data/v44.0/sobjects/Account";
		String method = "POST";
		String contentType = "application/json; charset=utf-8";
		String accessToken = "000A000A0a0A0!QAKAJDSKLNDLAS5215153153AD21312A.LKJHSADLKJAL2465461256151111010000254";
		String body = "{\"Name\":\"Test\"}";
		byte[] result = executeRequest(url
		, method
		, contentType
		, accessToken
		, body.getBytes(StandardCharsets.UTF_8));

		if (result != null && result.length > 0) {
			System.out.println(new String(result,StandardCharsets.UTF_8));
		} else {
			System.out.println("レスポンスがないよ!!");
		}
	}

	static byte[] executeRequest(String url,String method,String contentType,String accessToken,byte[] body) {
		HttpURLConnection urlConn = null;
		try {
			// HTTP接続
			URL reqURL = new URL(url);
			urlConn = (HttpURLConnection) reqURL.openConnection();
			urlConn.setRequestMethod(method);
			if ( contentType != null ) urlConn.setRequestProperty("Content-Type", contentType);
			if ( accessToken != null ) urlConn.setRequestProperty("Authorization", "Bearer "+accessToken);//Salesforce用アクセストークン
			urlConn.setDoOutput(true);

			urlConn.connect();

			// body送信
			if (body != null) {
				OutputStream out = urlConn.getOutputStream();
				try {
					out.write(body);
				} finally {
					out.close();
				}
			}

			//レスポンスの取得
			InputStream is = null;
			try {
				is = urlConn.getInputStream();
			}catch(Exception e) {
				is = urlConn.getErrorStream();
			}

			try (BufferedInputStream bis = new BufferedInputStream(is);
							ByteArrayOutputStream baos = new ByteArrayOutputStream();){
				byte[] bbb = new byte[1024];
				while(true) {
					int r = bis.read(bbb);
					if ( r == -1 ) {
						break;
					}
					if ( r == 0 ) {
						Thread.sleep(100);
						continue;
					}
					baos.write(bbb,0,r);
				}
				return baos.toByteArray();
			}finally {
				if ( is != null ) {
					is.close();
				}
			}

		}catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (urlConn != null) {
				// HTTP切断
				urlConn.disconnect();
			}
		}
		return null;
	}
}
実行結果

実行結果コンソール(インデント付与済み)

{ "id": "0011903JO01", "success": true, "errors": [] }

取引先にTestが追加されていれば成功です。

設定できるフィールドはName以外にも多数あります。
指定フィールドはAPIでも取得することはできますし、SOAP APIのレファレンスとかも見てみると案外取れたりします。

idはデータ取得、更新、削除などで使用するキーとなります。

テストで必要であれば実際の値をメモっておきましょう。この記事では以降、この結果をキーに設定したサンプルを掲載していきます。

REST APIでレコードを取得する

レコードを取得するには
「https://<インスタンスID>.salesforce.com/services/data/<APIバージョン>/sobjects/<オブジェクト名>/<オブジェクトID>」
にアクセスし、GETを指定してリクエストします。

先ほど作成したTestレコードのidを設定してデータ取得してみましょう。

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class SForceTest {


	public static void main(String[] args) {
		get();
	}

	static void get() {
		String url = "https://app.salesforce.com/services/data/v44.0/sobjects/Account/0011903JO01";
		String method = "GET";
		String accessToken = "000A000A0a0A0!QAKAJDSKLNDLAS5215153153AD21312A.LKJHSADLKJAL2465461256151111010000254";
		byte[] result = executeRequest(url
		, method
		, null
		, accessToken
		, null
		);

		if (result != null && result.length > 0) {
			System.out.println(new String(result,StandardCharsets.UTF_8));
		} else {
			System.out.println("レスポンスがないよ!!");
		}
	}

	static byte[] executeRequest(String url,String method,String contentType,String accessToken,byte[] body) {
		HttpURLConnection urlConn = null;
		try {
			// HTTP接続
			URL reqURL = new URL(url);
			urlConn = (HttpURLConnection) reqURL.openConnection();
			urlConn.setRequestMethod(method);
			if ( contentType != null ) urlConn.setRequestProperty("Content-Type", contentType);
			if ( accessToken != null ) urlConn.setRequestProperty("Authorization", "Bearer "+accessToken);//Salesforce用アクセストークン
			urlConn.setDoOutput(true);

			urlConn.connect();

			// body送信
			if (body != null) {
				OutputStream out = urlConn.getOutputStream();
				try {
					out.write(body);
				} finally {
					out.close();
				}
			}

			//レスポンスの取得
			InputStream is = null;
			try {
				is = urlConn.getInputStream();
			}catch(Exception e) {
				is = urlConn.getErrorStream();
			}

			try (BufferedInputStream bis = new BufferedInputStream(is);
							ByteArrayOutputStream baos = new ByteArrayOutputStream();){
				byte[] bbb = new byte[1024];
				while(true) {
					int r = bis.read(bbb);
					if ( r == -1 ) {
						break;
					}
					if ( r == 0 ) {
						Thread.sleep(100);
						continue;
					}
					baos.write(bbb,0,r);
				}
				return baos.toByteArray();
			}finally {
				if ( is != null ) {
					is.close();
				}
			}

		}catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (urlConn != null) {
				// HTTP切断
				urlConn.disconnect();
			}
		}
		return null;
	}
}
実行結果コンソール(インデント付与済み)

{ "attributes": { "type": "Account", "url": "/services/data/v44.0/sobjects/Account/0011903JO01" }, "Id": "0011903JO01", "IsDeleted": false, "MasterRecordId": null, "Name": "Test", "Type": null, "ParentId": null, "BillingStreet": null, "BillingCity": null, "BillingState": null, "BillingPostalCode": null, "BillingCountry": null, "BillingLatitude": null, "BillingLongitude": null, "BillingGeocodeAccuracy": null, "BillingAddress": null, "ShippingStreet": null, "ShippingCity": null, "ShippingState": null, "ShippingPostalCode": null, "ShippingCountry": null, "ShippingLatitude": null, "ShippingLongitude": null, "ShippingGeocodeAccuracy": null, "ShippingAddress": null, "Phone": null, "Fax": null, "Website": null, "PhotoUrl": "/services/images/photo/0011903JO01", "Industry": null, "NumberOfEmployees": null, "Description": null, "Site": null, "OwnerId": "039012930", "CreatedDate": "2019-09-29T11:03:41.000+0000", "CreatedById": "039012930", "LastModifiedDate": "2019-09-29T11:03:41.000+0000", "LastModifiedById": "039012930", "SystemModstamp": "2019-09-29T11:03:41.000+0000", "LastActivityDate": null, "LastViewedDate": "2019-09-29T11:04:37.000+0000", "LastReferencedDate": "2019-09-29T11:04:37.000+0000", "Jigsaw": null, "JigsawCompanyId": null, "AccountSource": null, "SicDesc": null, }

ほとんど値を設定していないのでnullですね。

REST APIでレコードを更新する

レコードの更新を行うには
「https://<インスタンスID>.salesforce.com/services/data/<APIバージョン>/sobjects/<オブジェクト名>/<オブジェクトID>」
にアクセスし、PATCHを指定してリクエストします。

Java11以前の標準HTTPリクエスト機能はPATCHが使えなくなっていますので、リフレクションで無理やり使えるようにしてみました。

Java11をご利用の方は新しいほうのAPIを利用することをお勧めします。

更新内容はJSON文字列で指定します。

試しに作成したTestのレコードをTest2という名前に変更してみましょう。

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class SForceTest {

	static {
		try {
			Field method = HttpURLConnection.class.getDeclaredField("methods");
			Field modifiersField = Field.class.getDeclaredField("modifiers");
			modifiersField.setAccessible(true);
			modifiersField.setInt(method,
			method.getModifiers() & ~Modifier.FINAL);
			method.setAccessible(true);
			String[] methods = (String[]) method.get(null);
			boolean isAllowPatchMethod = false;
			for ( String m : methods ) {
				if ( m.equals("PATCH") ) {
					isAllowPatchMethod = true;
				}
			}
			if (!isAllowPatchMethod) {
				String[] newMethods = new String[methods.length+1];
				System.arraycopy(methods, 0, newMethods, 0, methods.length);
				newMethods[methods.length] = "PATCH";
				method.set(null, newMethods);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		update();
	}

	static void update() {
		String url = "https://app.salesforce.com/services/data/v44.0/sobjects/Account/0011903JO01";
		String method = "PATCH";
		String contentType = "application/json; charset=utf-8";
		String accessToken = "000A000A0a0A0!QAKAJDSKLNDLAS5215153153AD21312A.LKJHSADLKJAL2465461256151111010000254";
		String body = "{\"Name\":\"Test2\"}";
		byte[] result = executeRequest(url
		, method
		, contentType
		, accessToken
		, body.getBytes(StandardCharsets.UTF_8));

		if (result != null && result.length > 0) {
			System.out.println(new String(result,StandardCharsets.UTF_8));
		} else {
			System.out.println("レスポンスがないよ!!");
		}
	}
	static byte[] executeRequest(String url,String method,String contentType,String accessToken,byte[] body) {
		HttpURLConnection urlConn = null;
		try {
			// HTTP接続
			URL reqURL = new URL(url);
			urlConn = (HttpURLConnection) reqURL.openConnection();
			urlConn.setRequestMethod(method);
			if ( contentType != null ) urlConn.setRequestProperty("Content-Type", contentType);
			if ( accessToken != null ) urlConn.setRequestProperty("Authorization", "Bearer "+accessToken);//Salesforce用アクセストークン
			urlConn.setDoOutput(true);

			urlConn.connect();

			// body送信
			if (body != null) {
				OutputStream out = urlConn.getOutputStream();
				try {
					out.write(body);
				} finally {
					out.close();
				}
			}

			//レスポンスの取得
			InputStream is = null;
			try {
				is = urlConn.getInputStream();
			}catch(Exception e) {
				is = urlConn.getErrorStream();
			}

			try (BufferedInputStream bis = new BufferedInputStream(is);
							ByteArrayOutputStream baos = new ByteArrayOutputStream();){
				byte[] bbb = new byte[1024];
				while(true) {
					int r = bis.read(bbb);
					if ( r == -1 ) {
						break;
					}
					if ( r == 0 ) {
						Thread.sleep(100);
						continue;
					}
					baos.write(bbb,0,r);
				}
				return baos.toByteArray();
			}finally {
				if ( is != null ) {
					is.close();
				}
			}

		}catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (urlConn != null) {
				// HTTP切断
				urlConn.disconnect();
			}
		}
		return null;
	}
}
実行結果

取引先名がTest2に変更されればOKです。

実行結果コンソール

レスポンスがないよ!!

REST APIでレコードを削除する

レコードの削除を行うには
「https://<インスタンスID>.salesforce.com/services/data/<APIバージョン>/sobjects/<オブジェクト名>/<オブジェクトID>」
にアクセスし、DELETE を指定してリクエストします。

それでは、先ほど更新したTest2のレコードを削除してみましょう。

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class SForceTest {

	public static void main(String[] args) {
		delete();
	}

	static void delete() {
		String url = "https://app.salesforce.com/services/data/v44.0/sobjects/Account/0011903JO01";
		String method = "DELETE";
		String accessToken = "000A000A0a0A0!QAKAJDSKLNDLAS5215153153AD21312A.LKJHSADLKJAL2465461256151111010000254";
		byte[] result = executeRequest(url
		, method
		, null
		, accessToken
		, null
		);

		if (result != null && result.length > 0) {
			System.out.println(new String(result,StandardCharsets.UTF_8));
		} else {
			System.out.println("レスポンスがないよ!!");
		}
	}

	static byte[] executeRequest(String url,String method,String contentType,String accessToken,byte[] body) {
		HttpURLConnection urlConn = null;
		try {
			// HTTP接続
			URL reqURL = new URL(url);
			urlConn = (HttpURLConnection) reqURL.openConnection();
			urlConn.setRequestMethod(method);
			if ( contentType != null ) urlConn.setRequestProperty("Content-Type", contentType);
			if ( accessToken != null ) urlConn.setRequestProperty("Authorization", "Bearer "+accessToken);//Salesforce用アクセストークン
			urlConn.setDoOutput(true);

			urlConn.connect();

			// body送信
			if (body != null) {
				OutputStream out = urlConn.getOutputStream();
				try {
					out.write(body);
				} finally {
					out.close();
				}
			}

			//レスポンスの取得
			InputStream is = null;
			try {
				is = urlConn.getInputStream();
			}catch(Exception e) {
				is = urlConn.getErrorStream();
			}

			try (BufferedInputStream bis = new BufferedInputStream(is);
							ByteArrayOutputStream baos = new ByteArrayOutputStream();){
				byte[] bbb = new byte[1024];
				while(true) {
					int r = bis.read(bbb);
					if ( r == -1 ) {
						break;
					}
					if ( r == 0 ) {
						Thread.sleep(100);
						continue;
					}
					baos.write(bbb,0,r);
				}
				return baos.toByteArray();
			}finally {
				if ( is != null ) {
					is.close();
				}
			}

		}catch(Exception e) {
			e.printStackTrace();
		} finally {
			if (urlConn != null) {
				// HTTP切断
				urlConn.disconnect();
			}
		}
		return null;
	}
}
実行結果

実行結果コンソール

レスポンスがないよ!!

取引先からTest2が削除されたらOKです。