Pushy は、APN (iOS、macOS、Safari) プッシュ通知を送信するための Java ライブラリです。
Pushy は、Apple の HTTP/2 ベースの APN プロトコルを使用してプッシュ通知を送信し、TLS とトークンベースの認証の両方をサポートします。他のプッシュ通知ライブラリとは一線を画しており、徹底した文書化、非同期操作、産業規模の操作に向けた設計に重点を置いています。 Pushy を使用すると、APN ゲートウェイへの複数の並列接続を維持して、多数の異なるアプリケーション (「トピック」) に大量の通知を送信することが簡単かつ効率的になります。
私たちは、Pushy がすでに Java アプリケーションから APN プッシュ通知を送信するための最良のツールであると信じています。バグ レポートやプル リクエストを通じて、このツールをさらに改善するためにご協力いただければ幸いです。
開発またはテスト目的でプッシュ通知を送信するためのシンプルな GUI アプリケーションが必要な場合は、Pushy の姉妹プロジェクトである Pushy Console にも興味があるかもしれません。
Maven を使用している場合は、次の依存関係宣言を POM に追加することで、Pushy をプロジェクトに追加できます。
< dependency >
< groupId >com.eatthepath</ groupId >
< artifactId >pushy</ artifactId >
< version >0.15.4</ version >
</ dependency >
Maven (または Gradle など、Maven の依存関係を理解するもの) を使用しない場合は、Pushy を.jar
ファイルとしてダウンロードし、プロジェクトに直接追加できます。また、クラスパスに Pushy のランタイム依存関係があることを確認する必要もあります。彼らです:
Pushy 自体をビルドして実行するには Java 8 以降が必要です。必須ではありませんが、パフォーマンスを向上させるために、ユーザーは SSL プロバイダーとして netty-native を使用することを選択できます。ネイティブプロバイダーを使用するには、netty-tcnative がクラスパス上にあることを確認してください。 Maven ユーザーは、次のようにしてプロジェクトに依存関係を追加できます。
< dependency >
< groupId >io.netty</ groupId >
< artifactId >netty-tcnative-boringssl-static</ artifactId >
< version >2.0.62.Final</ version >
< scope >runtime</ scope >
</ dependency >
Pushy を使い始める前に、Apple とのプロビジョニング作業を行ってアプリを登録し、必要な証明書または署名キーを取得する必要があります (これらについては後ほど詳しく説明します)。このプロセスの詳細については、Apple の UserNotifications ドキュメントの「APN へのアプリの登録」セクションを参照してください。特に macOS 10.13 (El Capitan) では、いくつかの注意事項があることに注意してください。
一般に、APN クライアントはプッシュ通知を送信する前に、何らかの方法で APN サーバーで認証する必要があります。現在、APN (および Pushy) は、TLS ベースの認証とトークンベースの認証という 2 つの認証方法をサポートしています。 2 つのアプローチは相互に排他的です。クライアントごとにどちらかを選択する必要があります。
TLS ベースの認証では、クライアントは接続時に TLS 証明書をサーバーに提示し、証明書で指定された任意の「トピック」に通知を送信できます。一般に、これは、単一のクライアントが単一の受信側アプリにのみプッシュ通知を送信できることを意味します。
アプリを登録し、必要な証明書を取得したら、Pushy でプッシュ通知の送信を開始するために最初に行う必要があるのは、 ApnsClient
作成することです。 TLS 認証を使用するクライアントには、APN サーバーで認証するための証明書と秘密キーが必要です。証明書とキーを保存する最も一般的な方法は、パスワードで保護された PKCS#12 ファイルに保存することです (この記事の執筆時点で Apple の指示に従えば、パスワードで保護された .p12 ファイルが作成されます)。 TLS ベースの認証を使用するクライアントを作成するには、次の手順を実行します。
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setClientCredentials ( new File ( "/path/to/certificate.p12" ), "p12-file-password" )
. build ();
トークンベースの認証でも、クライアントは TLS で保護された接続を使用してサーバーに接続しますが、接続時にサーバーに証明書を提示しません。代わりに、クライアントは送信する各通知に暗号署名されたトークンを含めます (心配しないでください。これは Pushy が自動的に処理します)。クライアントは、有効な署名キーを持っている任意の「トピック」にプッシュ通知を送信できます。
トークンベースのクライアントの使用を開始するには、Apple から署名キー (状況によっては秘密キーとも呼ばれます) を取得する必要があります。署名キーを取得したら、新しいクライアントを作成できます。
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. build ();
Pushy の APN クライアントは、APN サーバーへの接続の内部プールを維持し、オンデマンドで新しい接続を作成します。その結果、クライアントを明示的に起動する必要がなくなりました。選択した認証方法に関係なく、クライアントを作成したら、プッシュ通知の送信を開始できるようになります。プッシュ通知には少なくとも、デバイス トークン (通知の宛先デバイスを識別し、認証トークンとは異なる概念です)、トピック、およびペイロードが必要です。
final SimpleApnsPushNotification pushNotification ;
{
final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder ();
payloadBuilder . setAlertBody ( "Example!" );
final String payload = payloadBuilder . build ();
final String token = TokenUtil . sanitizeTokenString ( "<efc7492 bdbd8209>" );
pushNotification = new SimpleApnsPushNotification ( token , "com.example.myApp" , payload );
}
Pushy にはSimpleApnsPayloadBuilder
が含まれており、Gson および Jackson に基づくペイロード ビルダーは別個のモジュールとして利用できます。 APN ペイロードは単なる JSON 文字列であり、呼び出し元は選択した方法でペイロードを生成できます。 Pushy のペイロード ビルダーは便利かもしれませんが、呼び出し元はそれらを使用する義務はありません。
プッシュ通知の送信プロセスは非同期です。通知を送信してサーバーから応答を取得するプロセスには時間がかかる場合がありますが、クライアントはすぐにCompletableFuture
返します。そのCompletableFuture
使用して、送信操作の進行状況と最終的な結果を追跡できます。通知を送信すると、送信された通知への参照を常に保持するCompletableFuture
のサブクラスであるPushNotificationFuture
が返されることに注意してください。
final PushNotificationFuture < SimpleApnsPushNotification , PushNotificationResponse < SimpleApnsPushNotification >>
sendNotificationFuture = apnsClient . sendNotification ( pushNotification );
CompletableFuture
は、次の 3 つの状況のいずれかで完了します。
CompletableFuture
例外で失敗します。これは通常、一時的な失敗であると考えられ、呼び出し元は問題が解決したら通知を再度送信してみる必要があります。例:
try {
final PushNotificationResponse < SimpleApnsPushNotification > pushNotificationResponse =
sendNotificationFuture . get ();
if ( pushNotificationResponse . isAccepted ()) {
System . out . println ( "Push notification accepted by APNs gateway." );
} else {
System . out . println ( "Notification rejected by the APNs gateway: " +
pushNotificationResponse . getRejectionReason ());
pushNotificationResponse . getTokenInvalidationTimestamp (). ifPresent ( timestamp -> {
System . out . println ( " t …and the token is invalid as of " + timestamp );
});
}
} catch ( final ExecutionException e ) {
System . err . println ( "Failed to send push notification." );
e . printStackTrace ();
}
CompletableFuture
は、操作の完了時に追加のタスクを実行するようにスケジュールするためのアフォーダンスがあることに注意することが重要です。個々のプッシュ通知を待つのは実際には非効率であり、完了するまでブロックするのではなく、フォローアップ タスクをCompletableFuture
に追加する方が、ほとんどのユーザーにとって良いサービスが得られます。例として:
sendNotificationFuture . whenComplete (( response , cause ) -> {
if ( response != null ) {
// Handle the push notification response as before from here.
} else {
// Something went wrong when trying to send the notification to the
// APNs server. Note that this is distinct from a rejection from
// the server, and indicates that something went wrong when actually
// sending the notification or waiting for a reply.
cause . printStackTrace ();
}
});
すべての APN クライアントは、メッセージを送信したことがないクライアントも含めて、システム リソースを割り当てて保持する可能性があるため、それらを解放することが重要です。 APN クライアントは、永続的で存続期間の長いリソースであることを目的としています。通知 (または通知のバッチ) を送信した後にクライアントをシャットダウンする必要はありませんが、アプリケーションのシャットダウン時にはクライアントをシャットダウンする必要があります。
final CompletableFuture < Void > closeFuture = apnsClient . close ();
シャットダウンするとき、クライアントは、送信されたが確認されていないすべての通知がサーバーから応答を受信するまで待機します。 sendNotification
に渡されたが、まだサーバーに送信されていない通知 (つまり、内部キューで待機している通知) は、切断時にすぐに失敗します。通常、呼び出し元はシャットダウンする前に、送信されたすべての通知がサーバーによって確認されたことを確認する必要があります。
高スループットのアプリケーションのためにシステム リソースを最大限に活用するには、常にある程度の努力が必要です。このプロセスをガイドするために、Pushy の使用に関するいくつかのベスト プラクティスをカバーする Wiki ページをまとめました。これらすべての点については、Wiki でさらに詳しく説明されていますが、一般的に推奨されるのは次のとおりです。
ApnsClient
インスタンスを存続期間の長いリソースとして扱うCompletableFutures
に追加します。Pushy には、クライアントの動作とパフォーマンスに関する洞察を提供するメトリクスを監視するためのインターフェイスが含まれています。 ApnsClientMetricsListener
インターフェイスの独自の実装を作成して、メトリクスを記録およびレポートできます。また、Dropwizard Metrics ライブラリを使用し、Micrometer アプリケーション監視ファサードを別個のモジュールとして使用して、メトリクスを収集およびレポートするメトリクス リスナーも提供します。メトリクスの受信を開始するには、新しいクライアントを構築するときにリスナーを設定します。
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. setMetricsListener ( new MyCustomMetricsListener ())
. build ();
リスナー実装のメトリクス処理メソッドは、ブロッキング コードを呼び出さないようにしてください。ハンドラー メソッドでカウンターを直接インクリメントするのが適切ですが、データベースまたはリモート監視エンドポイントへの呼び出しは別のスレッドにディスパッチする必要があります。
アウトバウンド接続にプロキシを使用する必要がある場合は、 ApnsClient
インスタンスを構築するときにProxyHandlerFactory
指定できます。 ProxyHandlerFactory
の具体的な実装は、HTTP、SOCKS4、および SOCKS5 プロキシに提供されます。
例:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. setProxyHandlerFactory ( new Socks5ProxyHandlerFactory (
new InetSocketAddress ( "my.proxy.com" , 1080 ), "username" , "password" ))
. build ();
JVM システム プロパティ経由で構成された HTTP プロキシを使用している場合は、以下も使用できます。
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. setProxyHandlerFactory ( HttpProxyHandlerFactory . fromSystemProxies (
ApnsClientBuilder . DEVELOPMENT_APNS_HOST ))
. build ();
Pushy はログ記録に SLF4J を使用します。まだ詳しくない方のために説明すると、SLF4J は、クラスパスに特定の「バインディング」を追加することで、ユーザーがデプロイ時に使用するログ ライブラリを選択できるようにするファサードです。ユーザーが選択を行う必要がないように、Pushy 自体は SLF4J バインディングに依存しません。独自に追加する必要があります (独自のプロジェクトに依存関係として追加するか、直接インストールします)。クラスパスに SLF4J バインディングがない場合は、おそらく次のような警告が表示されます。
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
詳細については、SLF4J ユーザーマニュアルを参照してください。
Pushy は次のようにログ レベルを使用します。
レベル | ログに記録されたイベント |
---|---|
error | 重大な回復不可能なエラー。 Pushy のバグを示す可能性のある回復可能なエラー |
warn | 重大だが回復可能なエラー。呼び出し元のコードのバグを示す可能性のあるエラー |
info | 重要なライフサイクル イベント |
debug | マイナーなライフサイクル イベント。予期される例外 |
trace | 個別の IO 操作 |
Pushy には、呼び出し元が統合テストやベンチマークで使用できるモック APN サーバーが含まれています。通常の操作ではモック サーバー (または関連クラス) を使用する必要はありません。
モックサーバーを構築するには、呼び出し元はMockApnsServerBuilder
使用する必要があります。すべてのサーバーには、模擬サーバーが各受信プッシュ通知を受け入れるか拒否するかを決定するPushNotificationHandler
(ビルダーに提供されるPushNotificationHandlerFactory
によって構築される) が必要です。 Pushy には、ベンチマークに役立つAcceptAllPushNotificationHandlerFactory
と、統合テストに役立つValidatingPushNotificationHandlerFactory
が含まれています。
呼び出し元は、モック サーバーを構築するときにMockApnsServerListener
を提供することもできます。リスナーには、モック サーバーがクライアントからの通知を受け入れるか拒否するたびに通知が届きます。
Pushy は MIT ライセンスの下で利用可能です。
Pushy の現在のバージョンは 0.15.4 です。これは完全に機能しており、運用環境で広く使用されていますが、パブリック API は 1.0 リリース前に大幅に変更される可能性があります。