com.zb.app.websocket.server.WebSocketServerHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.zb.app.websocket.server.WebSocketServerHandler.java

Source

/*
 * Copyright 2011-2016 ZuoBian.com All right reserved. This software is the confidential and proprietary information of
 * ZuoBian.com ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the license agreement you entered into with ZuoBian.com.
 */
package com.zb.app.websocket.server;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import com.zb.app.common.cache.ConcurrentHashSet;
import com.zb.app.common.cons.ResultCode;
import com.zb.app.common.core.SpringContextAware;
import com.zb.app.common.core.lang.Argument;
import com.zb.app.common.core.utilities.CoreUtilities;
import com.zb.app.common.notify.NotifyService;
import com.zb.app.common.notify.event.EventType;
import com.zb.app.common.result.JsonResultUtils;
import com.zb.app.common.util.DateViewTools;
import com.zb.app.web.webuser.ZuobianWebUser;
import com.zb.app.websocket.api.IMessageHandler;
import com.zb.app.websocket.api.MessageEvent;
import com.zb.app.websocket.api.MessageMapper;
import com.zb.app.websocket.cons.ActionEnum;
import com.zb.app.websocket.exception.WebSocketUnSupportException;
import com.zb.app.websocket.server.core.IMessageCallback;
import com.zb.app.websocket.server.core.ISocketCallback;
import com.zb.app.websocket.server.wrapper.ClientWrapper;
import com.zb.app.websocket.server.wrapper.SessionWrapper;
import com.zb.app.websocket.server.wrapper.SocketMessage;

/**
 * @author zxc Jul 24, 2014 4:02:38 PM
 */
@SuppressWarnings("unused")
@Service
public class WebSocketServerHandler {

    @Autowired
    private NotifyService notifyService;

