Search code examples
javaiospush-notificationapple-push-notificationsjavaapns

SocketException: Connection closed by remote host when pushing to producation destination


I need to send notifications to IOS devices and we are using Java as backend. I am using notnoop/java-apns library to push notifications to IOS devices.

I am getting a SocketException:Connection closed by remote host when I try to push to APNS using the production certificates and production destination.

service = APNS.newService()
            .withCert(classLoader.getResourceAsStream("files/Production_Certificate.p12"), "password")
            .withDelegate(delegate)
            .withProductionDestination()
            .build();

When I use the development certificate and sandbox destination, everything works perfectly and the device receives the push notifications.

I tried to push notifications using another tool and our production certificate and the push was received, so the certificate is valid. I don't understand what is going wrong. Am i doing something wrong or is it some network related issue or something wrong in the library?

The following is the stacktrace:-

Couldn't send message after 3 retries.Message(Id=1; Token=0557A5BE7EEA5718A1064FC138EEE855BBDDE15FC8C5841CEADBAD2716A42AEC; Payload={"aps":{"alert":{"body":"Tasks have been assigned to you.","title":"New tasks!"},"sound":"default","badge":1}}) java.net.SocketException: Connection closed by remote host at sun.security.ssl.SSLSocketImpl.checkWrite(SSLSocketImpl.java:1490) at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123) at java.io.OutputStream.write(OutputStream.java:75) at com.notnoop.apns.internal.ApnsConnectionImpl.sendMessage(ApnsConnectionImpl.java:328) at com.notnoop.apns.internal.ApnsConnectionImpl.sendMessage(ApnsConnectionImpl.java:312) at com.notnoop.apns.internal.ApnsServiceImpl.push(ApnsServiceImpl.java:46) at com.notnoop.apns.internal.AbstractApnsService.push(AbstractApnsService.java:89) at com.notnoop.apns.internal.ApnsServiceImpl.push(ApnsServiceImpl.java:36) at com.mobileinsight.api.service.impl.IOSBroadcastServiceImpl.broadcastNewTaskNotifications(IOSBroadcastServiceImpl.java:125) at com.mobileinsight.api.service.impl.IOSBroadcastServiceImpl.broadcastIOSPushNotifications(IOSBroadcastServiceImpl.java:60) at com.mobileinsight.api.service.impl.PushNotificationServiceImpl.pushNotificationsInBulk(PushNotificationServiceImpl.java:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:319) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at com.sun.proxy.$Proxy136.pushNotificationsInBulk(Unknown Source) at com.mobileinsight.api.quartz.PushNotificationBatchRunner.doWork(PushNotificationBatchRunner.java:24) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)


Solution

  • Finally, figured out the problem. The p12 file accepted by this library should contain only one certificate, in this case, the production certificate.

    The keystore file we were using all this time contained multiple certificates. The java libraries get confused as to which to be chosen. Java's SSL system automatically uses the first certificate in the file, so obviously one server or the other will never work, depending on the order of certificates in the file.

    To resolve this, we produced a different keystore file for each. So we exported(to .p12 format) just the production certificate from the keychain.

    Even pushy library works if the p12 file contains just a single certificate.