Posted at

Dynamics CRM Online 2015 Web API をJavaから使ってみた。

More than 3 years have passed since last update.


Dynamics CRM Online 2015 Web API Preview

Dynamics CRM Online のWeb APIが、プレビュー版ではありますが、リリースされていました。

Dynamics CRM Online 2015 Web API Preview

Web APIでDynamics CRMの機能を使うことができれば、何かと重宝しそうです。

早速サンプルを参考に... と思いきや、samplesはやはりC#が中心でした。

しかし、Web APIなのでJavaからだって普通に叩けば、JSONあたりの形式でレスポンスが返ってくるはず...

難しくはなさそうなので、Javaで使ってみることにしました。


事前準備

ところで、ある程度自由に使える下記2つの環境がある、といふのが大前提です。

- Dynamics CRM Online

- Azure Active Directory

さて、まずは認証です。

認証してアクセストークンさえ頂戴すれば、あとはあれこれAPIを叩くだけです。

認証についてのドキュメントを見てみます。

(ふむふむ、なるほど... :zzz: なr..h...ど.. :zzz:

なるほど、アクセストークンは、Azure ADから発行されるのを使うらしいです。

Azure ADとDynamics CRM の設定をあれこれする必要があるぽいので、Microsoftの中の人のブログを参考にしてみます。

- Dynamics CRM Online 2015 Update 1 新機能: Web API 開発者プレビュー その 1

- Dynamics CRM Online 2015 Update 1 新機能: Web API 開発者プレビュー その 2

上記のブログで、Dynamics CRMのWeb API機能の有効化と、Azure ADへのDynamics CRMの認証設定の手順がわかりました。

実際にやってみて、Azure ADにリダイレクトURIを登録、クライアントIDも発行されました。

crm-web-api-test.jpg

今回は http://localhost:8080/appointments をリダイレクトURIとしました。

事前準備としては、これで良さそうです。


Javaアプリの準備

先に挙げたブログでは、C#でアプリを作成する際のサンプルコードが記載されていました。

これを参考に、Javaでサンプルを作成してみます。

Azure ADの認証画面にリダイレクトすることを考えると、Webアプリを作成した方が簡単そうです。

ということで、こんな感じのWebアプリ(サーバアプリ)を作ってみます。



  1. ブラウザで localhost:8080 にアクセスしたら、Azure ADの認証画面にリダイレクトする。

  2. 認証が成功してAzure ADに登録したリダイレクトURIに戻ってきたら、Dynamics CRMからアクセストークンを取得する。

  3. 取得したアクセストークンを使って、Dynamics CRMのWeb APIを実行して結果(JSON)をブラウザに表示する。


サーバアプリをとにかく簡単に作りたいので、Spring Bootを使います。

SPRING INITIALIZRで「Web」だけチェックを入れて、GradleプロジェクトとしてWebアプリのひな形をダウンロード、Eclipseにインポートします。

eclipse.png

これで、Webアプリの準備はできました。

DemoApplication.javaを普通に「Java Application」で実行すれば、EmbeddedのTomcatが8080ポートで起動するはずです。


Azure ADの認証画面へのリダイレクト

いよいよアプリを作ってみます。先ほどのインポートしたWebアプリのひな形に、RESTのエンドポイントを実装するため、コントローラクラスを追加します。

そして、ブラウザで http://localhost:8080/ にアクセスしたら、Azure ADの認証画面にリダイレクトするためのエンドポイントを追加します。


DemoController.java

package com.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DemoController {

private static final String AUTHORITY = "https://login.windows.net/common";
private static final String CLIENT_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
private static final String REDIRECT_URI = "http://localhost:8080/appointments";

@RequestMapping("/")
public String getAppointments() {

return "redirect:"
+ AUTHORITY + "/oauth2/authorize"
+ "?response_type=code"
+ "&client_id=" + CLIENT_ID
+ "&redirect_uri=" + REDIRECT_URI;

}

}


CLIENT_ID には、Azure ADから発行されたクライアントIDを設定します。

REDIRECT_URI にも、Azure ADに登録したリダイレクトURIを設定します。

(今回は http://localhost:8080/appointments がリダイレクトURI)

実行して、ブラウザで http://localhost:8080/ にアクセスしてみます。

Azure ADの認証画面にリダイレクトされるので、Dynamics CRMのユーザでサインインします。

signin.png

サインインが成功すれば、REDIRECT_URI のアドレスに再度リダイレクトされます。

ただ、まだREDIRECT_URIに対応したエンドポイントを作成してないのでエラーになります。

localhost8080.png

でもちゃんと、http://localhost:8080/appointments にリダイレクトされました。


Dynamics CRMからアクセストークン取得

先ほどのソースに、REDIRECT_URI のアドレスに対応するエンドポイントを追加します。

Azure ADからリダイレクトされた際に、Azure ADはリクエストパラメータのcode認証コードを付与してきました。

この認証コードクライアントIDを使って、Dynamics CRMからアクセストークンを取得します。


Web APIはOAuth2.0での認証が必要なため、C#のサンプルではNuGet よりADAL (Active Directory Authentication Library) を追加して認証処理を行っていました。

Javaなので、C#のライブラリは使えません。

しかし、MavenRepositoryで「ADAL」を検索しみると、Javaのライブラリがちゃんとありました。

build.gradle の dependencies に 'com.microsoft.azure:adal4j:1.1.2' を追加します。

あとは、サンプルを見つつ、それっぽく認証の処理を実装してみます。


DemoController.java


private static final String RESOURCE = "https://<CRM組織名>.crm7.dynamics.com";

@RequestMapping("/appointments")
@ResponseBody
public String getAppointments(@RequestParam("code") String authorizationCode) throws Exception {

// 認証してアクセストークンを取得
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireTokenByAuthorizationCode(
authorizationCode,
RESOURCE,
CLIENT_ID,
new URI(REDIRECT_URI), null);
result = future.get();
} finally {
service.shutdown();
}
String accessToken = result.getAccessToken();

return null;

}


認証サーバのURL("https://login.windows.net/common")を与えて、AuthenticationContextのインスタンスを生成します。

AuthenticationContextは、アクセストークンを取得するメソッドをいくつも実装していますが、今回は認証コードを元にアクセストークンを取得する acquireTokenByAuthorizationCode を使います。

引数にAzure ADが付与してきた認証コードと、Azure ADから発行されたクライアントID、Azure ADに登録したリダイレクトURIを指定します。

認証が成功すれば、認証された情報がつまった AuthenticationResult が返されます。

ここにアクセストークンやリフレッシュトークンが入っています。

アクセストークンは、AuthenticationResultのgetAccessTokenメソッドで取得できます。

これでやっと、Web APIが叩けそうです。


Dynamics CRMのWeb APIを実行

アクセストークンが取得できたので、それをHTTPヘッダに仕込んで、APIをコールしてみます。

Microsoft Dynamics CRM Web API Preview Entity Types Referenceを見てみると、たくさんのエンティティへのアクセスが提供されていることが分かります。

今回はDynamics CRMに「予定」として登録したデータ、appointment Entityを取得してみます。

ドキュメントによると、URLはDynamics CRMの URL+/api/data/appointments でいけそうです。


appointment Entity

Base Type: activitypointer

Commitment representing a time interval with start/end times and duration.

Collection URL: [organization URI]/api/data/appointments

Display Name: Appointment

PrimaryKey: activityid


先ほどアクセストークンを取得した行辺りから、APIをコールする処理を追記します。

なのことはない、Apache HttpClientでGETリクエストを投げて、レスポンスを文字列にして返すだけです。


DemoController.java


String accessToken = result.getAccessToken();

// 取得したアクセストークンでDynamicsCRMのAPIをコール
String appointments = null;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet(RESOURCE + "/api/data/appointments");
httpGet.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
appointments = EntityUtils.toString(entity);
}
}
return appointments;