    private static Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class);

    // session,???Client?
    private static Map<ClientWrapper, Set<SessionWrapper>> sessionCacheMap = new ConcurrentHashMap<ClientWrapper, Set<SessionWrapper>>();

    // SocketServer??
    private ISocketCallback callBack;

    // ,
    private Timer timer;
    // ??
    private final Object lock = new Object();
    // 
    private final static int TIMER_START_MILLIS = 10;
    private final static int TIMER_INTERVAL_MILLIS = 100 * 1000;

    // /////////////////////////////////////////////////////////////////////////////////////
    // ////
    // //// ?
    // ////
    // /////////////////////////////////////////////////////////////////////////////////////

    private WebSocketServerHandler() {

    }

    public static WebSocketServerHandler getInstance() {
        return (WebSocketServerHandler) SpringContextAware.getBean("webSocketServerHandler");
    }

    @PostConstruct
    public void init() {
        // ?
        if (timer != null) {
            stop();
        }
        timer = new Timer(false);
        timer.schedule(new CheckTimerTask(), TIMER_START_MILLIS, TIMER_INTERVAL_MILLIS);
        logger.info("CheckTimerTask started; interval={} ms", TIMER_INTERVAL_MILLIS);
    }

    public void stop() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        sessionCacheMap.clear();
        logger.info("CheckTimerTask stopped,and sessionCacheMap has clear!");
    }

    /**
     * apply ?
     * 
     * @author zxc
     */
    public interface ApplyMethod {

        public void invoke(ClientWrapper cw, SessionWrapper sw);
    }

    /**
     * Session?
     * 
     * @author zxc
     */
    class CheckTimerTask extends TimerTask implements ApplyMethod {

        private long lastRun = System.currentTimeMillis();
        private long delta;

        @Override
        public void run() {
            long now = System.currentTimeMillis();
            delta = now - lastRun;
            lastRun = now;
            apply(this);
        }

        @Override
        public void invoke(ClientWrapper cw, SessionWrapper sw) {
            clientHeartbeat(new SocketMessage<Object>(cw), sw.getSocketSession());
        }
    }

    // /////////////////////////////////////////////////////////////////////////////////////
    // ////
    // //// ?
    // ////
    // /////////////////////////////////////////////////////////////////////////////////////

    public void notifyNewClientMessageEvent(final ZuobianWebUser webUser, final MessageMapper<?, ?> mm) {
        notifyService.notify(new MessageEvent(webUser, EventType.newClientMessage, mm));
    }

    public void notifyNewClientConnectEvent(final ZuobianWebUser webUser, final MessageMapper<?, ?> mm) {
        notifyService.notify(new MessageEvent(webUser, EventType.newClientConnect, mm));
    }

    // /////////////////////////////////////////////////////////////////////////////////////
    // ////
    // //// ?API
    // ////
    // /////////////////////////////////////////////////////////////////////////////////////

    /**
     * session,???
     */
    public void sessionOpened(WebSocketSession session) {
        log(session.getRemoteAddress() + " session,???");
        addSession(session);
    }

    /**
     * session,??
     */
    public void sessionUpdate(WebSocketSession session) {
        log(session.getRemoteAddress() + " session,??");
        addSession(session);
    }

    /**
     * ?ServerClient SessionInfo
     * 
     * @return
     */
    public Collection<SessionWrapper> getAllSessionInfo() {
        Collection<SessionWrapper> wsList = new ArrayList<SessionWrapper>();
        for (Set<SessionWrapper> sessionInfoList : sessionCacheMap.values()) {
            wsList.addAll(sessionInfoList);
        }
        return wsList;
    }

    /**
     * WebSocketSession
     * 
     * @return
     */
    public Collection<WebSocketSession> getAllSocketSession() {
        Collection<WebSocketSession> wsList = new ArrayList<WebSocketSession>();
        for (Set<SessionWrapper> sessionInfoList : sessionCacheMap.values()) {
            for (SessionWrapper sw : sessionInfoList) {
                wsList.add(sw.getSocketSession());
            }
        }
        return wsList;
    }

    /**
     * sessionCacheMap,?,?
     * 
     * @return 
     */
    public int getSessionCacheSize() {
        return sessionCacheMap.size();
    }

    /**
     * session,???,????
     */
    public void pullTextMessage(WebSocketSession session, TextMessage message) {
        log(session.getRemoteAddress()
                + " session,???,????!");
        SocketMessage<String> socketMessage = new SocketMessage<String>(getClienInfo(session), message);
        ActionEnum action = socketMessage.getAction();
        switch (action) {
        case CLIENT_REGIST:
            SessionWrapper _sw = getSessionInfo(session);
            addSession(session);
            notifyNewClientConnectEvent(_sw.getWebUser(),
                    new MessageMapper<SocketMessage<?>, IMessageHandler>(socketMessage));
            break;

        case CLIENT_CLOSED:
            sessionClosed(session);
            break;

        case CLIENT_RETURN_RESULT:
            SessionWrapper sessionWrapper = getSessionInfo(session);
            sessionWrapper.setSocketMessage(socketMessage);
            break;

        case CLIENT_SEND_MESSAGE:
            SessionWrapper sw = getSessionInfo(session);
            sw.pullMsg(socketMessage);
            notifyNewClientMessageEvent(sw.getWebUser(),
                    new MessageMapper<SocketMessage<?>, IMessageHandler>(socketMessage));
            break;

        case CLIENT_HEARTBEAT:
            clientHeartbeat(socketMessage, session);
            break;

        case ERROR_FORMAT_MESSAGE:
            handleDataFormatError(socketMessage, session);
            break;

        default:
            throw new WebSocketUnSupportException("??");
        }
    }

    /**
     * session,????,???
     */
    public void pushTextMessage(SessionWrapper sessionWrapper, final SocketMessage<String> socketMessage) {
        WebSocketSession session = sessionWrapper.getSocketSession();
        log(session.getRemoteAddress()
                + " session,????,???");
        ActionEnum action = socketMessage.getAction();
        switch (action) {
        case SERVER_SEND_MESSAGE:
            sessionWrapper.pushMsg(socketMessage, new IMessageCallback() {

                @Override
                public void doAction(SessionWrapper sw) {
                    sw.pullMsg(socketMessage);
                    notifyNewClientMessageEvent(sw.getWebUser(),
                            new MessageMapper<SocketMessage<?>, IMessageHandler>(socketMessage));
                }
            });
            break;

        case SERVER_CLOSED:
            sessionClosed(session);
            break;

        case SERVER_HEARTBEAT:
            clientHeartbeat(socketMessage, session);
            break;

        case ERROR_FORMAT_MESSAGE:
            break;

        default:
            throw new WebSocketUnSupportException("??");
        }
    }

    /**
     * session?
     */
    public void sessionClosed(WebSocketSession session) {
        if (session != null && !session.isOpen()) {
            logger.debug("sessionClosed session is closed!by auto sessionClosed");
            removeSession(session);
            return;
        }
        if (session != null && session.isOpen()) {
            removeSession(session);
        }
    }

    /**
     * ,session
     */
    public void exceptionCaught(WebSocketSession session, Throwable cause) {
        cause.printStackTrace();
        log(CoreUtilities.getExceptionText(cause));
        sessionClosed(session);
    }

    /**
     * ???
     * 
     * @param receiverMessage
     * @param session
     */
    public void handleDataFormatError(SocketMessage<?> receiverMessage, WebSocketSession session) {
        ClientWrapper cw = receiverMessage.getClientInfo();
        SessionWrapper sessionWrapper = getSessionInfo(session);
        if (sessionWrapper != null) {
            sessionWrapper.setLastHeartbeatTime(System.currentTimeMillis());
        }
        SocketMessage<String> sm = new SocketMessage<String>(cw);
        sm.setAction(ActionEnum.ERROR_FORMAT_MESSAGE);
        sm.setData(JsonResultUtils.createJsonResult(ResultCode.ERROR_FORMAT,
                "??????"));
        sessionWrapper.pushMsg(sm);
    }

    // /////////////////////////////////////////////////////////////////////////////////////
    // ////
    // //// ??
    // ////
    // /////////////////////////////////////////////////////////////////////////////////////

    /**
     * Session,?
     */
    public void addSession(WebSocketSession session) {
        ClientWrapper clientInfo = new ClientWrapper(session);
        if (Argument.isNotPositive(clientInfo.getmId()) || !clientInfo.getWebUser().hasLogin()) {// ?
            try {
                sessionClosed(session);
            } catch (Exception e) {
                logger.debug(
                        "addSession session cookie invalid,no find webUser mId or member no login! Unregistered visitors!");
            }
            return;
        }
        Long mId = clientInfo.getmId();
        if (!sessionCacheMap.containsKey(clientInfo)) {
            Set<SessionWrapper> set = new ConcurrentHashSet<SessionWrapper>();
            set.add(new SessionWrapper(clientInfo, session));
            sessionCacheMap.put(clientInfo, set);

            logger.info("id{}WebSocket?ip{},mId:{},nick:{}", session.getId(),
                    clientInfo.getIp(), mId, clientInfo.getWebUser().getUserName());

        } else {
            Set<SessionWrapper> set = sessionCacheMap.get(clientInfo);
            if (Argument.isEmpty(set)) {
                set = new ConcurrentHashSet<SessionWrapper>();
                set.add(new SessionWrapper(clientInfo, session));
                sessionCacheMap.put(clientInfo, set);
            }
            if (!set.contains(new SessionWrapper(clientInfo, session))) {
                set.add(new SessionWrapper(clientInfo, session));
            }

            logger.info("SessionManager update Session:{},IP:{},mId:{}", session.getId(), clientInfo.getIp(), mId);
        }
        notifyNewClientConnectEvent(clientInfo.getWebUser(), null);
    }

    /**
     * midClientWrapper?
     * 
     * @param mid Session mid
     * @return true/false
     */
    public boolean hasClientInfo(Long mId) {
        return sessionCacheMap.containsKey(new ClientWrapper(mId));
    }

    /**
     * ?mId??client?
     * 
     * @param mId
     * @return
     */
    public ClientWrapper getClienInfo(Long mId) {
        if (hasClientInfo(mId)) {
            Set<ClientWrapper> set = sessionCacheMap.keySet();
            for (ClientWrapper cw : set) {
                if (cw != null && cw.equals(new ClientWrapper(mId))) {
                    return cw;
                }
            }
        }
        return null;
    }

    /**
     * ?WebSocketSession??client?
     * 
     * @param mId
     * @return
     */
    public ClientWrapper getClienInfo(WebSocketSession session) {
        Collection<SessionWrapper> registSessionWrappers = getAllSessionInfo();
        if (Argument.isEmpty(registSessionWrappers)) {
            return null;
        }
        for (SessionWrapper sw : registSessionWrappers) {
            if (sw != null && StringUtils.equalsIgnoreCase(session.getId(), sw.getId())) {
                return sw.getClientInfo();
            }
        }
        return null;
    }

    /**
     * ??server?
     * 
     * @param mId
     * @return
     */
    public Set<SessionWrapper> getSessionInfo(Long mId) {
        return hasClientInfo(mId) ? sessionCacheMap.get(getClienInfo(mId)) : Collections.<SessionWrapper>emptySet();
    }

    /**
     * ?mId??WebSocketSession
     * 
     * @param mId
     * @return Session
     */
    public Collection<WebSocketSession> getSession(Long mId) {
        Set<SessionWrapper> set = getSessionInfo(mId);
        Collection<WebSocketSession> wsList = new ArrayList<WebSocketSession>();
        for (SessionWrapper sw : set) {
            wsList.add(sw.getSocketSession());
        }
        return wsList;
    }

    /**
     * ?WebSocketSession??client?
     * 
     * @param mId
     * @return
     */
    public SessionWrapper getSessionInfo(WebSocketSession session) {
        Collection<SessionWrapper> registSessionWrappers = getAllSessionInfo();
        if (Argument.isEmpty(registSessionWrappers)) {
            return null;
        }
        for (SessionWrapper sw : registSessionWrappers) {
            if (sw != null && StringUtils.equalsIgnoreCase(session.getId(), sw.getId())) {
                return sw;
            }
        }
        return null;
    }

    /**
     * ?server?
     * 
     * @param mId
     * @return
     */
    public Set<SessionWrapper> getSessionInfoSet(WebSocketSession session) {
        SessionWrapper sw = getSessionInfo(session);
        if (sw != null && sw.getClientWrapper() != null
                && !Argument.isNotPositive(sw.getClientWrapper().getmId())) {
            return sessionCacheMap.get(new ClientWrapper(sw.getClientWrapper().getmId()));
        }
        return Collections.<SessionWrapper>emptySet();
    }

    /**
     * ?mId,Session,client
     * 
     * @param mId
     */
    public void removeSession(Long mId) {
        Set<SessionWrapper> set = getSessionInfo(mId);
        if (Argument.isEmpty(set)) {
            return;
        }
        for (SessionWrapper sessionWrapper : set) {
            ClientWrapper clientInfo = sessionWrapper.getClientInfo();
            String sessionId = sessionWrapper.getId();
            String ip = clientInfo.getIp();
            try {
                sessionWrapper.getSocketSession().close(CloseStatus.NORMAL);
                logger.error("WebSocketServerHandler removeSession() successfull! mid={},sessionId={},ip={}", mId,
                        sessionId, ip);
            } catch (Exception e) {
                logger.error("WebSocketServerHandler removeSession() faild! mid={},sessionId={},ip={}", mId,
                        sessionId, ip);
            } finally {
                sessionCacheMap.remove(clientInfo);
            }
        }
    }

    /**
     * ?WebSocketSession,Session
     * 
     * @param session Session
     */
    public void removeSession(WebSocketSession session) {
        SessionWrapper sw = getSessionInfo(session);
        if (sw == null || StringUtils.isEmpty(sw.getId())) {
            return;
        }
        ClientWrapper clientInfo = sw.getClientInfo();
        String sessionId = sw.getId();
        String ip = clientInfo.getIp();
        Long mId = clientInfo.getmId();
        try {
            session.close(CloseStatus.NORMAL);
            logger.error("WebSocketServerHandler removeSession() successfull! mid={},sessionId={},ip={}", mId,
                    sessionId, ip);
        } catch (Exception e) {
            if (session != null && !session.isOpen()) {
                logger.error(
                        "WebSocketServerHandler removeSession() is Normally closed!  mid={},sessionId={},ip={}",
                        mId, sessionId, ip);
            } else {
                logger.error("WebSocketServerHandler removeSession() faild!  mid={},sessionId={},ip={}", mId,
                        sessionId, ip);
            }
        } finally {
            Set<SessionWrapper> set = sessionCacheMap.get(clientInfo);
            set.remove(sw);
            if (Argument.isEmpty(set)) {
                sessionCacheMap.remove(clientInfo);
            }
        }
    }

    /**
     * ?????
     * 
     * @param receiverMessage
     * @param session
     */
    public void clientHeartbeat(SocketMessage<?> receiverMessage, WebSocketSession session) {
        ClientWrapper cw = receiverMessage.getClientInfo();
        SessionWrapper sw = getSessionInfo(session);
        String json = JsonResultUtils.createJsonResult(ResultCode.HEARTBEAT, "$zuobian$",
                "Server zxc,hello world!");
        if (session == null) {
            return;
        }
        if (session != null && !session.isOpen()) {
            removeSession(session);
        }
        try {
            session.sendMessage(new TextMessage(json));
            if (sw != null) {
                sw.setLastHeartbeatTime(System.currentTimeMillis());
            }
            logger.debug("clientHeartbeat sessionId={} ,mId={},cId={}, date={}", sw.getId(), cw.getmId(),
                    cw.getWebUser().getcId(), DateViewTools.formatFullDate(new Date()));
        } catch (Exception e) {
            if (System.currentTimeMillis() - sw.getLastHeartbeatTime() >= 1000 * 60 * 5) { // 5??Client
                removeSession(session);
            }
            logger.debug("clientHeartbeat send errorsessionId={} ,mId={},cId={}, date={}", sw.getId(),
                    cw.getmId(), cw.getWebUser().getcId(), DateViewTools.formatFullDate(new Date()));
        }
    }

    public void clientReturnResult(SocketMessage<?> receiverMessage) {
        log("?" + receiverMessage.getClientInfo() + "?"
                + receiverMessage.getRemark());
    }

    /**
     * ?ServerClient
     * 
     * @param socketMessage
     * @return
     */
    public boolean pushAllClient(SocketMessage<String> socketMessage) {
        if (sessionCacheMap != null && sessionCacheMap.size() > 0) {
            Collection<SessionWrapper> allSocketSession = getAllSessionInfo();
            for (SessionWrapper ws : allSocketSession) {
                ClientWrapper cw = ws.getClientInfo();
                logger.debug("?mId={},cId={},sessionId={} Session,!", cw.getmId(),
                        cw.getWebUser().getcId(), ws.getId());
                try {
                    ws.getSocketSession().sendMessage(socketMessage.getServerMessage());
                } catch (Exception e) {
                    logger.error("pushAllClient error!");
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 
     */
    public void sessionIdle(WebSocketSession session) throws Exception {

    }

    /**
     * ??SeesionApplyMethod invoke >>?
     * 
     * @param method
     */
    private void apply(ApplyMethod method) {
        if (sessionCacheMap == null || sessionCacheMap.size() == 0) {
            return;
        }
        for (Entry<ClientWrapper, Set<SessionWrapper>> entry : sessionCacheMap.entrySet()) {
            Set<SessionWrapper> set = entry.getValue();
            ClientWrapper cw = entry.getKey();
            if (!Argument.isNotEmpty(set)) {
                continue;
            }
            for (SessionWrapper sw : set) {
                method.invoke(cw, sw);
            }
        }
    }

    public void log(String msg) {
        if (callBack != null) {
            callBack.notifyAction(msg);
        }
    }

    public ISocketCallback getCallBack() {
        return callBack;
    }

    public void setCallBack(ISocketCallback callBack) {
        this.callBack = callBack;
    }
}