com.jim.im.message.service.ApnClientUnit.java Source code

Java tutorial

Introduction

Here is the source code for com.jim.im.message.service.ApnClientUnit.java

Source

/*
 * Copyright 2014 Jim. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jim.im.message.service;

import com.relayrides.pushy.apns.ApnsClient;
import com.relayrides.pushy.apns.PushNotificationResponse;
import com.relayrides.pushy.apns.util.ApnsPayloadBuilder;
import com.relayrides.pushy.apns.util.SimpleApnsPushNotification;
import com.jim.im.TenantSpi;
import com.jim.im.db.entity.message.AppCert;
import com.jim.im.exception.message.ApnCertException;
import com.jim.im.exception.message.ApnPushException;
import com.jim.im.message.apn.ApnEnv;
import com.jim.im.message.dao.AppCertDao;
import com.jim.im.mongo.entity.msg.CommMessage;
import com.jim.im.mongo.entity.msg.IosMessage;
import com.jim.im.utils.Assert;
import com.jim.im.utils.StringUtil;
import io.netty.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.net.ssl.SSLException;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * pushyapnClient????appapnClient
 * msgappidapnClient??
 * 
 * @version 1.0.0
 */
@Component
public class ApnClientUnit {

    public static Logger logger = LoggerFactory.getLogger(ApnClientUnit.class);

    Map<String, ApnsClient<SimpleApnsPushNotification>> apnClients;
    Map<String, Future<Void>> connectFutures = new HashMap<String, Future<Void>>();
    List<Thread> connectTasks = new ArrayList<Thread>();

    @Autowired
    AppCertDao appCertDao;
    @Autowired
    TenantSpi tenantSpi;

    @PostConstruct
    public void initApnClients() {
        apnClients = new HashMap<>(tenantSpi.getAppIdCount());

        for (String appId : tenantSpi.getAllAppId()) {
            AppCert example = new AppCert();
            example.setAppId(appId);
            List<AppCert> certs = appCertDao.findByExample(example);
            if (certs == null || certs.size() == 0) {
                continue;
            }
            AppCert cert = certs.get(0);
            final ApnEnv env = cert.isProductionCertEnabled() ? ApnEnv.ProductionEvn : ApnEnv.DevEnv;
            String pwd = cert.isProductionCertEnabled() ? cert.getProductionCertPwd() : cert.getDevCertPwd();
            byte[] bytes = cert.isProductionCertEnabled() ? cert.getProductionCert() : cert.getDevCert();
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            startApnsClient(appId, env, pwd, bis);
        }
    }

    public void sendMsg(IosMessage msg) throws ApnCertException, ApnPushException, InterruptedException {
        Assert.notNull(apnClients);
        ApnsClient<SimpleApnsPushNotification> apnsClient = apnClients.get(msg.getAppId());
        if (apnsClient == null) {
            throw new ApnCertException("apn??,appId:" + msg.getAppId());
        }

        waitApnClientConnect(msg, apnsClient);
        //TODO topic
        String apnTopic = "com.GuangZhouXuanWu.iphoneEtion";
        SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(msg.getToken(), apnTopic,
                buildApnPayload(msg.getPushContent(), msg.toClientMessage()));
        Future<PushNotificationResponse<SimpleApnsPushNotification>> sendNotificationFuture = apnsClient
                .sendNotification(pushNotification);

        try {
            final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture
                    .get();
            if (pushNotificationResponse.isAccepted()) {
                return;
            } else {
                throw new ApnPushException("Notification rejected by the APNs gateway, msgId: " + msg.getId()
                        + "reason: " + pushNotificationResponse.getRejectionReason());
            }
        } catch (final ExecutionException e) {
            throw new ApnPushException(
                    "Failed to send push notification because of ExecutionException, msgId: " + msg.getId(), e);
        }

    }

    private String buildApnPayload(String tittle, CommMessage msg) {
        if (StringUtil.isEmpty(tittle))
            tittle = "??";
        ApnsPayloadBuilder builder = new ApnsPayloadBuilder();
        builder.setAlertBody(tittle).addCustomProperty("c", msg.getContent());
        return builder.buildWithDefaultMaximumLength();
    }

    public void destroy() {
        connectTasks.forEach(Thread::interrupt);
    }

    /**
     * @param appId
     * @param env
     * @param pwd
     * @param bis
     */
    private void startApnsClient(String appId, final ApnEnv env, String pwd, InputStream bis) {
        try {
            final ApnsClient<SimpleApnsPushNotification> apnsClient = new ApnsClient<>(bis, pwd);
            Thread task = new Thread(new ApnClientConnectTask(apnsClient, env.getHost(), appId));
            task.setName("ApnClientConnectTask_appId:" + appId);
            task.start();
            connectTasks.add(task);
            apnClients.put(appId, apnsClient);
        } catch (SSLException e) {
            logger.error("failed to get SSLContext, exist. appId: " + appId, e);
            System.exit(1);
        }
    }

    /**
     * @param msg
     * @param apnsClient
     * @throws InterruptedException
     */
    private void waitApnClientConnect(IosMessage msg, ApnsClient<SimpleApnsPushNotification> apnsClient)
            throws InterruptedException {
        if (!apnsClient.isConnected()) {
            Future<Void> future = null;
            while (true) {
                future = connectFutures.get(msg.getAppId());
                if (future == null) {
                    try {
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                } else {
                    break;
                }
            }
            future.await();
        }
    }

    class ApnClientConnectTask implements Runnable {
        ApnsClient<?> apnClient;
        String host;
        String appId;

        ApnClientConnectTask(ApnsClient<?> apnsClient, String host, String appId) {
            this.apnClient = apnsClient;
            this.host = host;
            this.appId = appId;
        }

        @Override
        public void run() {
            try {
                Future<Void> connectFuture = apnClient.connect(host);
                connectFutures.put(appId, connectFuture);
                connectFuture.await();
            } catch (InterruptedException e) {
                try {
                    apnClient.disconnect().await();
                } catch (InterruptedException e1) {
                    //ignore it
                }
            }
        }
    }
}