Pushy é uma biblioteca Java para enviar notificações push de APNs (iOS, macOS e Safari).
Pushy envia notificações push usando o protocolo APNs baseado em HTTP/2 da Apple e oferece suporte a TLS e autenticação baseada em token. Ela se diferencia de outras bibliotecas de notificação push com foco em documentação completa, operação assíncrona e design para operação em escala industrial. Com o Pushy, é fácil e eficiente manter múltiplas conexões paralelas com o gateway APNs para enviar um grande número de notificações para muitos aplicativos diferentes ("tópicos").
Acreditamos que o Pushy já é a melhor ferramenta para enviar notificações push de APNs de aplicativos Java e esperamos que você nos ajude a torná-lo ainda melhor por meio de relatórios de bugs e solicitações pull.
Se você precisa de um aplicativo GUI simples para enviar notificações push para fins de desenvolvimento ou teste, você também pode estar interessado no projeto irmão do Pushy, o Pushy Console.
Se você usa Maven, você pode adicionar Pushy ao seu projeto adicionando a seguinte declaração de dependência ao seu POM:
< dependency >
< groupId >com.eatthepath</ groupId >
< artifactId >pushy</ artifactId >
< version >0.15.4</ version >
</ dependency >
Se você não usa o Maven (ou qualquer outra coisa que entenda as dependências do Maven, como Gradle), você pode baixar o Pushy como um arquivo .jar
e adicioná-lo diretamente ao seu projeto. Você também precisará certificar-se de ter as dependências de tempo de execução do Pushy em seu caminho de classe. Eles são:
O próprio Pushy requer Java 8 ou mais recente para ser construído e executado. Embora não seja obrigatório, os usuários podem optar por usar netty-native como provedor SSL para melhorar o desempenho. Para usar um provedor nativo, certifique-se de que netty-tcnative esteja em seu classpath. Os usuários do Maven podem adicionar uma dependência ao seu projeto da seguinte maneira:
< dependency >
< groupId >io.netty</ groupId >
< artifactId >netty-tcnative-boringssl-static</ artifactId >
< version >2.0.62.Final</ version >
< scope >runtime</ scope >
</ dependency >
Antes de começar a usar o Pushy, você precisará fazer algum trabalho de provisionamento com a Apple para registrar seu aplicativo e obter os certificados ou chaves de assinatura necessários (mais sobre isso em breve). Para obter detalhes sobre esse processo, consulte a seção Registrando seu aplicativo com APNs da documentação UserNotifications da Apple. Observe que existem algumas ressalvas, principalmente no macOS 10.13 (El Capitan).
De modo geral, os clientes APNs devem se autenticar no servidor APNs de alguma forma antes de poderem enviar notificações push. Atualmente, APNs (e Pushy) suportam dois métodos de autenticação: autenticação baseada em TLS e autenticação baseada em token. As duas abordagens são mutuamente exclusivas; você precisará escolher um ou outro para cada cliente.
Na autenticação baseada em TLS, os clientes apresentam um certificado TLS ao servidor ao se conectarem e podem enviar notificações para qualquer “tópico” nomeado no certificado. Geralmente, isso significa que um único cliente só pode enviar notificações push para um único aplicativo receptor.
Depois de registrar seu aplicativo e obter os certificados necessários, a primeira coisa que você precisa fazer para começar a enviar notificações push com Pushy é criar um ApnsClient
. Os clientes que usam autenticação TLS precisam de um certificado e uma chave privada para se autenticarem no servidor APNs. A maneira mais comum de armazenar o certificado e a chave é em um arquivo PKCS#12 protegido por senha (você acabará com um arquivo .p12 protegido por senha se seguir as instruções da Apple no momento da redação deste artigo). Para criar um cliente que usará autenticação baseada em TLS:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setClientCredentials ( new File ( "/path/to/certificate.p12" ), "p12-file-password" )
. build ();
Na autenticação baseada em token, os clientes ainda se conectam ao servidor usando uma conexão segura por TLS, mas não apresentam um certificado ao servidor durante a conexão. Em vez disso, os clientes incluem um token assinado criptograficamente em cada notificação que enviam (não se preocupe: o Pushy cuida disso para você automaticamente). Os clientes podem enviar notificações push para qualquer “tópico” para o qual possuam uma chave de assinatura válida.
Para começar a usar um cliente baseado em token, você precisará obter uma chave de assinatura (também chamada de chave privada em alguns contextos) da Apple. Depois de obter sua chave de assinatura, você poderá criar um novo cliente:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. build ();
Os clientes APNs da Pushy mantêm um pool interno de conexões com o servidor APNs e criam novas conexões sob demanda. Como resultado, os clientes não precisam ser iniciados explicitamente. Independentemente do método de autenticação escolhido, depois de criar um cliente, ele estará pronto para começar a enviar notificações push. No mínimo, as notificações push precisam de um token de dispositivo (que identifica o dispositivo de destino da notificação e é uma ideia distinta de um token de autenticação), um tópico e uma carga útil.
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 inclui SimpleApnsPayloadBuilder
e construtores de carga útil baseados em Gson e Jackson estão disponíveis como módulos separados. As cargas úteis de APNs são apenas strings JSON, e os chamadores podem produzir cargas úteis pelo método de sua escolha; embora os construtores de carga útil do Pushy possam ser convenientes, os chamadores não são obrigados a usá-los.
O processo de envio de uma notificação push é assíncrono; embora o processo de envio de uma notificação e obtenção de uma resposta do servidor possa levar algum tempo, o cliente retornará um CompletableFuture
imediatamente. Você pode usar esse CompletableFuture
para acompanhar o progresso e o resultado final da operação de envio. Observe que o envio de uma notificação retorna PushNotificationFuture
, que é uma subclasse de CompletableFuture
que sempre contém uma referência à notificação que foi enviada.
final PushNotificationFuture < SimpleApnsPushNotification , PushNotificationResponse < SimpleApnsPushNotification >>
sendNotificationFuture = apnsClient . sendNotification ( pushNotification );
O CompletableFuture
será concluído em uma das três circunstâncias:
CompletableFuture
falha com uma exceção. Geralmente, isso deve ser considerado uma falha temporária e os chamadores devem tentar enviar a notificação novamente quando o problema for resolvido.Um exemplo:
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 ();
}
É importante observar que CompletableFuture
possui recursos para agendar tarefas adicionais para execução quando uma operação for concluída. Esperar por cada notificação push individual é ineficiente na prática, e a maioria dos usuários será melhor atendida adicionando tarefas de acompanhamento ao CompletableFuture
em vez de bloqueá-lo até que ele seja concluído. Por exemplo:
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 ();
}
});
Todos os clientes APNs – mesmo aqueles que nunca enviaram uma mensagem – podem alocar e reter recursos do sistema, e é importante liberá-los. Os clientes APNs pretendem ser recursos persistentes e de longa duração; você definitivamente não precisa desligar um cliente após enviar uma notificação (ou mesmo um lote de notificações), mas desejará desligar seu cliente (ou clientes) quando seu aplicativo estiver sendo encerrado:
final CompletableFuture < Void > closeFuture = apnsClient . close ();
Ao desligar, os clientes aguardarão todas as notificações enviadas, mas não confirmadas, para receber uma resposta do servidor. As notificações que foram passadas para sendNotification
mas ainda não enviadas ao servidor (ou seja, notificações aguardando em uma fila interna) falharão imediatamente ao serem desconectadas. Os chamadores geralmente devem certificar-se de que todas as notificações enviadas foram reconhecidas pelo servidor antes de desligar.
Aproveitar ao máximo os recursos do sistema para aplicativos de alto rendimento sempre exige algum esforço. Para guiá-lo durante o processo, criamos uma página wiki que cobre algumas práticas recomendadas para usar o Pushy. Todos esses pontos são abordados com muito mais detalhes na wiki, mas em geral, nossas recomendações são:
ApnsClient
como recursos de longa duraçãoCompletableFutures
se quiser rastrear o status de suas notificações push Pushy inclui uma interface para monitorar métricas que fornecem informações sobre o comportamento e desempenho dos clientes. Você pode escrever sua própria implementação da interface ApnsClientMetricsListener
para registrar e relatar métricas. Também fornecemos ouvintes de métricas que coletam e relatam métricas usando a biblioteca Dropwizard Metrics e usando a fachada de monitoramento do aplicativo Micrometer como módulos separados. Para começar a receber métricas, defina um ouvinte ao criar um novo cliente:
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 ();
Observe que os métodos de tratamento de métricas na implementação do seu ouvinte nunca devem chamar código de bloqueio. É apropriado incrementar contadores diretamente nos métodos do manipulador, mas as chamadas para bancos de dados ou pontos de extremidade de monitoramento remoto devem ser despachadas para threads separados.
Se precisar usar um proxy para conexões de saída, você pode especificar um ProxyHandlerFactory
ao construir sua instância ApnsClient
. Implementações concretas de ProxyHandlerFactory
são fornecidas para proxies HTTP, SOCKS4 e SOCKS5.
Um exemplo:
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 ();
Se estiver usando proxies HTTP configurados por meio de propriedades do sistema JVM, você também poderá usar:
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 usa SLF4J para registro. Se você ainda não estiver familiarizado com ele, SLF4J é uma fachada que permite aos usuários escolher qual biblioteca de log usar no momento da implantação, adicionando uma "ligação" específica ao caminho de classe. Para evitar fazer a escolha por você, o próprio Pushy não depende de nenhuma ligação SLF4J; você precisará adicionar um por conta própria (adicionando-o como uma dependência em seu próprio projeto ou instalando-o diretamente). Se você não tiver ligações SLF4J em seu caminho de classe, provavelmente verá um aviso parecido com este:
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.
Para obter mais informações, consulte o manual do usuário SLF4J.
Pushy usa níveis de registro da seguinte forma:
Nível | Eventos registrados |
---|---|
error | Erros graves e irrecuperáveis; erros recuperáveis que provavelmente indicam um bug no Pushy |
warn | Erros graves, mas recuperáveis; erros que podem indicar um bug no código do chamador |
info | Eventos importantes do ciclo de vida |
debug | Eventos menores do ciclo de vida; exceções esperadas |
trace | Operações IO individuais |
Pushy inclui um servidor APNs simulado que os chamadores podem usar em testes de integração e benchmarks. Não é necessário usar um servidor simulado (ou qualquer classe relacionada) em operação normal.
Para construir um servidor simulado, os chamadores devem usar MockApnsServerBuilder
. Todos os servidores requerem um PushNotificationHandler
(construído por um PushNotificationHandlerFactory
fornecido ao construtor) que decide se o servidor simulado aceitará ou rejeitará cada notificação push recebida. Pushy inclui um AcceptAllPushNotificationHandlerFactory
que é útil para benchmarking e um ValidatingPushNotificationHandlerFactory
que pode ser útil para testes de integração.
Os chamadores também podem fornecer um MockApnsServerListener
ao construir um servidor simulado; os ouvintes são notificados sempre que o servidor simulado aceita ou rejeita uma notificação de um cliente.
Pushy está disponível sob a licença MIT.
A versão atual do Pushy é 0.15.4. É totalmente funcional e amplamente utilizado em ambientes de produção, mas a API pública pode mudar significativamente antes do lançamento 1.0.