Amazon Simple Notification Service
開発者ガイド (API バージョン 2010-03-31)

プラットフォームエンドポイントの作成とデバイストークンの管理

プッシュ通知サービスへのアプリケーションおよびモバイルデバイスの登録時に、プッシュ通知サービスはデバイストークンを返します。Amazon SNS はデバイストークンを使用して、モバイルエンドポイントを作成します。ここに直接プッシュ通知メッセージを送信できます。詳細については、「前提条件」および「Amazon SNS モバイルプッシュを使用するために必要な手順の概要」を参照してください。

このセクションでは、プラットフォームエンドポイントを作成してデバイストークンを管理するための推奨される方法について説明します。

プラットフォームエンドポイントの作成

Amazon SNS を使用してアプリケーションにプッシュ通知を送信するには、まずプラットフォームエンドポイントの作成アクションを呼び出すことで、そのアプリケーションのデバイストークンを Amazon SNS に登録する必要があります。このアクションは、パラメーターとしてプラットフォームアプリケーションの Amazon リソースネーム (ARN) およびデバイストークンを使用し、作成されたプラットフォームエンドポイントの ARN を返します。

プラットフォームエンドポイントの作成アクションは、次の処理を行います。

  • プラットフォームエンドポイントが既に存在する場合、再度作成しないでください。呼び出し元に既存のプラットフォームエンドポイントの ARN を返します。

  • デバイストークンが同じだが設定が異なるプラットフォームエンドポイントが既に存在する場合、再度作成しないでください。呼び出し元に例外をスローします。

  • プラットフォームエンドポイントが存在しない場合は、作成します。呼び出し元に新しく作成したプラットフォームエンドポイントの ARN を返します。

このアプローチでは、作業エンドポイントが常に提供されるとは限らないため、アプリケーションが起動するたびにプラットフォームエンドポイントの作成アクションを呼び出さないでください。これは、たとえばアプリケーションがアンインストールされて同じデバイスに再インストールされ、そのアプリケーションのエンドポイントが既に存在しているが無効な場合などに発生する可能性があります。登録プロセスに成功すると、以下のことが達成されます。

  1. このアプリケーションデバイスの組み合わせにプラットフォームエンドポイントが存在することを確認します。

  2. プラットフォームエンドポイントのデバイストークンが最新の有効なデバイストークンであることを確認します。

  3. プラットフォームエンドポイントが有効であり、使用できる状態にあることを確認します。

擬似コード

次の擬似コードは、さまざまな開始条件において有効な最新の作業プラットフォームエンドポイントを作成するための推奨される方法について説明します。このアプローチは、アプリケーションが初めて登録されるかどうか、このアプリケーションのプラットフォームエンドポイントが既に存在するかどうか、プラットフォームエンドポイントが有効かどうか、適切なデバイストークンがあるかどうかなどに関係なく機能します。重複するプラットフォームエンドポイントが作成されたり、既に最新のプラットフォームエンドポイントが有効になっている場合に既存のプラットフォームエンドポイントが変更されたりしないため、連続して複数回呼び出しても安全です。

retrieve the latest device token from the mobile operating system if (the platform endpoint ARN is not stored) # this is a first-time registration call create platform endpoint store the returned platform endpoint ARN endif call get endpoint attributes on the platform endpoint ARN if (while getting the attributes a not-found exception is thrown) # the platform endpoint was deleted call create platform endpoint with the latest device token store the returned platform endpoint ARN else if (the device token in the endpoint does not match the latest one) or (get endpoint attributes shows the endpoint as disabled) call set endpoint attributes to set the latest device token and then enable the platform endpoint endif endif

