Commit 0c10e8d9d3ca01fb31f632560f6089f5d2b1d585
1 parent
0eba7c40
优化info消息的cseq计数
Showing
12 changed files
with
85 additions
and
58 deletions
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
| ... | ... | @@ -56,6 +56,8 @@ public class VideoManagerConstants { |
| 56 | 56 | |
| 57 | 57 | public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; |
| 58 | 58 | |
| 59 | + public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_"; | |
| 60 | + | |
| 59 | 61 | //************************** redis 消息********************************* |
| 60 | 62 | public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_"; |
| 61 | 63 | ... | ... |
src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java
| 1 | 1 | package com.genersoft.iot.vmp.conf.runner; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.common.VideoManagerConstants; | |
| 4 | +import com.genersoft.iot.vmp.conf.UserSetup; | |
| 3 | 5 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 4 | 6 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 5 | 7 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -23,6 +25,9 @@ public class SipDeviceRunner implements CommandLineRunner { |
| 23 | 25 | @Autowired |
| 24 | 26 | private IRedisCatchStorage redisCatchStorage; |
| 25 | 27 | |
| 28 | + @Autowired | |
| 29 | + private UserSetup userSetup; | |
| 30 | + | |
| 26 | 31 | @Override |
| 27 | 32 | public void run(String... args) throws Exception { |
| 28 | 33 | // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线 |
| ... | ... | @@ -32,7 +37,8 @@ public class SipDeviceRunner implements CommandLineRunner { |
| 32 | 37 | for (String deviceId : onlineForAll) { |
| 33 | 38 | storager.online(deviceId); |
| 34 | 39 | } |
| 35 | - | |
| 40 | + // 重置cseq计数 | |
| 41 | + redisCatchStorage.resetAllCSEQ(); | |
| 36 | 42 | // TODO 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 |
| 37 | 43 | } |
| 38 | 44 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
| ... | ... | @@ -14,7 +14,7 @@ import javax.sip.message.Request; |
| 14 | 14 | |
| 15 | 15 | import com.genersoft.iot.vmp.common.StreamInfo; |
| 16 | 16 | import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; |
| 17 | -import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache; | |
| 17 | +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | |
| 18 | 18 | import org.springframework.beans.factory.annotation.Autowired; |
| 19 | 19 | import org.springframework.stereotype.Component; |
| 20 | 20 | |
| ... | ... | @@ -36,6 +36,9 @@ public class SIPRequestHeaderProvider { |
| 36 | 36 | private SipFactory sipFactory; |
| 37 | 37 | |
| 38 | 38 | @Autowired |
| 39 | + private IRedisCatchStorage redisCatchStorage; | |
| 40 | + | |
| 41 | + @Autowired | |
| 39 | 42 | private VideoStreamSessionManager streamSession; |
| 40 | 43 | |
| 41 | 44 | public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { |
| ... | ... | @@ -195,6 +198,7 @@ public class SIPRequestHeaderProvider { |
| 195 | 198 | |
| 196 | 199 | // Forwards |
| 197 | 200 | MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
| 201 | + | |
| 198 | 202 | // ceq |
| 199 | 203 | CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); |
| 200 | 204 | |
| ... | ... | @@ -218,7 +222,7 @@ public class SIPRequestHeaderProvider { |
| 218 | 222 | return request; |
| 219 | 223 | } |
| 220 | 224 | |
| 221 | - public Request createInfoRequest(Device device, StreamInfo streamInfo, String content) | |
| 225 | + public Request createInfoRequest(Device device, StreamInfo streamInfo, String content, Long cseq) | |
| 222 | 226 | throws PeerUnavailableException, ParseException, InvalidArgumentException { |
| 223 | 227 | Request request = null; |
| 224 | 228 | Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId()); |
| ... | ... | @@ -247,10 +251,12 @@ public class SIPRequestHeaderProvider { |
| 247 | 251 | |
| 248 | 252 | // Forwards |
| 249 | 253 | MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); |
| 250 | - | |
| 254 | + if (cseq == null) { | |
| 255 | + cseq = redisCatchStorage.getCSEQ(Request.INFO); | |
| 256 | + } | |
| 251 | 257 | // ceq |
| 252 | 258 | CSeqHeader cSeqHeader = sipFactory.createHeaderFactory() |
| 253 | - .createCSeqHeader(InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()), Request.INFO); | |
| 259 | + .createCSeqHeader(cseq, Request.INFO); | |
| 254 | 260 | |
| 255 | 261 | request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader, |
| 256 | 262 | fromHeader, toHeader, viaHeaders, maxForwards); | ... | ... |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
| ... | ... | @@ -18,7 +18,6 @@ import com.genersoft.iot.vmp.service.IMediaServerService; |
| 18 | 18 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 19 | 19 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 20 | 20 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 21 | -import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache; | |
| 22 | 21 | import gov.nist.javax.sip.SipProviderImpl; |
| 23 | 22 | import gov.nist.javax.sip.SipStackImpl; |
| 24 | 23 | import gov.nist.javax.sip.message.SIPRequest; |
| ... | ... | @@ -1553,12 +1552,12 @@ public class SIPCommander implements ISIPCommander { |
| 1553 | 1552 | @Override |
| 1554 | 1553 | public void playPauseCmd(Device device, StreamInfo streamInfo) { |
| 1555 | 1554 | try { |
| 1556 | - | |
| 1555 | + Long cseq = redisCatchStorage.getCSEQ(Request.INFO); | |
| 1557 | 1556 | StringBuffer content = new StringBuffer(200); |
| 1558 | 1557 | content.append("PAUSE RTSP/1.0\r\n"); |
| 1559 | - content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1558 | + content.append("CSeq: " + cseq + "\r\n"); | |
| 1560 | 1559 | content.append("PauseTime: now\r\n"); |
| 1561 | - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1560 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); | |
| 1562 | 1561 | logger.info(request.toString()); |
| 1563 | 1562 | ClientTransaction clientTransaction = null; |
| 1564 | 1563 | if ("TCP".equals(device.getTransport())) { |
| ... | ... | @@ -1581,11 +1580,12 @@ public class SIPCommander implements ISIPCommander { |
| 1581 | 1580 | @Override |
| 1582 | 1581 | public void playResumeCmd(Device device, StreamInfo streamInfo) { |
| 1583 | 1582 | try { |
| 1583 | + Long cseq = redisCatchStorage.getCSEQ(Request.INFO); | |
| 1584 | 1584 | StringBuffer content = new StringBuffer(200); |
| 1585 | 1585 | content.append("PLAY RTSP/1.0\r\n"); |
| 1586 | - content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1586 | + content.append("CSeq: " + cseq + "\r\n"); | |
| 1587 | 1587 | content.append("Range: npt=now-\r\n"); |
| 1588 | - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1588 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); | |
| 1589 | 1589 | logger.info(request.toString()); |
| 1590 | 1590 | ClientTransaction clientTransaction = null; |
| 1591 | 1591 | if ("TCP".equals(device.getTransport())) { |
| ... | ... | @@ -1607,12 +1607,13 @@ public class SIPCommander implements ISIPCommander { |
| 1607 | 1607 | @Override |
| 1608 | 1608 | public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) { |
| 1609 | 1609 | try { |
| 1610 | + Long cseq = redisCatchStorage.getCSEQ(Request.INFO); | |
| 1610 | 1611 | StringBuffer content = new StringBuffer(200); |
| 1611 | 1612 | content.append("PLAY RTSP/1.0\r\n"); |
| 1612 | - content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1613 | + content.append("CSeq: " + cseq + "\r\n"); | |
| 1613 | 1614 | content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n"); |
| 1614 | 1615 | |
| 1615 | - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1616 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); | |
| 1616 | 1617 | logger.info(request.toString()); |
| 1617 | 1618 | ClientTransaction clientTransaction = null; |
| 1618 | 1619 | if ("TCP".equals(device.getTransport())) { |
| ... | ... | @@ -1634,11 +1635,12 @@ public class SIPCommander implements ISIPCommander { |
| 1634 | 1635 | @Override |
| 1635 | 1636 | public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) { |
| 1636 | 1637 | try { |
| 1638 | + Long cseq = redisCatchStorage.getCSEQ(Request.INFO); | |
| 1637 | 1639 | StringBuffer content = new StringBuffer(200); |
| 1638 | 1640 | content.append("PLAY RTSP/1.0\r\n"); |
| 1639 | - content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n"); | |
| 1641 | + content.append("CSeq: " + cseq + "\r\n"); | |
| 1640 | 1642 | content.append("Scale: " + String.format("%.1f",speed) + "\r\n"); |
| 1641 | - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString()); | |
| 1643 | + Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); | |
| 1642 | 1644 | logger.info(request.toString()); |
| 1643 | 1645 | ClientTransaction clientTransaction = null; |
| 1644 | 1646 | if ("TCP".equals(device.getTransport())) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
| ... | ... | @@ -89,7 +89,7 @@ public class ZLMRunner implements CommandLineRunner { |
| 89 | 89 | }); |
| 90 | 90 | |
| 91 | 91 | // 获取zlm信息 |
| 92 | - logger.info("等待默认zlm接入..."); | |
| 92 | + logger.info("[zlm接入]等待默认zlm中..."); | |
| 93 | 93 | |
| 94 | 94 | // 获取所有的zlm, 并开启主动连接 |
| 95 | 95 | List<MediaServerItem> all = mediaServerService.getAllFromDatabase(); | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/bean/CatalogSubscribeTask.java
| ... | ... | @@ -25,24 +25,28 @@ public class CatalogSubscribeTask implements Runnable{ |
| 25 | 25 | sipCommander.catalogSubscribe(device, eventResult -> { |
| 26 | 26 | ResponseEvent event = (ResponseEvent) eventResult.event; |
| 27 | 27 | Element rootElement = null; |
| 28 | - try { | |
| 29 | - rootElement = XmlUtil.getRootElement(event.getResponse().getRawContent(), "gb2312"); | |
| 30 | - } catch (DocumentException e) { | |
| 31 | - e.printStackTrace(); | |
| 32 | - } | |
| 33 | - Element resultElement = rootElement.element("Result"); | |
| 34 | - String result = resultElement.getText(); | |
| 35 | - if (result.toUpperCase().equals("OK")){ | |
| 36 | - // 成功 | |
| 37 | - logger.info("目录订阅成功: {}", device.getDeviceId()); | |
| 28 | + if (event.getResponse().getRawContent() != null) { | |
| 29 | + try { | |
| 30 | + rootElement = XmlUtil.getRootElement(event.getResponse().getRawContent(), "gb2312"); | |
| 31 | + } catch (DocumentException e) { | |
| 32 | + e.printStackTrace(); | |
| 33 | + } | |
| 34 | + Element resultElement = rootElement.element("Result"); | |
| 35 | + String result = resultElement.getText(); | |
| 36 | + if (result.toUpperCase().equals("OK")){ | |
| 37 | + // 成功 | |
| 38 | + logger.info("[目录订阅]成功: {}", device.getDeviceId()); | |
| 39 | + }else { | |
| 40 | + // 失败 | |
| 41 | + logger.info("[目录订阅]失败: {}-{}", device.getDeviceId(), result); | |
| 42 | + } | |
| 38 | 43 | }else { |
| 39 | - // 失败 | |
| 40 | - logger.info("目录订阅失败: {}-{}", device.getDeviceId(), result); | |
| 44 | + // 成功 | |
| 45 | + logger.info("[目录订阅]成功: {}", device.getDeviceId()); | |
| 41 | 46 | } |
| 42 | - | |
| 43 | 47 | },eventResult -> { |
| 44 | 48 | // 失败 |
| 45 | - logger.warn("目录订阅失败: {}-信令发送失败", device.getDeviceId()); | |
| 49 | + logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); | |
| 46 | 50 | }); |
| 47 | 51 | } |
| 48 | 52 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
| ... | ... | @@ -51,6 +51,8 @@ public class DeviceServiceImpl implements IDeviceService { |
| 51 | 51 | dynamicTask.stopCron(device.getDeviceId()); |
| 52 | 52 | device.setSubscribeCycleForCatalog(0); |
| 53 | 53 | sipCommander.catalogSubscribe(device, null, null); |
| 54 | + // 清空cseq计数 | |
| 55 | + | |
| 54 | 56 | return true; |
| 55 | 57 | } |
| 56 | 58 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
| ... | ... | @@ -83,7 +83,7 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR |
| 83 | 83 | */ |
| 84 | 84 | @Override |
| 85 | 85 | public void run(String... args) throws Exception { |
| 86 | - logger.info("Media Server 缓存初始化"); | |
| 86 | + logger.info("[缓存初始化] Media Server "); | |
| 87 | 87 | List<MediaServerItem> mediaServerItemList = mediaServerMapper.queryAll(); |
| 88 | 88 | for (MediaServerItem mediaServerItem : mediaServerItemList) { |
| 89 | 89 | if (StringUtils.isEmpty(mediaServerItem.getId())) { | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
| ... | ... | @@ -15,6 +15,14 @@ import java.util.Map; |
| 15 | 15 | public interface IRedisCatchStorage { |
| 16 | 16 | |
| 17 | 17 | /** |
| 18 | + * 计数器。为cseq进行计数 | |
| 19 | + * | |
| 20 | + * @param method sip 方法 | |
| 21 | + * @return | |
| 22 | + */ | |
| 23 | + Long getCSEQ(String method); | |
| 24 | + | |
| 25 | + /** | |
| 18 | 26 | * 开始播放时将流存入 |
| 19 | 27 | * |
| 20 | 28 | * @param stream 流信息 |
| ... | ... | @@ -181,4 +189,6 @@ public interface IRedisCatchStorage { |
| 181 | 189 | * 获取Device |
| 182 | 190 | */ |
| 183 | 191 | Device getDevice(String deviceId); |
| 192 | + | |
| 193 | + void resetAllCSEQ(); | |
| 184 | 194 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
| ... | ... | @@ -36,6 +36,28 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { |
| 36 | 36 | |
| 37 | 37 | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| 38 | 38 | |
| 39 | + @Override | |
| 40 | + public Long getCSEQ(String method) { | |
| 41 | + String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetup.getServerId() + "_" + method; | |
| 42 | + | |
| 43 | + long result = redis.incr(key, 1L); | |
| 44 | + if (result > Integer.MAX_VALUE) { | |
| 45 | + redis.set(key, 1); | |
| 46 | + result = 1; | |
| 47 | + } | |
| 48 | + return result; | |
| 49 | + } | |
| 50 | + | |
| 51 | + @Override | |
| 52 | + public void resetAllCSEQ() { | |
| 53 | + String scanKey = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetup.getServerId() + "_*"; | |
| 54 | + List<Object> keys = redis.scan(scanKey); | |
| 55 | + for (int i = 0; i < keys.size(); i++) { | |
| 56 | + String key = (String) keys.get(i); | |
| 57 | + redis.set(key, 1); | |
| 58 | + } | |
| 59 | + } | |
| 60 | + | |
| 39 | 61 | /** |
| 40 | 62 | * 开始播放时将流存入redis |
| 41 | 63 | * | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
| ... | ... | @@ -9,7 +9,6 @@ import com.genersoft.iot.vmp.service.IMediaServerService; |
| 9 | 9 | import com.genersoft.iot.vmp.service.bean.SSRCInfo; |
| 10 | 10 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 11 | 11 | import com.genersoft.iot.vmp.service.IPlayService; |
| 12 | -import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache; | |
| 13 | 12 | import io.swagger.annotations.Api; |
| 14 | 13 | import io.swagger.annotations.ApiImplicitParam; |
| 15 | 14 | import io.swagger.annotations.ApiImplicitParams; |
| ... | ... | @@ -31,7 +30,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; |
| 31 | 30 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 32 | 31 | import org.springframework.web.context.request.async.DeferredResult; |
| 33 | 32 | |
| 34 | -import javax.sip.message.Response; | |
| 35 | 33 | import java.util.UUID; |
| 36 | 34 | |
| 37 | 35 | @Api(tags = "视频回放") |
| ... | ... | @@ -168,7 +166,6 @@ public class PlaybackController { |
| 168 | 166 | logger.warn("streamId不存在!"); |
| 169 | 167 | return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); |
| 170 | 168 | } |
| 171 | - setCseq(streamId); | |
| 172 | 169 | Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); |
| 173 | 170 | cmder.playPauseCmd(device, streamInfo); |
| 174 | 171 | json.put("msg", "ok"); |
| ... | ... | @@ -189,7 +186,6 @@ public class PlaybackController { |
| 189 | 186 | logger.warn("streamId不存在!"); |
| 190 | 187 | return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); |
| 191 | 188 | } |
| 192 | - setCseq(streamId); | |
| 193 | 189 | Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); |
| 194 | 190 | cmder.playResumeCmd(device, streamInfo); |
| 195 | 191 | json.put("msg", "ok"); |
| ... | ... | @@ -211,7 +207,6 @@ public class PlaybackController { |
| 211 | 207 | logger.warn("streamId不存在!"); |
| 212 | 208 | return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); |
| 213 | 209 | } |
| 214 | - setCseq(streamId); | |
| 215 | 210 | Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); |
| 216 | 211 | cmder.playSeekCmd(device, streamInfo, seekTime); |
| 217 | 212 | json.put("msg", "ok"); |
| ... | ... | @@ -238,18 +233,10 @@ public class PlaybackController { |
| 238 | 233 | logger.warn("不支持的speed: " + speed); |
| 239 | 234 | return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST); |
| 240 | 235 | } |
| 241 | - setCseq(streamId); | |
| 242 | 236 | Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); |
| 243 | 237 | cmder.playSpeedCmd(device, streamInfo, speed); |
| 244 | 238 | json.put("msg", "ok"); |
| 245 | 239 | return new ResponseEntity<String>(json.toString(), HttpStatus.OK); |
| 246 | 240 | } |
| 247 | 241 | |
| 248 | - public void setCseq(String streamId) { | |
| 249 | - if (InfoCseqCache.CSEQCACHE.containsKey(streamId)) { | |
| 250 | - InfoCseqCache.CSEQCACHE.put(streamId, InfoCseqCache.CSEQCACHE.get(streamId) + 1); | |
| 251 | - } else { | |
| 252 | - InfoCseqCache.CSEQCACHE.put(streamId, 2L); | |
| 253 | - } | |
| 254 | - } | |
| 255 | 242 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/InfoCseqCache.java deleted
100644 → 0
| 1 | -package com.genersoft.iot.vmp.vmanager.gb28181.session; | |
| 2 | - | |
| 3 | -import java.util.Map; | |
| 4 | -import java.util.concurrent.ConcurrentHashMap; | |
| 5 | - | |
| 6 | -/** | |
| 7 | - * @ClassName: InfoCseqCache | |
| 8 | - * @Description: INFO类型的Sip中cseq的缓存 | |
| 9 | - */ | |
| 10 | -public class InfoCseqCache { | |
| 11 | - | |
| 12 | - public static Map<String, Long> CSEQCACHE = new ConcurrentHashMap<>(); | |
| 13 | - | |
| 14 | -} | |
| 15 | 0 | \ No newline at end of file |