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

# Dynamics CRM Online 2015 Web API Preview
Dynamics CRM Online のWeb APIが、プレビュー版ではありますが、リリースされていました。
[Dynamics CRM Online 2015 Web API Preview](https://msdn.microsoft.com/en-us/dynamics/crm/webapipreview.aspx)
Web APIでDynamics CRMの機能を使うことができれば、何かと重宝しそうです。
早速サンプルを参考に... と思いきや、[samples](https://msdn.microsoft.com/en-us/dynamics/crm/webapipreview.aspx#samples)はやはりC#が中心でした。
しかし、Web APIなのでJavaからだって普通に叩けば、JSONあたりの形式でレスポンスが返ってくるはず...

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

# 事前準備
ところで、ある程度自由に使える下記2つの環境がある、といふのが大前提です。
- Dynamics CRM Online
- Azure Active Directory

さて、まずは認証です。
認証してアクセストークンさえ頂戴すれば、あとはあれこれAPIを叩くだけです。
[認証についてのドキュメント](https://msdn.microsoft.com/en-us/dynamics/crm/mt149073.aspx)を見てみます。

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

なるほど、アクセストークンは、Azure ADから発行されるのを使うらしいです。
Azure ADとDynamics CRM の設定をあれこれする必要があるぽいので、Microsoftの中の人のブログを参考にしてみます。
 - [Dynamics CRM Online 2015 Update 1 新機能: Web API 開発者プレビュー その 1](http://blogs.msdn.com/b/crmjapan/archive/2015/06/19/dynamics-crm-online-2015-update-1-sdk-web-api-preview-1.aspx)
 - [Dynamics CRM Online 2015 Update 1 新機能: Web API 開発者プレビュー その 2](http://blogs.msdn.com/b/crmjapan/archive/2015/06/22/dynamics-crm-online-2015-update-1-sdk-web-api-preview-2.aspx)

上記のブログで、Dynamics CRMのWeb API機能の有効化と、Azure ADへのDynamics CRMの認証設定の手順がわかりました。
実際にやってみて、Azure ADにリダイレクトURIを登録、クライアントIDも発行されました。
![crm-web-api-test.jpg](https://qiita-image-store.s3.amazonaws.com/0/60568/e68be181-de34-1f06-949f-6697389a2b56.jpeg)
今回は http\://localhost:8080/appointments をリダイレクトURIとしました。
事前準備としては、これで良さそうです。

# Javaアプリの準備
先に挙げたブログでは、C#でアプリを作成する際のサンプルコードが記載されていました。
これを参考に、Javaでサンプルを作成してみます。
Azure ADの認証画面にリダイレクトすることを考えると、Webアプリを作成した方が簡単そうです。
ということで、こんな感じのWebアプリ(サーバアプリ)を作ってみます。
***
1. ブラウザで localhost:8080 にアクセスしたら、Azure ADの認証画面にリダイレクトする。
1. 認証が成功してAzure ADに登録したリダイレクトURIに戻ってきたら、Dynamics CRMからアクセストークンを取得する。
1. 取得したアクセストークンを使って、Dynamics CRMのWeb APIを実行して結果(JSON)をブラウザに表示する。

***
サーバアプリをとにかく簡単に作りたいので、Spring Bootを使います。
[SPRING INITIALIZR](https://start.spring.io/)で「Web」だけチェックを入れて、GradleプロジェクトとしてWebアプリのひな形をダウンロード、Eclipseにインポートします。
![eclipse.png](https://qiita-image-store.s3.amazonaws.com/0/60568/e2772a97-898d-c18a-d47f-df27237acd85.png)
これで、Webアプリの準備はできました。
DemoApplication.javaを普通に「Java Application」で実行すれば、EmbeddedのTomcatが8080ポートで起動するはずです。

# Azure ADの認証画面へのリダイレクト
いよいよアプリを作ってみます。先ほどのインポートしたWebアプリのひな形に、RESTのエンドポイントを実装するため、コントローラクラスを追加します。
そして、ブラウザで http\://localhost:8080/ にアクセスしたら、Azure ADの認証画面にリダイレクトするためのエンドポイントを追加します。

```java: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](https://qiita-image-store.s3.amazonaws.com/0/60568/bf4f9348-6298-65d9-9bc4-8cf6e3296246.png)


サインインが成功すれば、*REDIRECT_URI* のアドレスに再度リダイレクトされます。
ただ、まだ*REDIRECT_URI*に対応したエンドポイントを作成してないのでエラーになります。
![localhost8080.png](https://qiita-image-store.s3.amazonaws.com/0/60568/b0e85085-b48c-0807-a0b2-166004714825.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](http://mvnrepository.com/artifact/com.microsoft.azure/adal4j)で「ADAL」を検索しみると、Javaのライブラリがちゃんとありました。
build.gradle の dependencies に 'com.microsoft.azure:adal4j:1.1.2' を追加します。
あとは、サンプルを見つつ、それっぽく認証の処理を実装してみます。

```java: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](https://msdn.microsoft.com/dynamics/crm/dn927174)を見てみると、たくさんのエンティティへのアクセスが提供されていることが分かります。
今回はDynamics CRMに「予定」として登録したデータ、[appointment Entity]
(https://msdn.microsoft.com/dynamics/crm/dn927174#appointment)を取得してみます。
ドキュメントによると、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リクエストを投げて、レスポンスを文字列にして返すだけです。

```java: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;
```

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

```java: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で返ってきていることが確認できました。

```xml
{
@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から快適に使うことができそうです。