メッセージを送信する


メッセージを送信する

Amazon Device Messaging(ADM)を使用してアプリのインスタンスにメッセージを送信するには、開発者サーバーで以下の準備が整っている必要があります。

メッセージペイロードと一意性

ADMでは、ペイロードサイズが最大6KBまでのメッセージを送信できます。たとえば、インスタントメッセージプログラムを介して、次のようなペイロードのメッセージを送信できます。

"data":
   {
   "from":"Sam",
   "message":"Hey, Max.How are you?",
   "time":"10/26/2012 09:10:00"
   }

一方、「同期」タイプの単純なステータスメッセージなど、ペイロードデータがまったく含まれないメッセージもあります。このようなメッセージは、アプリやユーザーに同期の状況を通知するだけで、一意データのペイロード転送は行いません。この場合、空のdataパラメーターを渡し、consolidationKeyパラメーターの値を含めます。この連結キーがあることで、ADMは冗長ペイロードを持つ同期メッセージを識別でき、メッセージのうちの1つだけをアプリに送信するよう試みます。

"data":{},
"consolidationKey":"Sync"

ただし、ペイロードを持つメッセージが一意ではない場合もあります。その際も、consolidationKeyパラメーターを使用してメッセージを連結することができます(可能な場合)。

"data":
   {
   "message":"Download \"Catcher in the Rye\"",
   "url":"https:\/\/s3.amazon.com\/CatcherInTheRye"
   },
"consolidationKey":"NewDownload"

リクエスト形式

メッセージ送信時に、開発者サーバーコンポーネント(以下「開発者サーバー」といいます)は次のようなHTTP POSTリクエストを発行します。

POST /messaging/registrations/(送信先アプリインスタンスの登録ID)/messages HTTP/1.1
Host: api.amazon.com
Authorization: Bearer (アクセストークン)
Content-Type: application/json
X-Amzn-Type-Version: com.amazon.device.messaging.ADMMessage@1.0
Accept: application/json
X-Amzn-Accept-Type: com.amazon.device.messaging.ADMSendResult@1.0

{
    "data":{"key1":"value1","key2":"value2"},
    "consolidationKey":"Some Key",
    "expiresAfter":86400
}

リクエストの先頭2行では、POSTリクエストのURLを指定しています。このURLには、メッセージを受信するアプリインスタンスの登録IDを必ず含めてください。この登録IDは、アプリインスタンスから事前に取得しておきます。アプリから開発者サーバーに登録IDを送信する方法の例については、ADM SDKダウンロードパッケージに含まれるADMサンプルアプリを参照してください。

https://api.amazon.com/messaging/registrations/(送信先アプリインスタンスの登録ID)/messages

リクエスト自体は、ヘッダーとメッセージ本文で構成されています。ヘッダーには、次のフィールドを含めてください。

フィールド 説明
Authorization 現在のアクセストークンを指定します。有効値: Bearer (アクセストークン) Authorization: Bearer Atc|MQEWYJxEnP3I1ND03ZzbY_NxQkA7Kn7Aioev _OfMRcyVQ4NxGzJMEaKJ8f0lSOiV-yW270o6fnkI
Content-Type 有効値:application/json Content-Type: application/json
X-Amzn-Type-Version 有効値:com.amazon.device.messaging.ADMMessage@1.0 X-Amzn-Type-Version: com.amazon.device.messaging.ADMMessage@1.0
Accept 有効値:application/json Accept: application/json
X-Amzn-Accept-Type 有効値:com.amazon.device.messaging.ADMSendResult@1.0 X-Amzn-Accept-Type: com.amazon.device.messaging.ADMSendResult@1.0

メッセージ本文のコンテンツには、次のパラメーターを含む文字列で構成されたJSONObjectを指定する必要があります。

パラメーター 説明
data メッセージと共に送信するペイロードデータ。データはJSON形式のキーと値のペアから成り、いずれもString値である必要があります。キーと値のほか、これらを囲む引用符、キーと値の間を区切る「:」文字、ペアを区切るコンマ、フィールドを囲む中かっこを含めた合計データサイズが、6KBを超えることはできません。キーと値のペア間の空白は、ペイロードサイズの計算に含まれません。同期メッセージのように、メッセージにペイロードが含まれない場合は、空のオブジェクト("data":{}など)を渡すことができます。 "data":{ "from":"Sam", "message":"Hey, Max.How are you?", "time":"10/26/2012 09:10:00" }
consolidationKey オプション値。​これは、複数のメッセージが論理的に同一であることを示すための任意の文字列です。それまでエンキューされていたメッセージに優先して、この新しいメッセージを使用することもできます。それまでエンキューされていたメッセージが送信されないという保証はありません。連結キーは64文字以下にする必要があります。 "consolidationKey":"SyncNow"
expiresAfter オプション値。デバイスがオフラインの場合に、ADMがメッセージを保持す秒数を表します。この時間が経過すると、メッセージは破棄される可能性があります。有効値の範囲は60(1分間)~2678400(31日間)で、デフォルト値は604800(1週間)になっています。 "expiresAfter":86400
md5 オプション値。これは、dataパラメーターのMD5チェックサムをBase64でエンコードしたものです。md5パラメーターの値を指定すると、値が正確かどうかが検証されます。値を指定しなかった場合は、サーバーによって値が計算されます。どちらの場合も、md5パラメーターの値がデバイスに渡され、計算値がx-amzn-data-md5レスポンスヘッダー内で返されます。 MD5チェックサムの計算例については、ADM Sample Codeフォルダを参照してください。

