Pushy ist eine Java-Bibliothek zum Senden von Push-Benachrichtigungen an APNs (iOS, macOS und Safari).
Pushy sendet Push-Benachrichtigungen über das HTTP/2-basierte APNs-Protokoll von Apple und unterstützt sowohl TLS als auch tokenbasierte Authentifizierung. Sie unterscheidet sich von anderen Push-Benachrichtigungsbibliotheken durch den Schwerpunkt auf gründliche Dokumentation, asynchronen Betrieb und Design für den Betrieb im industriellen Maßstab. Mit Pushy ist es einfach und effizient, mehrere parallele Verbindungen zum APNs-Gateway aufrechtzuerhalten, um eine große Anzahl von Benachrichtigungen an viele verschiedene Anwendungen („Themen“) zu senden.
Wir glauben, dass Pushy bereits das beste Tool zum Senden von APNs-Push-Benachrichtigungen aus Java-Anwendungen ist, und wir hoffen, dass Sie uns durch Fehlerberichte und Pull-Requests dabei helfen, es noch besser zu machen.
Wenn Sie eine einfache GUI-Anwendung zum Versenden von Push-Benachrichtigungen zu Entwicklungs- oder Testzwecken benötigen, könnte Sie auch Pushys Schwesterprojekt Pushy Console interessieren.
Wenn Sie Maven verwenden, können Sie Pushy zu Ihrem Projekt hinzufügen, indem Sie die folgende Abhängigkeitsdeklaration zu Ihrem POM hinzufügen:
< dependency >
< groupId >com.eatthepath</ groupId >
< artifactId >pushy</ artifactId >
< version >0.15.4</ version >
</ dependency >
Wenn Sie Maven (oder etwas anderes, das Maven-Abhängigkeiten versteht, wie Gradle) nicht verwenden, können Sie Pushy als .jar
Datei herunterladen und direkt zu Ihrem Projekt hinzufügen. Sie müssen außerdem sicherstellen, dass Pushys Laufzeitabhängigkeiten in Ihrem Klassenpfad vorhanden sind. Sie sind:
Pushy selbst erfordert zum Erstellen und Ausführen Java 8 oder neuer. Obwohl dies nicht erforderlich ist, können Benutzer für eine verbesserte Leistung netty-native als SSL-Anbieter verwenden. Um einen nativen Anbieter zu verwenden, stellen Sie sicher, dass sich netty-tcnative in Ihrem Klassenpfad befindet. Maven-Benutzer können ihrem Projekt wie folgt eine Abhängigkeit hinzufügen:
< dependency >
< groupId >io.netty</ groupId >
< artifactId >netty-tcnative-boringssl-static</ artifactId >
< version >2.0.62.Final</ version >
< scope >runtime</ scope >
</ dependency >
Bevor Sie mit Pushy beginnen können, müssen Sie einige Bereitstellungsarbeiten mit Apple durchführen, um Ihre App zu registrieren und die erforderlichen Zertifikate oder Signaturschlüssel zu erhalten (mehr dazu in Kürze). Einzelheiten zu diesem Vorgang finden Sie im Abschnitt „Registrieren Ihrer App bei APNs“ in der UserNotifications-Dokumentation von Apple. Bitte beachten Sie, dass es einige Einschränkungen gibt, insbesondere unter macOS 10.13 (El Capitan).
Im Allgemeinen müssen sich APNs-Clients auf irgendeine Weise beim APNs-Server authentifizieren, bevor sie Push-Benachrichtigungen senden können. Derzeit unterstützen APNs (und Pushy) zwei Authentifizierungsmethoden: TLS-basierte Authentifizierung und tokenbasierte Authentifizierung. Die beiden Ansätze schließen sich gegenseitig aus; Sie müssen für jeden Kunden das eine oder das andere auswählen.
Bei der TLS-basierten Authentifizierung legen Clients dem Server beim Herstellen einer Verbindung ein TLS-Zertifikat vor und können Benachrichtigungen an jedes im Zertifikat genannte „Thema“ senden. Im Allgemeinen bedeutet dies, dass ein einzelner Client Push-Benachrichtigungen nur an eine einzige empfangende App senden kann.
Sobald Sie Ihre App registriert haben und über die erforderlichen Zertifikate verfügen, müssen Sie zunächst einen ApnsClient
erstellen, um Push-Benachrichtigungen mit Pushy senden zu können. Clients, die die TLS-Authentifizierung verwenden, benötigen ein Zertifikat und einen privaten Schlüssel, um sich beim APNs-Server zu authentifizieren. Am häufigsten werden das Zertifikat und der Schlüssel in einer passwortgeschützten PKCS#12-Datei gespeichert (Sie erhalten am Ende eine passwortgeschützte .p12-Datei, wenn Sie den Anweisungen von Apple zum Zeitpunkt des Verfassens dieses Artikels folgen). So erstellen Sie einen Client, der die TLS-basierte Authentifizierung verwendet:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setClientCredentials ( new File ( "/path/to/certificate.p12" ), "p12-file-password" )
. build ();
Bei der tokenbasierten Authentifizierung stellen Clients weiterhin über eine TLS-gesicherte Verbindung eine Verbindung zum Server her, legen dem Server beim Herstellen der Verbindung jedoch kein Zertifikat vor. Stattdessen fügen Clients jeder von ihnen gesendeten Benachrichtigung ein kryptografisch signiertes Token bei (keine Sorge – Pushy erledigt dies automatisch für Sie). Kunden können Push-Benachrichtigungen an jedes „Thema“ senden, für das sie über einen gültigen Signaturschlüssel verfügen.
Um mit einem tokenbasierten Client zu beginnen, müssen Sie von Apple einen Signaturschlüssel (in manchen Kontexten auch privater Schlüssel genannt) erhalten. Sobald Sie Ihren Signaturschlüssel haben, können Sie einen neuen Kunden erstellen:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. build ();
Die APNs-Clients von Pushy unterhalten einen internen Pool von Verbindungen zum APNs-Server und erstellen bei Bedarf neue Verbindungen. Dadurch müssen Clients nicht explizit gestartet werden. Unabhängig davon, welche Authentifizierungsmethode Sie wählen: Sobald Sie einen Client erstellt haben, kann dieser mit dem Senden von Push-Benachrichtigungen beginnen. Push-Benachrichtigungen benötigen mindestens ein Geräte-Token (das das Zielgerät der Benachrichtigung identifiziert und sich von einem Authentifizierungstoken unterscheidet), ein Thema und eine Nutzlast.
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 enthält einen SimpleApnsPayloadBuilder
und Payload-Builder auf Basis von Gson und Jackson sind als separate Module verfügbar. APNs-Nutzlasten sind nur JSON-Strings, und Aufrufer können Nutzlasten mit der Methode ihrer Wahl erzeugen; Obwohl Pushys Payload-Builder praktisch sein mögen, sind Anrufer nicht verpflichtet, sie zu verwenden.
Der Vorgang des Sendens einer Push-Benachrichtigung ist asynchron; Obwohl das Senden einer Benachrichtigung und das Erhalten einer Antwort vom Server einige Zeit dauern kann, gibt der Client sofort ein CompletableFuture
zurück. Sie können CompletableFuture
verwenden, um den Fortschritt und das eventuelle Ergebnis des Sendevorgangs zu verfolgen. Beachten Sie, dass das Senden einer Benachrichtigung PushNotificationFuture
zurückgibt, eine Unterklasse von CompletableFuture
, die immer einen Verweis auf die gesendete Benachrichtigung enthält.
final PushNotificationFuture < SimpleApnsPushNotification , PushNotificationResponse < SimpleApnsPushNotification >>
sendNotificationFuture = apnsClient . sendNotification ( pushNotification );
Die CompletableFuture
wird in einem von drei Fällen abgeschlossen:
CompletableFuture
schlägt mit einer Ausnahme fehl. Dies sollte im Allgemeinen als vorübergehender Fehler betrachtet werden und Anrufer sollten versuchen, die Benachrichtigung erneut zu senden, wenn das Problem behoben wurde.Ein Beispiel:
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 ();
}
Es ist wichtig zu beachten, dass CompletableFuture
die Möglichkeit bietet, zusätzliche Aufgaben zu planen, die ausgeführt werden, wenn ein Vorgang abgeschlossen ist. Das Warten auf jede einzelne Push-Benachrichtigung ist in der Praxis ineffizient, und die meisten Benutzer sind besser bedient, wenn sie Folgeaufgaben zur CompletableFuture
hinzufügen, anstatt zu blockieren, bis sie abgeschlossen ist. Als Beispiel:
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 ();
}
});
Alle APNs-Clients – auch diejenigen, die noch nie eine Nachricht gesendet haben – können Systemressourcen zuweisen und behalten, und es ist wichtig, sie freizugeben. APNs-Clients sollen dauerhafte, langlebige Ressourcen sein; Sie müssen einen Client definitiv nicht herunterfahren, nachdem Sie eine Benachrichtigung (oder sogar einen Stapel von Benachrichtigungen) gesendet haben, aber Sie möchten Ihren Client (oder Ihre Clients) herunterfahren, wenn Ihre Anwendung heruntergefahren wird:
final CompletableFuture < Void > closeFuture = apnsClient . close ();
Beim Herunterfahren warten die Clients auf alle gesendeten, aber nicht bestätigten Benachrichtigungen, um eine Antwort vom Server zu erhalten. Benachrichtigungen, die an sendNotification
übergeben, aber noch nicht an den Server gesendet wurden (z. B. Benachrichtigungen, die in einer internen Warteschlange warten), schlagen sofort fehl, wenn die Verbindung getrennt wird. Grundsätzlich sollten Anrufer vor dem Herunterfahren sicherstellen, dass alle gesendeten Benachrichtigungen vom Server bestätigt wurden.
Die optimale Nutzung Ihrer Systemressourcen für Anwendungen mit hohem Durchsatz erfordert immer einen gewissen Aufwand. Um Sie durch den Prozess zu führen, haben wir eine Wiki-Seite mit einigen Best Practices für die Verwendung von Pushy zusammengestellt. Alle diese Punkte werden im Wiki ausführlicher behandelt, aber im Allgemeinen lauten unsere Empfehlungen:
ApnsClient
-Instanzen als langlebige RessourcenCompletableFutures
hinzu, wenn Sie den Status Ihrer Push-Benachrichtigungen verfolgen möchten Pushy umfasst eine Schnittstelle zur Überwachung von Metriken, die Einblick in das Verhalten und die Leistung der Kunden geben. Sie können Ihre eigene Implementierung der ApnsClientMetricsListener
Schnittstelle schreiben, um Metriken aufzuzeichnen und zu melden. Wir stellen auch Metrik-Listener bereit, die Metriken mithilfe der Dropwizard Metrics-Bibliothek und der Micrometer-Anwendungsüberwachungsfassade als separate Module sammeln und melden. Um mit dem Empfang von Metriken zu beginnen, legen Sie beim Erstellen eines neuen Clients einen Listener fest:
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 ();
Bitte beachten Sie, dass die Metrikverarbeitungsmethoden in Ihrer Listener-Implementierung niemals blockierenden Code aufrufen sollten. Es ist sinnvoll, Zähler direkt in den Handler-Methoden zu erhöhen, Aufrufe an Datenbanken oder Remote-Überwachungsendpunkte sollten jedoch an separate Threads weitergeleitet werden.
Wenn Sie einen Proxy für ausgehende Verbindungen verwenden müssen, können Sie beim Erstellen Ihrer ApnsClient
-Instanz eine ProxyHandlerFactory
angeben. Konkrete Implementierungen von ProxyHandlerFactory
werden für HTTP-, SOCKS4- und SOCKS5-Proxys bereitgestellt.
Ein Beispiel:
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 ();
Wenn Sie über JVM-Systemeigenschaften konfigurierte HTTP-Proxys verwenden, können Sie auch Folgendes verwenden:
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 verwendet SLF4J für die Protokollierung. Falls Sie noch nicht damit vertraut sind: SLF4J ist eine Fassade, die es Benutzern ermöglicht, auszuwählen, welche Protokollierungsbibliothek zum Zeitpunkt der Bereitstellung verwendet werden soll, indem sie dem Klassenpfad eine bestimmte „Bindung“ hinzufügt. Damit Ihnen die Auswahl nicht abgenommen wird, ist Pushy selbst nicht auf SLF4J-Bindungen angewiesen. Sie müssen eines selbst hinzufügen (entweder indem Sie es als Abhängigkeit in Ihrem eigenen Projekt hinzufügen oder indem Sie es direkt installieren). Wenn Ihr Klassenpfad keine SLF4J-Bindungen enthält, wird wahrscheinlich eine Warnung angezeigt, die etwa so aussieht:
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.
Weitere Informationen finden Sie im SLF4J-Benutzerhandbuch.
Pushy verwendet Protokollierungsstufen wie folgt:
Ebene | Protokollierte Ereignisse |
---|---|
error | Schwerwiegende, nicht behebbare Fehler; Behebbare Fehler, die wahrscheinlich auf einen Fehler in Pushy hinweisen |
warn | Schwerwiegende, aber behebbare Fehler; Fehler, die auf einen Fehler im Code des Aufrufers hinweisen können |
info | Wichtige Lebenszyklusereignisse |
debug | Kleinere Lebenszyklusereignisse; erwartete Ausnahmen |
trace | Einzelne IO-Operationen |
Pushy enthält einen simulierten APNs-Server, den Anrufer für Integrationstests und Benchmarks verwenden können. Im Normalbetrieb ist es nicht erforderlich, einen Scheinserver (oder verwandte Klassen) zu verwenden.
Um einen Scheinserver zu erstellen, sollten Aufrufer einen MockApnsServerBuilder
verwenden. Alle Server benötigen einen PushNotificationHandler
(erstellt durch eine dem Builder bereitgestellte PushNotificationHandlerFactory
), der entscheidet, ob der Scheinserver jede eingehende Push-Benachrichtigung akzeptiert oder ablehnt. Pushy enthält eine AcceptAllPushNotificationHandlerFactory
, die für Benchmarking hilfreich ist, und eine ValidatingPushNotificationHandlerFactory
, die für Integrationstests hilfreich sein kann.
Aufrufer können beim Erstellen eines Scheinservers auch einen MockApnsServerListener
bereitstellen. Listener werden benachrichtigt, wenn der Mock-Server eine Benachrichtigung von einem Client akzeptiert oder ablehnt.
Pushy ist unter der MIT-Lizenz erhältlich.
Die aktuelle Version von Pushy ist 0.15.4. Es ist voll funktionsfähig und wird häufig in Produktionsumgebungen verwendet, die öffentliche API kann sich jedoch vor der Veröffentlichung von 1.0 erheblich ändern.