com.mpush.core.push.BroadcastPushTask.java Source code

Java tutorial

Introduction

Here is the source code for com.mpush.core.push.BroadcastPushTask.java

Source

/*
 * (C) Copyright 2015-2016 the original author or authors.
 *
 * 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.
 *
 * Contributors:
 *     ohun@live.cn ()
 */

package com.mpush.core.push;

import com.mpush.api.Message;
import com.mpush.api.connection.Connection;
import com.mpush.api.connection.SessionContext;
import com.mpush.api.spi.push.IPushMessage;
import com.mpush.common.condition.AwaysPassCondition;
import com.mpush.api.common.Condition;
import com.mpush.common.message.PushMessage;
import com.mpush.common.qps.FlowControl;
import com.mpush.common.qps.OverFlowException;
import com.mpush.core.router.LocalRouter;
import com.mpush.core.router.RouterCenter;
import com.mpush.tools.common.TimeLine;
import com.mpush.tools.log.Logs;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by ohun on 16/10/24.
 *
 * @author ohun@live.cn ()
 */
public final class BroadcastPushTask implements PushTask, ChannelFutureListener {

    private final long begin = System.currentTimeMillis();

    private final AtomicInteger finishTasks = new AtomicInteger(0);

    private final FlowControl flowControl;

    private final IPushMessage message;

    private final Condition condition;

    private final TimeLine timeLine = new TimeLine();

    //Iterator, ????????/
    private final Iterator<Map.Entry<String, Map<Integer, LocalRouter>>> iterator;

    public BroadcastPushTask(IPushMessage message, FlowControl flowControl) {
        this.message = message;
        this.flowControl = flowControl;
        this.condition = message.getCondition();
        this.iterator = RouterCenter.I.getLocalRouterManager().routers().entrySet().iterator();
        this.timeLine.begin("push-center-begin");
    }

    @Override
    public void run() {
        flowControl.reset();
        boolean done = broadcast();
        if (done) {//done ?
            if (finishTasks.addAndGet(flowControl.total()) == 0) {
                report();
            }
        } else {//? TODO 
            PushCenter.I.delayTask(flowControl.getDelay(), this);
        }
        flowControl.end();
    }

    private boolean broadcast() {
        try {
            iterator.forEachRemaining(entry -> {

                String userId = entry.getKey();
                entry.getValue().forEach((clientType, router) -> {

                    Connection connection = router.getRouteValue();

                    if (checkCondition(condition, connection)) {//1.?
                        if (connection.isConnected()) {
                            if (connection.getChannel().isWritable()) { //TCP?
                                PushMessage.build(connection).setContent(message.getContent()).send(this);
                                //4. qps, ?????catch
                                if (!flowControl.checkQps()) {
                                    throw new OverFlowException(false);
                                }
                            }
                        } else { //2.??
                            Logs.PUSH.warn(
                                    "[Broadcast] find router in local but conn disconnect, message={}, conn={}",
                                    message, connection);
                            //?
                            RouterCenter.I.getLocalRouterManager().unRegister(userId, clientType);
                        }
                    }

                });

            });
        } catch (OverFlowException e) {
            //????
            return e.isOverMaxLimit() || !iterator.hasNext();
        }
        return !iterator.hasNext();//??, ?
    }

    private void report() {
        Logs.PUSH.info("[Broadcast] task finished, cost={}, message={}", (System.currentTimeMillis() - begin),
                message);
        PushCenter.I.getPushListener().onBroadcastComplete(message, timeLine.end().getTimePoints());//???
    }

    private boolean checkCondition(Condition condition, Connection connection) {
        if (condition == AwaysPassCondition.I)
            return true;
        SessionContext sessionContext = connection.getSessionContext();
        Map<String, Object> env = new HashMap<>();
        env.put("userId", sessionContext.userId);
        env.put("tags", sessionContext.tags);
        env.put("clientVersion", sessionContext.clientVersion);
        env.put("osName", sessionContext.osName);
        env.put("osVersion", sessionContext.osVersion);
        return condition.test(env);
    }

    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (future.isSuccess()) {//??
            Logs.PUSH.info("[Broadcast] push message to client success, userId={}, message={}", message.getUserId(),
                    message);
        } else {//?
            Logs.PUSH.warn("[Broadcast] push message to client failure, userId={}, message={}, conn={}",
                    message.getUserId(), message, future.channel());
        }
        if (finishTasks.decrementAndGet() == 0) {
            report();
        }
    }

    @Override
    public ScheduledExecutorService getExecutor() {
        return ((Message) message).getConnection().getChannel().eventLoop();
    }
}