Pushy est une bibliothèque Java permettant d'envoyer des notifications push APN (iOS, macOS et Safari).
Pushy envoie des notifications push à l'aide du protocole APNs basé sur HTTP/2 d'Apple et prend en charge l'authentification TLS et par jeton. Elle se distingue des autres bibliothèques de notifications push en mettant l'accent sur une documentation approfondie, un fonctionnement asynchrone et une conception pour un fonctionnement à l'échelle industrielle. Avec Pushy, il est simple et efficace de maintenir plusieurs connexions parallèles à la passerelle APN pour envoyer un grand nombre de notifications à de nombreuses applications différentes (« sujets »).
Nous pensons que Pushy est déjà le meilleur outil pour envoyer des notifications push APN à partir d'applications Java, et nous espérons que vous nous aiderez à l'améliorer encore via des rapports de bogues et des demandes d'extraction.
Si vous avez besoin d'une application graphique simple pour envoyer des notifications push à des fins de développement ou de test, vous pourriez également être intéressé par le projet sœur de Pushy, Pushy Console.
Si vous utilisez Maven, vous pouvez ajouter Pushy à votre projet en ajoutant la déclaration de dépendance suivante à votre POM :
< dependency >
< groupId >com.eatthepath</ groupId >
< artifactId >pushy</ artifactId >
< version >0.15.4</ version >
</ dependency >
Si vous n'utilisez pas Maven (ou quelque chose d'autre qui comprend les dépendances Maven, comme Gradle), vous pouvez télécharger Pushy sous forme de fichier .jar
et l'ajouter directement à votre projet. Vous devrez également vous assurer que vous disposez des dépendances d'exécution de Pushy sur votre chemin de classe. Ils sont:
Pushy lui-même nécessite Java 8 ou une version plus récente pour être construit et exécuté. Bien que cela ne soit pas obligatoire, les utilisateurs peuvent choisir d'utiliser netty-native comme fournisseur SSL pour des performances améliorées. Pour utiliser un fournisseur natif, assurez-vous que netty-tcnative se trouve sur votre chemin de classe. Les utilisateurs Maven peuvent ajouter une dépendance à leur projet comme suit :
< dependency >
< groupId >io.netty</ groupId >
< artifactId >netty-tcnative-boringssl-static</ artifactId >
< version >2.0.62.Final</ version >
< scope >runtime</ scope >
</ dependency >
Avant de pouvoir commencer avec Pushy, vous devrez effectuer un travail de provisionnement avec Apple pour enregistrer votre application et obtenir les certificats ou clés de signature requis (plus d'informations à ce sujet prochainement). Pour plus de détails sur ce processus, veuillez consulter la section Enregistrement de votre application auprès des APN de la documentation UserNotifications d'Apple. Attention, il existe quelques mises en garde, notamment sous macOS 10.13 (El Capitan).
De manière générale, les clients APNs doivent s'authentifier auprès du serveur APNs d'une manière ou d'une autre avant de pouvoir envoyer des notifications push. Actuellement, les APN (et Pushy) prennent en charge deux méthodes d'authentification : l'authentification basée sur TLS et l'authentification basée sur les jetons. Les deux approches s’excluent mutuellement ; vous devrez choisir l'un ou l'autre pour chaque client.
Dans l'authentification basée sur TLS, les clients présentent un certificat TLS au serveur lors de la connexion et peuvent envoyer des notifications à n'importe quel « sujet » nommé dans le certificat. Généralement, cela signifie qu'un seul client ne peut envoyer des notifications push qu'à une seule application réceptrice.
Une fois que vous avez enregistré votre application et disposez des certificats requis, la première chose que vous devrez faire pour commencer à envoyer des notifications push avec Pushy est de créer un ApnsClient
. Les clients utilisant l'authentification TLS ont besoin d'un certificat et d'une clé privée pour s'authentifier auprès du serveur APNs. Le moyen le plus courant de stocker le certificat et la clé est dans un fichier PKCS#12 protégé par mot de passe (vous vous retrouverez avec un fichier .p12 protégé par mot de passe si vous suivez les instructions d'Apple au moment d'écrire ces lignes). Pour créer un client qui utilisera l'authentification basée sur TLS :
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setClientCredentials ( new File ( "/path/to/certificate.p12" ), "p12-file-password" )
. build ();
Dans l'authentification basée sur un jeton, les clients se connectent toujours au serveur à l'aide d'une connexion sécurisée TLS, mais ne présentent pas de certificat au serveur lors de la connexion. Au lieu de cela, les clients incluent un jeton signé cryptographiquement avec chaque notification qu'ils envoient (ne vous inquiétez pas, Pushy gère cela automatiquement pour vous). Les clients peuvent envoyer des notifications push à n'importe quel « sujet » pour lequel ils disposent d'une clé de signature valide.
Pour démarrer avec un client basé sur des jetons, vous devrez obtenir une clé de signature (également appelée clé privée dans certains contextes) auprès d'Apple. Une fois que vous avez votre clé de signature, vous pouvez créer un nouveau client :
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. build ();
Les clients APNs de Pushy maintiennent un pool interne de connexions au serveur APNs et créent de nouvelles connexions à la demande. Par conséquent, il n’est pas nécessaire de démarrer explicitement les clients. Quelle que soit la méthode d'authentification que vous choisissez, une fois que vous avez créé un client, il est prêt à commencer à envoyer des notifications push. Au minimum, les notifications push nécessitent un jeton d'appareil (qui identifie l'appareil de destination de la notification et constitue une idée distincte d'un jeton d'authentification), un sujet et une charge utile.
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 inclut un SimpleApnsPayloadBuilder
et des générateurs de charge utile basés sur Gson et Jackson sont disponibles sous forme de modules distincts. Les charges utiles des APN ne sont que des chaînes JSON, et les appelants peuvent produire des charges utiles par la méthode de leur choix ; bien que les générateurs de charge utile de Pushy puissent être pratiques, les appelants ne sont pas obligés de les utiliser.
Le processus d'envoi d'une notification push est asynchrone ; bien que le processus d'envoi d'une notification et d'obtention d'une réponse du serveur puisse prendre un certain temps, le client renverra immédiatement un CompletableFuture
. Vous pouvez utiliser ce CompletableFuture
pour suivre la progression et le résultat éventuel de l'opération d'envoi. Notez que l'envoi d'une notification renvoie un PushNotificationFuture
, qui est une sous-classe de CompletableFuture
qui contient toujours une référence à la notification envoyée.
final PushNotificationFuture < SimpleApnsPushNotification , PushNotificationResponse < SimpleApnsPushNotification >>
sendNotificationFuture = apnsClient . sendNotification ( pushNotification );
Le CompletableFuture
se terminera dans l’une des trois circonstances suivantes :
CompletableFuture
échoue avec une exception. Cela doit généralement être considéré comme un échec temporaire et les appelants doivent essayer de renvoyer la notification une fois le problème résolu.Un exemple :
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 ();
}
Il est important de noter que CompletableFuture
permet de planifier des tâches supplémentaires à exécuter lorsqu'une opération est terminée. Attendre chaque notification push individuelle est inefficace dans la pratique, et la plupart des utilisateurs seront mieux servis en ajoutant des tâches de suivi à CompletableFuture
au lieu de bloquer jusqu'à ce qu'il soit terminé. A titre d'exemple :
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 ();
}
});
Tous les clients APN, même ceux qui n'ont jamais envoyé de message, peuvent allouer et conserver des ressources système, et il est important de les libérer. Les clients APN sont destinés à être des ressources persistantes et de longue durée ; vous n'avez certainement pas besoin d'arrêter un client après l'envoi d'une notification (ou même d'un lot de notifications), mais vous souhaiterez arrêter votre (ou vos) client(s) lorsque votre application s'arrête :
final CompletableFuture < Void > closeFuture = apnsClient . close ();
Lors de l'arrêt, les clients attendront toutes les notifications envoyées mais non accusées de réception pour recevoir une réponse du serveur. Les notifications qui ont été transmises à sendNotification
mais pas encore envoyées au serveur (c'est-à-dire les notifications en attente dans une file d'attente interne) échoueront immédiatement lors de la déconnexion. Les appelants doivent généralement s'assurer que toutes les notifications envoyées ont été reconnues par le serveur avant de s'arrêter.
Tirer le meilleur parti des ressources de votre système pour les applications à haut débit demande toujours un certain effort. Pour vous guider tout au long du processus, nous avons créé une page wiki couvrant quelques bonnes pratiques d'utilisation de Pushy. Tous ces points sont traités beaucoup plus en détail sur le wiki, mais en général, nos recommandations sont :
ApnsClient
comme des ressources à long termeCompletableFutures
si vous souhaitez suivre l'état de vos notifications push Pushy comprend une interface pour surveiller les métriques qui fournissent un aperçu du comportement et des performances des clients. Vous pouvez écrire votre propre implémentation de l'interface ApnsClientMetricsListener
pour enregistrer et rapporter des métriques. Nous fournissons également des écouteurs de métriques qui collectent et rapportent des métriques à l'aide de la bibliothèque Dropwizard Metrics et de la façade de surveillance de l'application Micrometer en tant que modules distincts. Pour commencer à recevoir des métriques, définissez un écouteur lors de la création d'un nouveau client :
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 ();
Veuillez noter que les méthodes de gestion des métriques dans votre implémentation d'écouteur ne doivent jamais appeler de code bloquant. Il est approprié d'incrémenter les compteurs directement dans les méthodes du gestionnaire, mais les appels aux bases de données ou aux points de terminaison de surveillance distants doivent être distribués vers des threads distincts.
Si vous devez utiliser un proxy pour les connexions sortantes, vous pouvez spécifier un ProxyHandlerFactory
lors de la création de votre instance ApnsClient
. Des implémentations concrètes de ProxyHandlerFactory
sont fournies pour les proxys HTTP, SOCKS4 et SOCKS5.
Un exemple :
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 ();
Si vous utilisez des proxys HTTP configurés via les propriétés du système JVM, vous pouvez également utiliser :
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 utilise SLF4J pour la journalisation. Si vous ne le connaissez pas déjà, SLF4J est une façade qui permet aux utilisateurs de choisir quelle bibliothèque de journalisation utiliser au moment du déploiement en ajoutant une « liaison » spécifique au chemin de classe. Pour éviter de faire le choix à votre place, Pushy lui-même ne dépend d'aucune liaison SLF4J ; vous devrez en ajouter un vous-même (soit en l'ajoutant en tant que dépendance dans votre propre projet, soit en l'installant directement). Si vous n'avez aucune liaison SLF4J sur votre chemin de classe, vous verrez probablement un avertissement ressemblant à ceci :
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.
Pour plus d'informations, consultez le manuel d'utilisation du SLF4J.
Pushy utilise les niveaux de journalisation comme suit :
Niveau | Événements enregistrés |
---|---|
error | Erreurs graves et irrécupérables ; erreurs récupérables qui indiquent probablement un bug dans Pushy |
warn | Erreurs graves mais récupérables ; erreurs pouvant indiquer un bug dans le code de l'appelant |
info | Événements importants du cycle de vie |
debug | Événements mineurs du cycle de vie ; exceptions attendues |
trace | Opérations d'E/S individuelles |
Pushy comprend un serveur APN fictif que les appelants peuvent utiliser dans les tests d'intégration et les benchmarks. Il n'est pas nécessaire d'utiliser un serveur fictif (ou toute classe associée) en fonctionnement normal.
Pour créer un serveur fictif, les appelants doivent utiliser un MockApnsServerBuilder
. Tous les serveurs nécessitent un PushNotificationHandler
(construit par un PushNotificationHandlerFactory
fourni au constructeur) qui décide si le serveur fictif acceptera ou rejettera chaque notification push entrante. Pushy inclut un AcceptAllPushNotificationHandlerFactory
qui est utile pour l'analyse comparative et un ValidatingPushNotificationHandlerFactory
qui peut être utile pour les tests d'intégration.
Les appelants peuvent également fournir un MockApnsServerListener
lors de la création d'un serveur fictif ; les auditeurs sont avertis chaque fois que le serveur fictif accepte ou rejette une notification d'un client.
Pushy est disponible sous la licence MIT.
La version actuelle de Pushy est la 0.15.4. Elle est entièrement fonctionnelle et largement utilisée dans les environnements de production, mais l'API publique peut changer considérablement avant la version 1.0.