Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.sseserver.local.LocalConnectionServiceImpl Maven / Gradle / Ivy
package com.github.sseserver.local;
import com.github.sseserver.SendService;
import com.github.sseserver.qos.MessageRepository;
import com.github.sseserver.qos.QosCompletableFuture;
import com.github.sseserver.remote.*;
import com.github.sseserver.springboot.SseServerBeanDefinitionRegistrar;
import com.github.sseserver.util.LambdaUtil;
import com.github.sseserver.util.PlatformDependentUtil;
import com.github.sseserver.util.TypeUtil;
import com.github.sseserver.util.WebUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 单机长连接(非分布式)
* 1. 如果用nginx代理, 要加下面的配置
* # 长连接配置
* proxy_buffering off;
* proxy_read_timeout 7200s;
* proxy_pass http://xx.xx.xx.xx:xxx;
* proxy_http_version 1.1; #nginx默认是http1.0, 改为1.1 支持长连接, 和后端保持长连接,复用,防止出现文件句柄打开数量过多的错误
* proxy_set_header Connection ""; # 去掉Connection的close字段
*
* @author hao 2021年12月7日19:27:41
*/
public class LocalConnectionServiceImpl implements LocalConnectionService, BeanNameAware, BeanFactoryAware {
private final static Logger log = LoggerFactory.getLogger(LocalConnectionServiceImpl.class);
private final static AtomicInteger SCHEDULED_INDEX = new AtomicInteger();
/**
* 业务维度与链接ID的关系表
*/
protected final Object mutex = new Object();
protected final Map> accessToken2ConnectionIdMap = new ConcurrentHashMap<>();
protected final Map> channel2ConnectionIdMap = new ConcurrentHashMap<>();
protected final Map> tenantId2ConnectionIdMap = new ConcurrentHashMap<>();
protected final Map> userId2ConnectionIdMap = new ConcurrentHashMap<>();
/**
* 链接
*/
protected final Map connectionMap = new ConcurrentHashMap<>();
/**
* 永久事件监听。
* {@link #connectListenerList ,#disconnectListeners}
*/
protected final List> connectListenerList = new ArrayList<>();
protected final List> disconnectListenerList = new ArrayList<>();
protected final List>>> listeningChangeWatchList = new ArrayList<>();
/**
* 如果 {@link Predicate#test(Object)} 返回true,则是只监听一次事件的一次性listener。 否则永久事件监听。
* {@link #connectListenerMap,#disconnectListenerMap}
*/
protected final Map>> connectListenerMap = new ConcurrentHashMap<>();
protected final Map>> disconnectListenerMap = new ConcurrentHashMap<>();
private final ThreadLocal scopeOnWriteableThreadLocal = new ThreadLocal<>();
private final boolean primary;
private final Map setDurationByUserIdMap = new ConcurrentHashMap<>();
private final Map setDurationByAccessTokenMap = new ConcurrentHashMap<>();
private BeanFactory beanFactory;
private String beanName = getClass().getSimpleName();
private final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1, r -> new Thread(r, getBeanName() + "-" + SCHEDULED_INDEX.incrementAndGet()));
private int reconnectTime = 5000;
private Integer serverPort;
private volatile BatchActiveRunnable clusterBatchActiveRunnable;
private long clusterBatchActiveDelay = 100L;
public LocalConnectionServiceImpl() {
this.primary = false;
}
public LocalConnectionServiceImpl(boolean primary) {
this.primary = primary;
}
@Override
public ScheduledExecutorService getScheduled() {
return scheduled;
}
@Override
public SendService> qos() {
String beanName = SseServerBeanDefinitionRegistrar.getAtLeastOnceBeanName(this.beanName);
return beanFactory.getBean(beanName, SendService.class);
}
@Override
public ClusterConnectionService getCluster() {
String beanName = SseServerBeanDefinitionRegistrar.getClusterConnectionServiceBeanName(this.beanName);
return beanFactory.getBean(beanName, ClusterConnectionService.class);
}
@Override
public ServiceDiscoveryService getDiscovery() {
String beanName = SseServerBeanDefinitionRegistrar.getServiceDiscoveryServiceBeanName(this.beanName);
return beanFactory.getBean(beanName, ServiceDiscoveryService.class);
}
@Override
public MessageRepository getLocalMessageRepository() {
String beanName = SseServerBeanDefinitionRegistrar.getLocalMessageRepositoryBeanName(this.beanName);
return beanFactory.getBean(beanName, MessageRepository.class);
}
@Override
public boolean isEnableCluster() {
String beanName = SseServerBeanDefinitionRegistrar.getClusterConnectionServiceBeanName(this.beanName);
try {
return beanFactory.containsBean(beanName);
} catch (Exception e) {
return false;
}
}
@Override
public ClusterMessageRepository getClusterMessageRepository() {
String beanName = SseServerBeanDefinitionRegistrar.getClusterMessageRepositoryBeanName(this.beanName);
return beanFactory.getBean(beanName, ClusterMessageRepository.class);
}
@Override
public SseEmitter connect(ACCESS_USER accessUser, Long keepaliveTime, Map attributeMap) {
if (keepaliveTime == null) {
keepaliveTime = 900_000L;
}
// 设置超时时间,0表示不过期。servlet默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException
SseEmitter result = new SseEmitter<>(keepaliveTime, accessUser);
result.setServerId(WebUtil.getIPAddress(serverPort));
result.onCompletion(completionCallBack(result));
result.onError(errorCallBack(result));
result.onTimeout(timeoutCallBack(result));
if (keepaliveTime > 0) {
result.setTimeoutCheckFuture(scheduled.schedule(
result::disconnectByTimeoutCheck, keepaliveTime, TimeUnit.MILLISECONDS));
}
Long id = result.getId();
String accessToken = wrapStringKey(result.getAccessToken());
String userId = wrapStringKey(result.getUserId());
String tenantId = wrapStringKey(result.getTenantId());
result.addDisConnectListener(e -> {
if (log.isDebugEnabled()) {
log.debug("sse {} connection disconnect : {}", beanName, e);
}
String channel = wrapStringKey(e.getChannel());
notifyListener(e, disconnectListenerList, disconnectListenerMap);
synchronized (mutex) {
connectionMap.remove(id);
Collection tokenEmitterList = accessToken2ConnectionIdMap.get(accessToken);
if (tokenEmitterList != null) {
tokenEmitterList.remove(id);
if (tokenEmitterList.isEmpty()) {
accessToken2ConnectionIdMap.remove(accessToken);
}
}
Collection userList = userId2ConnectionIdMap.get(userId);
if (userList != null) {
userList.remove(id);
if (userList.isEmpty()) {
userId2ConnectionIdMap.remove(userId);
}
}
Collection tenantList = tenantId2ConnectionIdMap.get(tenantId);
if (tenantList != null) {
tenantList.remove(id);
if (tenantList.isEmpty()) {
tenantId2ConnectionIdMap.remove(tenantId);
}
}
Collection channelList = channel2ConnectionIdMap.get(channel);
if (channelList != null) {
channelList.remove(id);
if (channelList.isEmpty()) {
channel2ConnectionIdMap.remove(channel);
}
}
}
});
result.addConnectListener(e -> {
String channel = wrapStringKey(e.getChannel());
synchronized (mutex) {
channel2ConnectionIdMap.computeIfAbsent(channel, o -> Collections.newSetFromMap(new ConcurrentHashMap<>(3)))
.add(e.getId());
}
if (log.isDebugEnabled()) {
log.debug("sse {} connection create : {}", beanName, e);
}
notifyListener(e, connectListenerList, connectListenerMap);
notifyActive(userId, accessToken);
});
result.addListeningWatch(e -> {
for (Consumer>> changeEventConsumer : new ArrayList<>(listeningChangeWatchList)) {
changeEventConsumer.accept(e);
}
});
synchronized (mutex) {
connectionMap.put(id, result);
accessToken2ConnectionIdMap.computeIfAbsent(accessToken, o -> Collections.newSetFromMap(new ConcurrentHashMap<>(3)))
.add(id);
tenantId2ConnectionIdMap.computeIfAbsent(tenantId, o -> Collections.newSetFromMap(new ConcurrentHashMap<>(3)))
.add(id);
userId2ConnectionIdMap.computeIfAbsent(userId, o -> Collections.newSetFromMap(new ConcurrentHashMap<>(3)))
.add(id);
}
if (attributeMap != null) {
result.getAttributeMap().putAll(attributeMap);
}
try {
result.send(SseEmitter.event()
.id(id.toString())
.reconnectTime(reconnectTime)
.name("connect-finish")
.data("{\"connectionId\":\"" + id + "\""
+ ",\"serverTime\":" + System.currentTimeMillis()
+ ",\"reconnectTime\":" + reconnectTime
+ ",\"name\":\"" + beanName + "\""
+ ",\"enableCluster\":" + isEnableCluster()
+ ",\"version\":\"" + PlatformDependentUtil.SSE_SERVER_VERSION + "\""
+ "}"));
return result;
} catch (IOException e) {
if (log.isErrorEnabled()) {
log.error("sse {} send {} IOException:{}", beanName, result, e, e);
}
return null;
}
}
public long getClusterBatchActiveDelay() {
return clusterBatchActiveDelay;
}
public void setClusterBatchActiveDelay(long clusterBatchActiveDelay) {
this.clusterBatchActiveDelay = clusterBatchActiveDelay;
}
private void notifyActive(String userId, String accessToken) {
localActive(userId, accessToken);
if (clusterBatchActiveRunnable != null || isEnableCluster()) {
if (clusterBatchActiveRunnable == null) {
synchronized (this) {
if (clusterBatchActiveRunnable == null) {
clusterBatchActiveRunnable = new BatchActiveRunnable(this);
}
}
}
clusterBatchActiveRunnable.add(userId, accessToken);
getScheduled().schedule(clusterBatchActiveRunnable, clusterBatchActiveDelay, TimeUnit.MILLISECONDS);
}
}
public void localActive(String userId, String accessToken) {
removeSetDuration(userId, accessToken);
}
private void removeSetDuration(String userId, String accessToken) {
Long setDurationByAccessToken = setDurationByAccessTokenMap.remove(wrapStringKey(accessToken));
if (setDurationByAccessToken != null) {
for (SseEmitter e : getConnectionByAccessToken(accessToken)) {
sendSetDuration(e, setDurationByAccessToken);
}
}
Long setDurationByUserId = setDurationByUserIdMap.remove(wrapStringKey(userId));
if (setDurationByUserId != null) {
for (SseEmitter e : getConnectionByUserId(accessToken)) {
sendSetDuration(e, setDurationByUserId);
}
}
}
@Override
public SseEmitter disconnectByConnectionId(Long connectionId) {
SseEmitter sseEmitter = getConnectionById(connectionId);
if (sseEmitter != null && sseEmitter.disconnect()) {
return sseEmitter;
} else {
return null;
}
}
@Override
public SseEmitter disconnectByConnectionId(Long connectionId, Long duration, Long sessionDuration) {
SseEmitter sseEmitter = getConnectionById(connectionId);
if (sseEmitter != null) {
if (duration != null || sessionDuration != null) {
if (duration == null) {
duration = 0L;
}
if (sessionDuration == null) {
sessionDuration = 0L;
}
sseEmitter.setSessionDuration(duration + sessionDuration);
}
if (sseEmitter.disconnect()) {
return sseEmitter;
}
}
return null;
}
@Override
public List> disconnectByConnectionIds(Collection connectionIds) {
if (connectionIds == null) {
return Collections.emptyList();
}
List> disconnectList = new ArrayList<>(connectionIds.size());
for (Long connectionId : connectionIds) {
SseEmitter disconnect = disconnectByConnectionId(connectionId);
if (disconnect != null) {
disconnectList.add(disconnect);
}
}
return disconnectList;
}
private boolean sendSetDuration(SseEmitter result, long durationSecond) {
if (send(result, "sse-set-duration", "{\"duration\":\"" + durationSecond + "\"}")) {
result.setSessionDuration(durationSecond);
return true;
} else {
return false;
}
}
@Override
public List> setDurationByUserId(Serializable userId, long durationSecond) {
List> list = getConnectionByUserId(userId);
List> result = new ArrayList<>(list.size());
for (SseEmitter emitter : list) {
if (sendSetDuration(emitter, durationSecond)) {
result.add(emitter);
}
}
if (result.isEmpty()) {
setDurationByUserIdMap.put(wrapStringKey(userId), durationSecond);
}
return result;
}
@Override
public List> setDurationByAccessToken(String accessToken, long durationSecond) {
List> list = getConnectionByAccessToken(accessToken);
List> result = new ArrayList<>(list.size());
for (SseEmitter emitter : list) {
if (sendSetDuration(emitter, durationSecond)) {
result.add(emitter);
}
}
if (result.isEmpty()) {
setDurationByAccessTokenMap.put(wrapStringKey(accessToken), durationSecond);
}
return result;
}
@Override
public SseEmitter setDurationByConnectionId(Long connectionId, long durationSecond) {
SseEmitter result = getConnectionById(connectionId);
if (sendSetDuration(result, durationSecond)) {
return result;
} else {
return null;
}
}
@Override
public List> disconnectByAccessToken(String accessToken) {
List> sseEmitters = getConnectionByAccessToken(accessToken);
List> result = new ArrayList<>();
if (sseEmitters != null) {
for (SseEmitter next : sseEmitters) {
if (next.disconnect()) {
result.add(next);
}
}
}
return result;
}
@Override
public List> disconnectByUserId(Serializable userId) {
List> sseEmitters = getConnectionByUserId(userId);
List> result = new ArrayList<>();
if (sseEmitters != null) {
for (SseEmitter next : sseEmitters) {
if (next.disconnect()) {
result.add(next);
}
}
}
return result;
}
@Override
public Collection> getConnectionAll() {
return (Collection) connectionMap.values();
}
@Override
public SseEmitter getConnectionById(Long connectionId) {
if (connectionId == null) {
return null;
} else {
return connectionMap.get(connectionId);
}
}
@Override
public List> getConnectionByChannel(String channel) {
Collection idList = channel2ConnectionIdMap.get(wrapStringKey(channel));
if (idList == null || idList.isEmpty()) {
return Collections.emptyList();
}
return idList.stream()
.map(this::getConnectionById)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List> getConnectionByAccessToken(String accessToken) {
Collection idList = accessToken2ConnectionIdMap.get(wrapStringKey(accessToken));
if (idList == null || idList.isEmpty()) {
return Collections.emptyList();
}
return idList.stream()
.map(this::getConnectionById)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List> getConnectionByTenantId(Serializable tenantId) {
Collection idList = tenantId2ConnectionIdMap.get(wrapStringKey(tenantId));
if (idList == null || idList.isEmpty()) {
return Collections.emptyList();
}
return idList.stream()
.map(this::getConnectionById)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List> getConnectionByUserId(Serializable userId) {
Collection idList = userId2ConnectionIdMap.get(wrapStringKey(userId));
if (idList == null || idList.isEmpty()) {
return Collections.emptyList();
}
return idList.stream()
.map(this::getConnectionById)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public void addConnectListener(String accessToken, String channel, Consumer> consumer) {
List> sseEmitters = getConnectionByAccessToken(accessToken);
if (sseEmitters != null) {
for (SseEmitter emitter : sseEmitters) {
if (emitter.isConnect() && Objects.equals(channel, emitter.getChannel())) {
consumer.accept(emitter);
}
}
}
connectListenerMap.computeIfAbsent(accessToken, e -> new ArrayList<>()).add(e -> {
if (Objects.equals(channel, e.getChannel())) {
consumer.accept(e);
return true;
}
return false;
});
}
@Override
public void addConnectListener(String accessToken, Consumer> consumer) {
List> sseEmitters = getConnectionByAccessToken(accessToken);
if (sseEmitters != null) {
for (SseEmitter emitter : sseEmitters) {
if (emitter.isConnect()) {
consumer.accept(emitter);
}
}
}
connectListenerMap.computeIfAbsent(accessToken, e -> new ArrayList<>()).add(e -> {
consumer.accept(e);
return true;
});
}
@Override
public void addConnectListener(Consumer> consumer) {
connectListenerList.add((Consumer) consumer);
}
@Override
public void addDisConnectListener(Consumer> consumer) {
disconnectListenerList.add((Consumer) consumer);
}
@Override
public void addDisConnectListener(String accessToken, Consumer> consumer) {
disconnectListenerMap.computeIfAbsent(accessToken, e -> new ArrayList<>()).add(e -> {
consumer.accept(e);
return true;
});
}
@Override
public void addListeningChangeWatch(Consumer>> watch) {
listeningChangeWatchList.add((Consumer) watch);
}
@Override
public List> getConnectionByListening(String sseListenerName) {
return (List) connectionMap.values().stream()
.filter(e -> e.existListener(sseListenerName))
.collect(Collectors.toList());
}
@Override
public List> getConnectionDTOAll() {
return this.getConnectionAll().stream()
.map(ConnectionDTO::convert)
.collect(Collectors.toList());
}
@Override
public List getConnectionDTOByUserId(Serializable userId) {
return this.getConnectionByUserId(userId).stream()
.map(ConnectionByUserIdDTO::convert)
.collect(Collectors.toList());
}
@Override
public boolean isOnline(Serializable userId) {
Set idList = userId2ConnectionIdMap.get(wrapStringKey(userId));
return idList != null && !idList.isEmpty();
}
@Override
public List getUsers() {
return getConnectionAll().stream()
.map(e -> (ACCESS_USER) e.getAccessUser())
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List getUsersByListening(String sseListenerName) {
return getConnectionAll().stream()
.filter(e -> e.existListener(sseListenerName))
.map(e -> (ACCESS_USER) e.getAccessUser())
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List getUsersByTenantIdListening(Serializable tenantId, String sseListenerName) {
return getConnectionByTenantId(tenantId).stream()
.filter(e -> e.existListener(sseListenerName))
.map(e -> (ACCESS_USER) e.getAccessUser())
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public Collection getConnectionIds() {
return Collections.unmodifiableCollection(connectionMap.keySet());
}
@Override
public Collection getAccessTokens() {
return Collections.unmodifiableCollection(accessToken2ConnectionIdMap.keySet());
}
@Override
public List getTenantIds(Class type) {
return getConnectionAll().stream()
.map(SseEmitter::getTenantId)
.filter(Objects::nonNull)
.map(e -> TypeUtil.cast(e, type))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List getChannels() {
return getConnectionAll().stream()
.map(SseEmitter::getChannel)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
/* getChannels */
@Override
public ACCESS_USER getUser(Serializable userId) {
List> list = getConnectionByUserId(userId);
return list.isEmpty() ? null : list.get(0).getAccessUser();
}
@Override
public Collection getUserIds(Class type) {
if (type == String.class) {
return (Collection) Collections.unmodifiableCollection(userId2ConnectionIdMap.keySet());
} else {
return userId2ConnectionIdMap.keySet().stream()
.map(e -> TypeUtil.cast(e, type))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
@Override
public List getUserIdsByListening(String sseListenerName, Class type) {
return getConnectionAll().stream()
.filter(e -> e.existListener(sseListenerName))
.map(SseEmitter::getUserId)
.filter(Objects::nonNull)
.map(e -> TypeUtil.cast(e, type))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
@Override
public List getUserIdsByTenantIdListening(Serializable tenantId, String sseListenerName, Class type) {
return getConnectionByTenantId(tenantId).stream()
.filter(e -> e.existListener(sseListenerName))
.map(SseEmitter::getUserId)
.filter(Objects::nonNull)
.map(e -> TypeUtil.cast(e, type))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
/**
* 获取当前登录端数量
*/
@Override
public int getAccessTokenCount() {
return accessToken2ConnectionIdMap.size();
}
/**
* 获取当前用户数量
*/
@Override
public int getUserCount() {
return userId2ConnectionIdMap.size();
}
/**
* 获取当前连接数量
*/
@Override
public int getConnectionCount() {
return connectionMap.size();
}
protected Runnable completionCallBack(SseEmitter sseEmitter) {
return () -> {
sseEmitter.disconnect();
if (log.isDebugEnabled()) {
log.debug("sse {} completion 结束连接:{}", beanName, sseEmitter);
}
};
}
protected Runnable timeoutCallBack(SseEmitter sseEmitter) {
return () -> {
sseEmitter.disconnect();
log.debug("sse {} timeout 超过最大连接时间:{}", beanName, sseEmitter);
};
}
protected Consumer errorCallBack(SseEmitter sseEmitter) {
return throwable -> {
sseEmitter.disconnect();
if (log.isDebugEnabled()) {
log.debug("sse {} {} error 发生错误:{}", beanName, sseEmitter, throwable, throwable);
}
};
}
protected String wrapStringKey(Object key) {
return key == null ? "" : key.toString();
}
protected void notifyListener(SseEmitter emitter,
List> listeners,
Map>> listenerMap) {
for (Consumer listener : listeners) {
try {
listener.accept(emitter);
} catch (Exception e) {
if (log.isErrorEnabled()) {
log.error("notifyListener error = {}. listener = {}, emitter = {}", e.toString(), listener, emitter, e);
}
}
}
List> consumerList = listenerMap.get(wrapStringKey(emitter.getAccessToken()));
if (consumerList != null) {
for (Predicate listener : new ArrayList<>(consumerList)) {
try {
if (listener.test(emitter)) {
consumerList.remove(listener);
}
} catch (Exception e) {
if (log.isErrorEnabled()) {
log.error("notifyListener error = {}. predicate = {}, emitter = {}", e.toString(), listener, emitter, e);
}
}
}
}
}
@Override
public T scopeOnWriteable(Callable runnable) {
scopeOnWriteableThreadLocal.set(true);
try {
return runnable.call();
} catch (Exception e) {
LambdaUtil.sneakyThrows(e);
return null;
} finally {
scopeOnWriteableThreadLocal.remove();
}
}
public boolean send(SseEmitter emitter, String name, Object body) {
if (emitter != null && emitter.isActive()) {
Boolean sendAtWriteable = scopeOnWriteableThreadLocal.get();
if (sendAtWriteable != null && sendAtWriteable && !emitter.isWriteable()) {
return false;
}
try {
emitter.send(name, body);
return true;
} catch (IOException e) {
emitter.disconnect();
}
}
return false;
}
public int getReconnectTime() {
return reconnectTime;
}
public void setReconnectTime(int reconnectTime) {
this.reconnectTime = reconnectTime;
}
@Override
public String getBeanName() {
return beanName;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public boolean isPrimary() {
return primary;
}
@Override
public Integer sendAll(String eventName, Object body) {
int count = 0;
for (SseEmitter value : connectionMap.values()) {
if (send(value, eventName, body)) {
count++;
}
}
return count;
}
@Override
public Integer sendAllListening(String eventName, Object body) {
int count = 0;
for (SseEmitter value : connectionMap.values()) {
if (value.existListener(eventName) && send(value, eventName, body)) {
count++;
}
}
return count;
}
@Override
public Integer sendByChannel(Collection channels, String eventName, Object body) {
int count = 0;
for (String channel : channels) {
for (SseEmitter value : getConnectionByChannel(channel)) {
if (send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByChannelListening(Collection channels, String eventName, Object body) {
int count = 0;
for (String channel : channels) {
for (SseEmitter value : getConnectionByChannel(channel)) {
if (value.existListener(eventName) && send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByAccessToken(Collection accessTokens, String eventName, Object body) {
int count = 0;
for (String accessToken : accessTokens) {
for (SseEmitter value : getConnectionByAccessToken(accessToken)) {
if (send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByAccessTokenListening(Collection accessTokens, String eventName, Object body) {
int count = 0;
for (String accessToken : accessTokens) {
for (SseEmitter value : getConnectionByAccessToken(accessToken)) {
if (value.existListener(eventName) && send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByUserId(Collection extends Serializable> userIds, String eventName, Object body) {
int count = 0;
for (Serializable userId : userIds) {
for (SseEmitter value : getConnectionByUserId(userId)) {
if (send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByUserIdListening(Collection extends Serializable> userIds, String eventName, Object body) {
int count = 0;
for (Serializable userId : userIds) {
for (SseEmitter value : getConnectionByUserId(userId)) {
if (value.existListener(eventName) && send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByTenantId(Collection extends Serializable> tenantIds, String eventName, Object body) {
int count = 0;
for (Serializable tenantId : tenantIds) {
for (SseEmitter value : getConnectionByTenantId(tenantId)) {
if (send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public Integer sendByTenantIdListening(Collection extends Serializable> tenantIds, String eventName, Object body) {
int count = 0;
for (Serializable tenantId : tenantIds) {
for (SseEmitter value : getConnectionByTenantId(tenantId)) {
if (value.existListener(eventName) && send(value, eventName, body)) {
count++;
}
}
}
return count;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Autowired(required = false)
public void setServerPort(@Value("${server.port:8080}") Integer serverPort) {
this.serverPort = serverPort;
}
@Override
public String toString() {
return "LocalConnectionServiceImpl{" +
beanName + "[" + connectionMap.size() + "]" +
'}';
}
private static class BatchActiveRunnable implements Runnable {
private final LocalConnectionServiceImpl localConnectionService;
private final Set requestSet = new LinkedHashSet<>();
private BatchActiveRunnable(LocalConnectionServiceImpl localConnectionService) {
this.localConnectionService = localConnectionService;
}
public void add(String userId, String accessToken) {
synchronized (requestSet) {
requestSet.add(new Request(userId, accessToken));
}
}
@Override
public void run() {
if (requestSet.isEmpty()) {
return;
}
ArrayList> activeList = new ArrayList<>();
synchronized (requestSet) {
for (Request request : requestSet) {
activeList.add(request.toMap());
}
requestSet.clear();
}
ClusterConnectionService cluster = localConnectionService.getCluster();
if (cluster instanceof ClusterConnectionServiceImpl) {
((ClusterConnectionServiceImpl) cluster).active(activeList);
}
}
private static class Request {
final String userId;
final String accessToken;
private Request(String userId, String accessToken) {
this.userId = userId;
this.accessToken = accessToken;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Request)) return false;
Request request = (Request) o;
return Objects.equals(userId, request.userId) && Objects.equals(accessToken, request.accessToken);
}
@Override
public int hashCode() {
return Objects.hash(userId, accessToken);
}
public Map toMap() {
Map map = new HashMap<>();
map.put("userId", userId);
map.put("accessToken", accessToken);
return map;
}
}
}
}