package com.hg.ecommerce.websocket; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.hg.ecommerce.config.ProjectContainer; import com.hg.ecommerce.dao.support.UUIDGenerator; import com.hg.ecommerce.websocket.support.Socket; import com.hg.ecommerce.websocket.support.SocketMessage; /** * ConnectHub, 在这里存放一切的连接对象 * @author Li He * */ public class ConnectionHub { public final static String uuid = UUIDGenerator.randomUUID(); private final static ConnectionHub connectionHub = new ConnectionHub(); /** * 添加一个连接 * @param userId unique user identifier * @param session socket object for WebSocket * @return socket invocation */ public static Socket cacheSocket(String userId, WebSocketSession session){ Socket socket = new Socket(session); connectionHub.pool.put(session.getId(), socket); connectionHub.user_cache.put(session.getId(), userId); return socket; } /** * 添加一个连接 * @param session socket object for WebSocket * @return socket invocation */ public static Socket cacheSocket(WebSocketSession session){ Socket socket = new Socket(session); connectionHub.pool.put(session.getId(), socket); return socket; } /** * 利用session id来获取Socket Object * @param sessionId session id * @return Socket Object */ public static Socket getSocket(String sessionId){ return connectionHub.pool.get(sessionId); } /** * 通过user id这种binding信息,来获取一个Socket对象 * @param uid user identifier * @return Socket */ public static Socket getSocketViaBinding(String uid) { for(Entry<String, String> entry : connectionHub.user_cache.entrySet()){ if(entry.getValue().equals(uid)){ return getSocket(entry.getKey()); } } return null; } /** * 根据user identifier的集合仅仅获取那些在线的用户的Socket对象, * <strong>当获取这些Socket是为了emit消息时,不推荐使用此方法,因为多次的遍历会显著降低效率</strong> * @param uids 若干个user identifier * @return List -- Socket集合 */ public static List<Socket> getSocketsViaBinding(String... uids){ List<Socket> sockets = new ArrayList<Socket>(); List<String> list = Arrays.asList(uids); list.retainAll(connectionHub.user_cache.values()); for(String uid : list){ sockets.add(getSocketViaBinding(uid)); } return sockets; } /** * 根据user identifier的集合获取那一部分offline的用户的bindings信息,当用户不在线却想要发送离线消息时, * 可以使用此方法筛选出offline的用户 * @param uids user identifier * @return List -- String */ public static List<String> getOfflineBindings(String... uids){ List<String> list = Arrays.asList(uids); list.removeAll(connectionHub.user_cache.values()); return list; } /** * 延迟绑定,绑定sessionId 到 一个userId * @param sessionId session id * @param userId user id */ public static void bindSocket(String sessionId, String userId){ connectionHub.user_cache.put(sessionId, userId); } /** * 将TextMessage的raw String解析为PlainTextMessage对象 * @param message TextMessage * @return PlainTextMessage * @throws IOException * @throws JsonMappingException * @throws JsonParseException */ public static SocketMessage transferMessage(TextMessage message) throws IOException { String payload = message.getPayload(); ObjectMapper mapper = ProjectContainer.getInstance(ObjectMapper.class); SocketMessage socketMessage = mapper.readValue(payload, SocketMessage.class); return socketMessage; } /** * 删掉不活跃的连接信息 * @return 删除掉的连接数量 */ public static int retainActiveSocket(){ int count = 0; for(Entry<String, Socket> entry : connectionHub.pool.entrySet()){ if(!entry.getValue().isActive()){ connectionHub.pool.remove(entry.getKey()); connectionHub.user_cache.remove(entry.getKey()); count++; } } return count; } /** * 删除一个Connection,用户应当自行承担connection是否已经关闭的风险 * @param sessionId * @throws IOException */ public static void removeSocket(String sessionId){ removeSocket(connectionHub.pool.get(sessionId)); } /** * safely删除一个Connection * @param session session * @throws IOException session关闭时可能抛出异常 */ public static void removeSocket(Socket socket){ if(socket==null) return; String sessionId = socket.getSessionId(); try{ if(socket.isActive()){ socket.close(); } }catch(IOException exception){ exception.printStackTrace(); }finally{ connectionHub.pool.remove(sessionId); connectionHub.user_cache.remove(sessionId); } } /** * return the number of the active socket * @return int value */ public static int getActiveSocketCount(){ return connectionHub.pool.size(); } /** * Get All The Sockets Except for the one with passing id * @param sessionId session id * @return collection of sockets */ public static Collection<Socket> getOtherSockets(String sessionId){ Collection<Socket> sockets = new ArrayList<Socket>(); for(Entry<String, Socket> entry : connectionHub.pool.entrySet()){ if(entry.getKey().equals(sessionId)){ continue; } sockets.add(entry.getValue()); } return sockets; } /** * 广播对象为全体在线用户 * @param event event name * @param message message object */ public static void emit(String event, Object message){ for(Socket socket : connectionHub.pool.values()){ socket.emit(event, message); } } //*********************************** gorgeous horizontal split ***********************************// /** * pool's key: session id; * pool's value: Socket object; */ private Map<String, Socket> pool = new HashMap<String, Socket>(); /** * cache's key: session id; * cache's value: user identifier; */ private Map<String, String> user_cache = new HashMap<String, String>(); private ConnectionHub() {} }