md5値をADMサーバーの値と一致させるには、このパラメーターの値を次のように計算する必要があります。

  1. UTF-8コードユニットベースのキー比較を使用して、メッセージのキーと値のペアをソートします。
  2. 一連のペアを"key1:value1,key2:value2"の形式で連結します。
  3. キーと値のペア間に空白があれば削除します。「:」文字と、キーや値の間に空白を置くことはできません。各ペアと「,」文字の間も同様です。
  4. 結果の文字列のUTF-8バイトを使用して、SHA-256値を計算します。計算にはRFC 1321で定義されたアルゴリズムを使用します。
  5. 計算の128ビット出力をBase64でエンコードします。

レスポンス形式

ADMサーバーは、POSTリクエストメッセージを正常に受信して解釈した後、次のようなHTTPレスポンスメッセージを送信します。

HTTP/1.1 200
X-Amzn-Data-md5: t5psxALRTM7WN30Q8f20tw==
X-Amzn-RequestId: e8bef3ce-242e-11e2-8484-47f4656fc00d
Content-Type: application/json
X-Amzn-Type-Version: com.amazon.device.messaging.ADMSendResult@1.0
Content-Length: 308

{"registrationID":"amzn1.adm-registration.v1.Y29tLmFtYXpvbi5EZXZpY2VNZXNzYWdpbmcu
YL3FOMUlCWEdpdm5TZ3RWbm9XUT0hN0lrSU1YUlNSVVBpT2pOd0lnWktvUT09"}

メッセージが受理され、デバイスへの配信用にエンキューされると、ADMからステータスコード200が返されます。その場合、レスポンスメッセージのJSONObjectに次のパラメーターが含まれます。

  • registrationID: アプリインスタンスの現在の登録ID。この値が開発者サーバーから渡されたものと異なる場合、開発者サーバーはレコードを更新してこの値を使用する必要があります。

メッセージが正常に受理されなかった場合は、ADMからエラー(200以外)のステータスコードが返されます。その場合、レスポンスメッセージのJSONObjectの本文に次のパラメーターが含まれている可能性があります。

  • reason: リクエストが受理されなかった理由。

ADMエラーステータスコードとその説明は次のとおりです。

コード 説明
400 入力の引数に無効なものがありました。使用可能なステータスコードは、 InvalidRegistrationIdInvalidDataInvalidConsolidationKeyInvalidExpirationInvalidChecksumInvalidTypeUnregisteredです。Unregisteredは、登録IDに関連付けられたアプリインスタンスがメッセージを受信できなくなっていることを示します。Unregisteredが返されるのは、アプリインスタンスが登録されたデバイスの所有者が変わった場合や、メッセージを受信しないリクエストをアプリインスタンスが行った場合です。InvalidRegistrationIdは、提供されたアクセストークンで識別できる送信元に登録IDが対応していないことを示します。 "reason": "InvalidRegistrationId"
401 提供されたアクセストークンが無効です。送信元は、アクセストークンを更新する必要があります。この更新については、アクセストークンをリクエストするを参照してください。 "reason":"AccessTokenExpired"
413 dataパラメーターで渡されたメッセージペイロードが、許容されている最大データサイズ(6KB)を超えています。 "reason":"MessageTooLarge"
429 リクエスト側が、許容されている最大メッセージレートを超過しました。送信元は、レスポンスに含まれるRetry-Afterヘッダーに従って、後で再試行する必要があります。ADMでは、一定時間内に送信できるメッセージ数を制限することで、高可用性が確保されています。具体的なキャパシティ要件がある場合は、お問い合わせください。リクエストに当たり、氏名、会社名、Eメールアドレス、必要なTPS(1秒あたりのトランザクション数)制限、理由をお知らせください。 "reason":"MaxRateExceeded"
500 内部サーバーエラーが発生しました。リクエストした側は、レスポンスに含まれているRetry-Afterヘッダーに従って、後で再試行する必要があります。 なし
503 サーバーが一時的に利用できない状態になっています。リクエストした側は、レスポンスに含まれているRetry-Afterヘッダーに従って、後で再試行する必要があります。 なし

レスポンスヘッダーには、次のフィールドがあります。

フィールド 説明
X-Amzn-Data-md5 Base64エンコードで計算した、データフィールドのMD5チェックサム。 X-Amzn-Data-md5: t5psxALRTM7WN30Q8f20tw==
X-Amzn-RequestId リクエストを一意に識別するためにADMによって作成された値。万が一、ADMに問題が発生した場合は、Amazon側でこの値を用いてトラブルシューティングを行います。 X-Amzn-RequestId: b55857e7-242d-11e2-8484-47f4656fc00d
Retry-After エラーレスポンス429、500、503が発生した場合に、​​このフィールドが返されます。Retry-Afterは、サービスが利用できない時間の長さ(予測)を示します。有効値は、レスポンス後の秒数(10進法の整数)またはHTTP形式の日付です。この値で有効な形式については、HTTP/1.1仕様のセクション14.37を参照してください。 Retry-After: 120
Content-Type リソースのコンテンツタイプ:application/json Content-Type: application/json
X-Amzn-Type-Version レスポンスの形式を示します。 X-Amzn-Type-Version: com.amazon.device.messaging.ADMSendResult@1.0

メッセージリクエストの実行とレスポンス処理

次のコードサンプルは、開発者サーバーのソフトウェアでメッセージ送信のリクエストを行い、ADMサーバーのレスポンスを処理する方法を例示したものです。

    /**
     * 特定のアプリインスタンスへのメッセージ配信をADMにリクエストします。
     */
    public void sendMessageToDevice(String registrationId, String accessToken) throws Exception
    {
        // メッセージのJSONペイロード表現。
        JSONObject payload = new JSONObject();

        // メッセージ内容のキーと値のペアを定義し、メッセージペイロードに
        // 追加します。
        JSONObject data = new JSONObject();
        data.put("firstKey", "firstValue");
        data.put("secondKey", "secondValue");
        payload.put("data", data);

        // 連結キーを追加します。アプリインスタンスに対して、同じ連結キーを持つ
        // 複数のメッセージの配信が保留になっている場合、ADMでは最後に追加されたメッセージの
        // 配信が試行されます。
        payload.put("consolidationKey", "ADM_Enqueue_Sample");

        // メッセージに、expiresAfter値(1日間)を追加します。ターゲットのアプリインスタンスが
        // expiresAfter期間内にオンラインにならなかった場合、メッセージは配信されません。
        payload.put("expiresAfter", 86400);

        // メッセージをJSONオブジェクトから文字列に変換します。
        String payloadString = payload.toString();

        // ベースURLを設定します。このURLには、目的のアプリインスタンスの登録IDに
        // 置き換わる部分を含めてください。String.formatを使用してURLを作成するため、
        // %1$sは置き換わる部分を表します。
        String admUrlTemplate = "https://api.amazon.com/messaging/registrations/%1$s/messages";

        URL admUrl = new URL(String.format(admUrlTemplate,registrationId));

        // POSTリクエスト用のHTTPS接続を生成します。HTTP接続は
        // 行えません。
        HttpsURLConnection conn = (HttpsURLConnection) admUrl.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);

        // コンテンツタイプを設定し、ヘッダーを受け取ります。
        conn.setRequestProperty("content-type", "application/json");
        conn.setRequestProperty("accept", "application/json");
        conn.setRequestProperty("X-Amzn-Type-Version", "com.amazon.device.messaging.ADMMessage@1.0");
        conn.setRequestProperty("X-Amzn-Accept-Type", "com.amazon.device.messaging.ADMSendResult@1.0");

        // ヘッダーとして認証トークンを追加します。
        conn.setRequestProperty("Authorization", "Bearer " + accessToken);

        // 接続の出力ストリームを取得し、メッセージペイロードを書き込みます。
        OutputStream os = conn.getOutputStream();
        os.write(payloadString.getBytes(), 0, payloadString.getBytes().length);
        os.flush();
        conn.connect();

        // 接続からレスポンスコードを取得します。
        int responseCode = conn.getResponseCode();

        // 失敗のレスポンスを受信したかどうか確認し、受信した場合は失敗の理由を取得します。
        if( responseCode != 200)
        {
            if( responseCode == 401 )
            {
                // レスポンスコード401を受信した場合は、アクセストークンの期限が切れていることを意味します。トークンを更新します。
                // このリクエストは再試行できます。
            }

            String errorContent = parseResponse(conn.getErrorStream());
            throw new RuntimeException(String.format("ERROR: The enqueue request failed with a " +
                                             "%d response code, with the following message: %s",
                                             responseCode, errorContent));
        }
        else
        {
            // リクエストに成功しました。レスポンスには、アプリインスタンスの正規登録IDが含まれていますが、
            // リクエストに使用されたものとは異なることもあります。

            String responseContent = parseResponse(conn.getInputStream());
            JSONObject parsedObject = new JSONObject(responseContent);

            String canonicalRegistrationId = parsedObject.getString("registrationID");

            // 2つの登録IDが異なるかどうか確認します。
            if(!canonicalRegistrationId.equals(registrationId))
            {
                // この時点で、このアプリインスタンス固有の適切な登録IDを使用して、
                // 登録IDの値が格納されるデータ構造を更新する必要があります。
            }
        }

    }

    private String parseResponse(InputStream in) throws Exception
    {
        // 入力ストリームから読み取り、Stringに変換します。
        InputStreamReader inputStream = new InputStreamReader(in);
        BufferedReader buff = new BufferedReader(inputStream);

        StringBuilder sb = new StringBuilder();
        String line = buff.readLine();
        while(line != null)
       {
          sb.append(line);
          line = buff.readLine();
        }

        return sb.toString();
    }