最終的にソースは次のようになりました。


DemoController.java

package com.example;

import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;

@Controller
public class DemoController {

private static final String AUTHORITY = "https://login.windows.net/common";
private static final String RESOURCE = "https://<CRM組織名>.crm7.dynamics.com";
private static final String CLIENT_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
private static final String REDIRECT_URI = "http://localhost:8080/appointments";

@RequestMapping("/")
public String getAppointments() {

return "redirect:"
+ AUTHORITY
+ "/oauth2/authorize"
+ "?response_type=code"
+ "&client_id=" + CLIENT_ID
+ "&redirect_uri=" + REDIRECT_URI;

}

@RequestMapping("/appointments")
@ResponseBody
public String getAppointments(@RequestParam("code") String authorizationCode) throws Exception {

// 認証してアクセストークンを取得
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future =
context.acquireTokenByAuthorizationCode(
authorizationCode,
RESOURCE,
CLIENT_ID,
new URI(REDIRECT_URI), null);
result = future.get();
} finally {
service.shutdown();
}
String accessToken = result.getAccessToken();

// 取得したアクセストークンでDynamicsCRMのAPIをコール
String appointments = null;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet(RESOURCE + "/api/data/appointments");
httpGet.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
appointments = EntityUtils.toString(entity, "utf-8");
}
}
return appointments;

}

}


実行して、ブラウザでhttp://localhost:8080/にアクセスすると、Dynamics CRMで「予定」として登録した情報が、JSONで返ってきていることが確認できました。

{

@odata.context: "https://<CRM組織名>.crm7.dynamics.com/api/data/$metadata#appointments",
value: [
{
@odata.etag: "W/"012345"",
scheduledstart: "2015-12-05T06:00:00Z",
scheduleddurationminutes: 60,
statuscode: 5,
isbilled: false,
description: "Dynamics CRM Online 2015 Web API をJavaから使ってみる",
createdon: "2015-12-04T05:54:32Z",
statecode: 3,
location: "品川",
subject: "Dynamics CRM Online 2015 Web APIの試行",
ownerid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
modifiedon: "2015-12-04T06:34:35Z",
versionnumber: 584089,
prioritycode: 2,
timezoneruleversionnumber: 0,
isregularactivity: true,
isalldayevent: false,
ismapiprivate: false,
modifiedby: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
activitytypecode: "appointment",
instancetypecode: 0,
isworkflowcreated: false,
createdby: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
attachmenterrors: 0,
owningbusinessunit: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
owninguser: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
activityid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
scheduledend: "2015-12-05T07:00:00Z"
}
]
}


まとめ

今回はWeb APIを使っみることに必要な、最低限のソースだけを紹介しました。

認証さえうまくいけば、あとは普通のWeb APIとしてJavaから快適に使うことができそうです。