このアプローチは、アプリケーションが自身を登録または再登録するときいつでも使用できます。また、デバイストークンの変更について Amazon SNS に通知するときも使用できます。この場合、最新のデバイストークン値を持つアクションを呼び出すだけです。このアプローチについて注意が必要な点は次のとおりです。

  • プラットフォームエンドポイントの作成アクションの呼び出しが考えられる 2 つのケースがあります。アプリケーションが自身のプラットフォームエンドポイント ARN を認識していない、ごく最初の時点で呼び出される可能性があります。これは初回登録時に発生します。さらに、最初のエンドポイント属性の取得アクションが not-found 例外で失敗した場合にも呼び出されます。これは、アプリケーションがエンドポイント ARN を認識しているが、削除された場合に発生する可能性があります。

  • エンドポイント属性の取得アクションは、プラットフォームエンドポイントが作成されたばかりの場合でもプラットフォームエンドポイントの状態を確認するために呼び出されます。これは、プラットフォームエンドポイントが既に存在するが無効になっている場合に発生します。この場合、プラットフォームエンドポイントの作成アクションが成功しますが、プラットフォームエンドポイントは有効にならないため、成功を返す前にプラットフォームエンドポイントの状態をもう一度確認する必要があります。

Java の例

前の擬似コードの Java での実装例を示します。

class RegistrationExample { AmazonSNSClient client = new AmazonSNSClient(); //provide credentials here private void registerWithSNS() { String endpointArn = retrieveEndpointArn(); String token = "Retrieved from the mobile operating system"; boolean updateNeeded = false; boolean createNeeded = (null == endpointArn); if (createNeeded) { // No platform endpoint ARN is stored; need to call createEndpoint. endpointArn = createEndpoint(); createNeeded = false; } System.out.println("Retrieving platform endpoint data..."); // Look up the platform endpoint and make sure the data in it is current, even if // it was just created. try { GetEndpointAttributesRequest geaReq = new GetEndpointAttributesRequest() .withEndpointArn(endpointArn); GetEndpointAttributesResult geaRes = client.getEndpointAttributes(geaReq); updateNeeded = !geaRes.getAttributes().get("Token").equals(token) || !geaRes.getAttributes().get("Enabled").equalsIgnoreCase("true"); } catch (NotFoundException nfe) { // We had a stored ARN, but the platform endpoint associated with it // disappeared. Recreate it. createNeeded = true; } if (createNeeded) { createEndpoint(); } System.out.println("updateNeeded = " + updateNeeded if (updateNeeded) { // The platform endpoint is out of sync with the current data; // update the token and enable it. System.out.println("Updating platform endpoint " + endpointArn); Map attribs = new HashMap(); attribs.put("Token", token); attribs.put("Enabled", "true"); SetEndpointAttributesRequest saeReq = new SetEndpointAttributesRequest() .withEndpointArn(endpointArn) .withAttributes(attribs); client.setEndpointAttributes(saeReq); } } /** * @return never null * */ private String createEndpoint() { String endpointArn = null; try { System.out.println("Creating platform endpoint with token " + token); CreatePlatformEndpointRequest cpeReq = new CreatePlatformEndpointRequest() .withPlatformApplicationArn(applicationArn) .withToken(token); CreatePlatformEndpointResult cpeRes = client .createPlatformEndpoint(cpeReq); endpointArn = cpeRes.getEndpointArn(); } catch (InvalidParameterException ipe) { String message = ipe.getErrorMessage(); System.out.println("Exception message: " + message); Pattern p = Pattern .compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " + "with the same token.*"); Matcher m = p.matcher(message); if (m.matches()) { // The platform endpoint already exists for this token, but with // additional custom data that // createEndpoint doesn't want to overwrite. Just use the // existing platform endpoint. endpointArn = m.group(1); } else { // Rethrow the exception, the input is actually bad. throw ipe; } } storeEndpointArn(endpointArn); return endpointArn; } /** * @return the ARN the app was registered under previously, or null if no * platform endpoint ARN is stored. */ private String retrieveEndpointArn() { // Retrieve the platform endpoint ARN from permanent storage, // or return null if null is stored. return arnStorage; } /** * Stores the platform endpoint ARN in permanent storage for lookup next time. * */ private void storeEndpointArn(String endpointArn) { // Write the platform endpoint ARN to permanent storage. arnStorage = endpointArn; } }

この実装で興味深いのは、InvalidParameterExceptioncreateEndpoint メソッドでどのように処理されるかという点です。Amazon SNS は、既存のプラットフォームエンドポイントに同じデバイストークンと null 以外の CustomUserData フィールドがあるとき、プラットフォームエンドポイントの作成リクエストを拒否します。代わりの方法では、CustomUserData が上書きされる (したがって、失われる) ためです。前のコードの createEndpoint メソッドは、Amazon SNS によってスローされた InvalidParameterException をキャプチャし、この特定の理由でスローされたかどうかを確認します。その理由でスローされた場合、例外から既存のプラットフォームエンドポイントの ARN が抽出されます。適切なデバイストークンを持つプラットフォームエンドポイントが存在するため、この処理は成功します。

詳細については、「Amazon SNS モバイルプッシュ API の使用」を参照してください。

トラブルシューティング

古いデバイストークンを使用してプラットフォームエンドポイントの作成を繰り返し呼び出す

特に GCM エンドポイントの場合、アプリケーションが発行した最初のデバイストークンを保存してから、アプリケーションが起動するたびにそのデバイストークンを使用してプラットフォームエンドポイントの作成を呼び出すのが最適に思えるかもしれません。これは、アプリケーションがデバイストークンの状態を管理する必要がなくなり、Amazon SNS がサービストークンを最新の値に自動的に更新するため、適切に思える場合があります。しかし、この解決策には多くの深刻な問題があります。

