EWS(Exchange web services)を使ってメールチェック

# 何をするか

Java言語を使って、Exchangeサーバのメールをチェックしたい方向けです。

EWS JAVA API
https://github.com/OfficeDev/ews-java-api

# サンプルコード

Mavenプロジェクトが便利なので、Eclipseで適当にプロジェクトを作ります。

## 依存関係の追加

依存関係にewsの部品を追記します。

```xml:pom.xml
  <dependencies>
    <dependency>
      <groupId>com.microsoft.ews-java-api</groupId>
      <artifactId>ews-java-api</artifactId>
      <version>2.0</version>
    </dependency>
    <!-- あっても無くても良いけど、便利なので追加 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.4</version>
    </dependency>
  </dependencies>
```

## Java本体のサンプルコード

EWS APIのサンプルコードにもいろいろありますが、AD認証してメールの件名とIDを取得するパターンを作ってみました。
特定のサブフォルダも探して、最終受信メールの内容を表示するまでのコードです。

Exchangeは詳しくないのですが、EWSのAPIは管理者に聞くのがベストです。
ですが、一般ユーザに公開されていない場合もあると思います。
```https://ドメイン名/EWS/Exchange.asmx``` が一般的な設定では無いかと思われます。

Exchangeのバージョン指定
```ExchangeVersion.Exchange2010_SP2``` としていますが、2010以降も接続可能なようです。
Version2016でも接続OKでした。


```java:Sample.java
package example.com;

import java.net.URI;


import org.apache.commons.lang3.StringUtils;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.PropertySet;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
import microsoft.exchange.webservices.data.core.service.folder.Folder;
import microsoft.exchange.webservices.data.core.service.item.EmailMessage;
import microsoft.exchange.webservices.data.core.service.item.Item;
import microsoft.exchange.webservices.data.credential.ExchangeCredentials;
import microsoft.exchange.webservices.data.credential.WebCredentials;
import microsoft.exchange.webservices.data.property.complex.FolderId;
import microsoft.exchange.webservices.data.search.FindFoldersResults;
import microsoft.exchange.webservices.data.search.FindItemsResults;
import microsoft.exchange.webservices.data.search.FolderView;
import microsoft.exchange.webservices.data.search.ItemView;

public class Sample{

    public static void main(String[] args) {

        try {
            ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
            service.setTraceEnabled(true);
            ExchangeCredentials credentials = new WebCredentials("ユーザID", "パスワード", "ドメイン名");
            service.setCredentials(credentials);
            service.setUrl(new URI("https://www.eample.com/EWS/Exchange.asmx"));

            // 最新の5件を取得
            ItemView view = new ItemView(5);
            Folder inbox = Folder.bind(service, WellKnownFolderName.Inbox);
            FindItemsResults<Item> findResults = service.findItems(inbox.getId(), view);

            System.out.println("TotalCount: " + findResults.getTotalCount());

            service.loadPropertiesForItems(findResults, PropertySet.FirstClassProperties);
            for (Item item : findResults.getItems()) {
                // Do something with the item as shown
                System.out.println("id:      " + item.getId());
                System.out.println("subject: " + item.getSubject());
            }


            // inbox配下のサブフォルダ
            FolderId folderId = new FolderId();
            FindFoldersResults findResults1 = service.findFolders(WellKnownFolderName.Inbox, new FolderView(Integer.MAX_VALUE));
            for (Folder folder : findResults1.getFolders()) {
                // System.out.println("Count======" + folder.getChildFolderCount());
                System.out.println("Name:     " + folder.getDisplayName());
                System.out.println("FolderId: " + folder.getId());
                if(StringUtils.equals(folder.getDisplayName(), "サブフォルダ名")) {
                    System.out.println("FolderId OK:" + folder.getId());
                    folderId = folder.getId();
                }
            }

            // サブフォルダの検索
            ItemView view2 = new ItemView(1);
            FindItemsResults<Item> findResults2 = service.findItems(folderId, view2);
            service.loadPropertiesForItems(findResults2, PropertySet.FirstClassProperties);
            System.out.println("TotalCount-Sub : " + findResults2.getTotalCount());
            for (Item item : findResults2.getItems()) {
                // Do something with the item as shown
                System.out.println("id:      " + item.getId());
                System.out.println("subject: " + item.getSubject());

                EmailMessage message = EmailMessage.bind(service, item.getId());
                // Write the sender's name.
                System.out.println("getName: " + message.getSender().getName());
                System.out.println("getBody: " + message.getBody().toString());
                System.out.println("getDateTimeReceived: " + message.getDateTimeReceived());
            }
            service.close();
        } catch (Exception e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
    }
}

```

## 実行結果

実行結果のサンプル

```
TotalCount: 395
id:      AQMkADQzNzA2NGM5LTQ4ZjctNGZkNy1iM2YwLTRmYzE5ZDYzYzY1MwBGAAADp4n/Ol+y90Oxe9GeAbjpogcAe0gmLZK3Jk6ygjzqlWOM/AAF5TMi9wAAAD9m8fKeQ89DgbkNxsPfAIEAAAIkSAAAAA==
subject: ○×△について

(....)

Name:     サブフォルダ名
FolderId: AQMkADQzNzA2NGM5LTQ4ZjctNGZkNy1iM2YwLTRmYzE5ZDYzYzY1MwAuAAADp4n/Ol+y90Oxe9GeAbjpogEAfe9GAvjtzk+ywx55+U+gRAAF9T8AMgAAAA==


(....)

TotalCount-Sub : 3650
id:      AQMkADQzNzA2NGM5LTQ4ZjctNGZkNy1iM2YwLTRmYzE5ZDYzYzY1MwBGAAADp4n/Ol+y90Oxe9GeAbjpogcAfe9GAvjtzk+ywx55+U+gRAAF9T8AMAAAAD9m8fKeQ89DgbkNxsPfAIEAAAJmggAAAA==
subject: メールタイトル
getName: test@example.com
getBody:  メール本文
あいうえお
かきくけこ
getDateTimeReceived: Mon Sep 04 08:00:00 JST 2017
```


# 注意事項

使ってみて分かったことを書きます。
くれぐれもメールサーバ管理人には、迷惑をかけないようにしましょう。


* ParseError Exceptionが発生する

メールを取得した際に、エラーが発生することがありました。
EWSには色々とバグがあるようで、フォークしている方がいらっしゃいました。

https://github.com/RavnSystems/ews-java-api/tree/master-ravn
https://github.com/RavnSystems/ews-java-api/commit/b406a91f0a4b753ac6a12a2abfc3e350ac7d2508

* ServiceRequestExceptionが発生する

エラーを拾ってくるので、必ず対応が必要です。
注意深く処理を見ると、internalExecuteで接続しているのですが、コネクションをcloseしていないので、張りっぱなしにならないよう注意が必要です。