java.nio.channels.ClosedChannelException带有jetty的HTTP / 2

时间:2016-09-09 00:57:00

标签: java push-notification apple-push-notifications jetty-9 javapns

我正在尝试使用HTTP / 2在生产时向我的设备发送苹果通知。

我将 -Xbootclasspath / p:/home/mohamed/Desktop/alpn-boot-8.1.9.v20160720.jar 作为默认虚拟机参数传递给我蚀。

以下是我现在使用的代码:

public static void pushoNotification() {
    try {
        // create a low-level Jetty HTTP/2 client
        HTTP2Client lowLevelClient = new HTTP2Client();
        lowLevelClient.start();

        // APNs requires the use of HPACK (header compression for HTTP/2), which prevents repeated header keys and values.
        KeyStore ks = KeyStore.getInstance("PKCS12");

        // Ensure that the password is the same as the one used later in setKeyStorePassword()
        ks.load(PushNotifications.class.getClassLoader().getResourceAsStream("Prod2.p12"), "a12B34".toCharArray());

        SslContextFactory ssl = new SslContextFactory(true);
        ssl.setKeyStore(ks);
        ssl.setKeyStorePassword("a12B34");

        // create a high-level Jetty client
        HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(lowLevelClient), ssl);
        client.start();

        // request-response exchange
        ContentResponse response = client.POST("https://api.push.apple.com").path("/3/device/19297dba97212ac6fd16b9cd50f2d86629aed0e49576b2b52ed05086087da802")
                .content(new StringContentProvider("{ \"aps\" : { \"alert\" : \"Hello\" } }")).send();
        response.toString();
        client.stop();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

我的依赖

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.21</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.21</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty.http2</groupId>
        <artifactId>http2-http-client-transport</artifactId>
        <version>9.4.0.M1</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-http</artifactId>
        <version>9.4.0.M1</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty.http2</groupId>
        <artifactId>http2-client</artifactId>
        <version>9.4.0.M1</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-client</artifactId>
        <version>9.4.0.M1</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-alpn-client</artifactId>
        <version>9.4.0.M1</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.mortbay.jetty.alpn</groupId>
        <artifactId>alpn-boot</artifactId>
        <version>8.1.9.v20160720</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.8.2</version>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.8.2</version>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>3.4.1</version>
    </dependency>

但是当我从 main 方法运行此方法时,它会抛出 java.nio.channels.ClosedChannelException

此外,我在eclipse控制台中得到了这个 INFO org.eclipse.jetty.http2.HTTP2Session - 通知监听器时失败org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2$SessionListenerPromise@87eaf9c

有什么想法吗?

由于

1 个答案:

答案 0 :(得分:0)

我现在是在大约3年零6个月后粘贴我的答案,目的只是为了帮助任何一个人遇到相同的错误,实际上我不记得我如何解决此问题,但是下面是我现在正在使用的有效代码...

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.ssl.SslContextFactory;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myproject.path.model.APNSObject;

public void sendIOSNotification(APNSObject apnsObject, List<String> deviceIds, String notificationType) {
    HTTP2Client lowLevelClient = null;
    HttpClient client = null;
    String hostname = null;
    InputStream inputStream = null;
    try {

        lowLevelClient = new HTTP2Client(); // create a low-level Jetty HTTP/2 client
        lowLevelClient.start();

        // APNs requires the use of HPACK (header compression for HTTP/2), which prevents repeated header keys and values.
        KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);

        inputStream = getClass().getClassLoader().getResourceAsStream(APPLE_CERTIFICATE_PATH);
        // Ensure that the password is the same as the one used later in setKeyStorePassword()
        ks.load(inputStream, APPLE_CERTIFICATE_PASSWORD.toCharArray());

        inputStream.close();

        SslContextFactory ssl = new SslContextFactory(true);
        ssl.setKeyStore(ks);
        ssl.setKeyStorePassword(APPLE_CERTIFICATE_PASSWORD);
        FuturePromise<Session> sessionPromise = new FuturePromise<>();

        if (notificationType != null && notificationType.trim().equalsIgnoreCase("Development"))
            hostname = APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME;
        else
            hostname = APPLE_NOTIFICATION_PRODUCTION_HOSTNAME;
        lowLevelClient.connect(ssl, new InetSocketAddress(hostname, Integer.parseInt(APPLE_NOTIFICATION_PORT)), new ServerSessionListener.Adapter(), sessionPromise);

        // create a high-level Jetty client
        client = new HttpClient(new HttpClientTransportOverHTTP2(lowLevelClient), ssl);
        client.start();

        // logger.info("Start Sending Notification.");
        for (String token : deviceIds) {
            ObjectMapper mapper = new ObjectMapper();
            String fcmJsonObject = mapper.setSerializationInclusion(Include.NON_NULL).setSerializationInclusion(Include.NON_EMPTY).writeValueAsString(apnsObject);
            Request request = client.POST("https://" + hostname).timeout(20, TimeUnit.SECONDS).path("/3/device/" + token)
                        .content(new StringContentProvider(fcmJsonObject, Charset.forName("UTF-8")));
            request = request.header("apns-topic", APPLE_APNS_TOPIC);
            ContentResponse response = request.send();

            // For development mode if it's not passed in parameters
            if (response.getStatus() == 400 && !hostname.equals(APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME)) {
                request = client.POST("https://" + APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME).timeout(20, TimeUnit.SECONDS).path("/3/device/" + token)
                            .content(new StringContentProvider(fcmJsonObject, Charset.forName("UTF-8")));
                request = request.header("apns-topic", APPLE_APNS_TOPIC);
                response = request.send();
                logger.info("Sending notification to '" + token + "' in debugging mode...");
            }

            if (response.getStatus() == 200)
                logger.info("Date: " + new Date() + " Notification sent successfully to '" + token + "' With Response Status: " + response.getStatus());
            else
                logger.warning("Sending Notification to token '" + token + "' Failed :( >> Response Status: " + response.getStatus() + "\nReason: " +  response.getReason());
        }
        logger.info("End Sending Notification.");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (lowLevelClient != null && !lowLevelClient.isStopped())
                lowLevelClient.stop();
            if (client != null && !client.isStopped())
                client.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (inputStream != null)
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

APNSObject.java类

import java.io.Serializable;

public class APNSObject implements Serializable {
    // {\"aps\":{\"alert\":{" + messageTitle + "\"body\":\"" + alert + "\"}, \"sound\":\"default\"}" + payload + "}
    private static final long serialVersionUID = 1L;

    private Aps aps;

    private TripBruEntity payload;// Our custom JSON Object

    public APNSObject() {
    }

    public APNSObject(String title, String body, TripBruEntity payload) {
        this(title, body, null, payload);
    }

    public APNSObject(String title, String body, String sound, TripBruEntity payload) {
        aps = new Aps(title, body, sound);
        this.payload = payload;
    }

    public Aps getAps() {
        return aps;
    }

    public void setAps(Aps aps) {
        this.aps = aps;
    }

    public TripBruEntity getPayload() {
        return payload;
    }

    public void setPayload(TripBruEntity payload) {
        this.payload = payload;
    }

    static class Aps {
        private Alert alert;
        private String sound;

        public Aps() {
        }

        public Aps(String title, String body, String sound) {
            this.alert = new Alert(title, body);
            this.sound = sound == null || sound.isEmpty() ? "default" : sound;
        }

        public Alert getAlert() {
            return alert;
        }

        public void setAlert(Alert alert) {
            this.alert = alert;
        }

        public String getSound() {
            return sound;
        }

        public void setSound(String sound) {
            this.sound = sound;
        }

        static class Alert {
            private String title;
            private String body;

            public Alert() {
            }

            public Alert(String title, String body) {
                this.title = title;
                this.body = body;
            }

            public String getTitle() {
                return title;
            }

            public void setTitle(String title) {
                this.title = title;
            }

            public String getBody() {
                return body;
            }

            public void setBody(String body) {
                this.body = body;
            }
        }
    }
}

这是我的配置文件:

APPLE_CERTIFICATE_PATH=/my/apply/certificate/path/so/plz/change/it/Certificates.p12
APPLE_CERTIFICATE_PASSWORD=myPassword(plz change it)
KEYSTORE_TYPE=PKCS12
APPLE_NOTIFICATION_DEVELOPMENT_HOSTNAME=api.development.push.apple.com
APPLE_NOTIFICATION_PRODUCTION_HOSTNAME=api.push.apple.com
APPLE_NOTIFICATION_PORT=2197
APPLE_APNS_TOPIC=MyAPNS_Topic(plz change it)

最后,这是我在项目中使用的jars

enter image description here