  • Amazon SNS は、期限切れのデバイストークンを新しいデバイストークンに更新するために GCM からのフィードバックを必要とします。GCM は、しばらくの間古いデバイストークンの情報を保持しますが、無期限ではありません。GCM が古いデバイストークンと新しいデバイストークンの関連性の認識を失うと、Amazon SNS はプラットフォームエンドポイントに保存されたデバイストークンを適切な値に更新することができなくなります。代わりに、プラットフォームエンドポイントが無効になるだけです。

  • プラットフォームアプリケーションには、同じデバイストークンに対応する複数のプラットフォームエンドポイントが含められます。

  • Amazon SNS では、同じデバイストークンを使用して作成できるプラットフォームエンドポイントの数に制限が適用されます。最終的に、新しいエンドポイントの作成は無効なパラメーターの例外によって失敗し、"This endpoint is already registered with a different token" というエラーメッセージが表示されます。

無効なデバイストークンに関連付けられたプラットフォームエンドポイントを再度有効にする

モバイルプラットフォーム (APNS や GCM など) が、発行リクエストで使用されたデバイストークンが無効であったことを Amazon SNS に通知すると、Amazon SNS はそのデバイストークンに関連付けられたプラットフォームエンドポイントを無効にします。その場合、Amazon SNS はそのデバイストークンに対するそれ以降の発行を拒否します。プラットフォームエンドポイントを再度に有効にして発行を継続すれば最適と考える可能性がありますが、ほとんどの場合これはうまくいきません。発行されるメッセージは配信されず、プラットフォームエンドポイントはその後まもなく無効になります。

これは、プラットフォームエンドポイントに関連付けられたデバイストークンが間違いなく無効であるためです。プラットフォームエンドポイントは、インストールされているどのアプリケーションにも応答しないため、配信が成功することはありません。次回発行されると、モバイルプラットフォームはデバイストークンが無効であることを Amazon SNS にもう一度通知し、Amazon SNS はプラットフォームエンドポイントをもう一度無効にします。

無効なプラットフォームエンドポイントを再度有効にするには、有効なデバイストークンに関連付けた後 (エンドポイント属性の設定アクションを呼び出して)、有効にする必要があります。その場合のみ、そのプラットフォームエンドポイントへの配信は成功します。デバイストークンを更新しないでプラットフォームエンドポイントを有効にできるのは、そのエンドポイントに関連付けられたデバイストークンが無効であったが、再度有効となった場合のみです。これは、たとえばアプリケーションがアンインストールされて同じモバイルデバイスに再インストールされ、同じデバイストークンを受け取った場合などに発生します。上に示したアプローチではこれが行われます。関連付けられたデバイストークンが使用可能な最新のものであると確認してから、プラットフォームエンドポイントを再度有効にすることのみ確実に行ってください。