Report 기능 추가, LMS 기능 추가, MMS 기능 테스트중
@2679d176296337be6c49dbf44640a22aa9d0472d
--- src/main/java/com/munjaon/server/cache/mapper/ReportMapper.java
+++ src/main/java/com/munjaon/server/cache/mapper/ReportMapper.java
... | ... | @@ -3,8 +3,13 @@ |
| 3 | 3 |
import com.munjaon.server.server.dto.ReportDto; |
| 4 | 4 |
import org.apache.ibatis.annotations.Mapper; |
| 5 | 5 |
|
| 6 |
+import java.util.List; |
|
| 7 |
+import java.util.Map; |
|
| 8 |
+ |
|
| 6 | 9 |
@Mapper |
| 7 | 10 |
public interface ReportMapper {
|
| 8 | 11 |
ReportDto getReportForUser(String userId); |
| 12 |
+ List<ReportDto> getReportListForUser(String userId); |
|
| 9 | 13 |
int deleteReport(String msgId); |
| 14 |
+ int deleteBulkReport(Map<String, String> reqMap); |
|
| 10 | 15 |
} |
--- src/main/java/com/munjaon/server/cache/service/ReportService.java
+++ src/main/java/com/munjaon/server/cache/service/ReportService.java
... | ... | @@ -6,6 +6,10 @@ |
| 6 | 6 |
import lombok.extern.slf4j.Slf4j; |
| 7 | 7 |
import org.springframework.stereotype.Service; |
| 8 | 8 |
|
| 9 |
+import java.util.HashMap; |
|
| 10 |
+import java.util.List; |
|
| 11 |
+import java.util.Map; |
|
| 12 |
+ |
|
| 9 | 13 |
@Slf4j |
| 10 | 14 |
@Service |
| 11 | 15 |
@RequiredArgsConstructor |
... | ... | @@ -16,7 +20,23 @@ |
| 16 | 20 |
return reportMapper.getReportForUser(userId); |
| 17 | 21 |
} |
| 18 | 22 |
|
| 23 |
+ public List<ReportDto> getReportListForUser(String userId) {
|
|
| 24 |
+ return reportMapper.getReportListForUser(userId); |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 19 | 27 |
public int deleteReport(String msgId) {
|
| 20 | 28 |
return reportMapper.deleteReport(msgId); |
| 21 | 29 |
} |
| 30 |
+ |
|
| 31 |
+ public int deleteBulkReport(String msgId, String userId) {
|
|
| 32 |
+ Map<String, String> reqMap = new HashMap<>(); |
|
| 33 |
+ reqMap.put("msgId", msgId);
|
|
| 34 |
+ reqMap.put("userId", userId);
|
|
| 35 |
+ |
|
| 36 |
+ return deleteBulkReport(reqMap); |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ public int deleteBulkReport(Map<String, String> reqMap) {
|
|
| 40 |
+ return reportMapper.deleteBulkReport(reqMap); |
|
| 41 |
+ } |
|
| 22 | 42 |
} |
--- src/main/java/com/munjaon/server/config/RunnerConfiguration.java
+++ src/main/java/com/munjaon/server/config/RunnerConfiguration.java
... | ... | @@ -1,11 +1,8 @@ |
| 1 | 1 |
package com.munjaon.server.config; |
| 2 | 2 |
|
| 3 | 3 |
import com.munjaon.server.queue.dto.QueueInfo; |
| 4 |
-import com.munjaon.server.queue.pool.SmsReadQueue; |
|
| 5 |
-import com.munjaon.server.queue.pool.SmsWriteQueue; |
|
| 6 |
-import com.munjaon.server.server.service.CollectServerService; |
|
| 7 |
-import com.munjaon.server.server.service.PropertyLoader; |
|
| 8 |
-import com.munjaon.server.server.service.QueueServerService; |
|
| 4 |
+import com.munjaon.server.queue.pool.*; |
|
| 5 |
+import com.munjaon.server.server.service.*; |
|
| 9 | 6 |
import com.munjaon.server.util.ServiceUtil; |
| 10 | 7 |
import lombok.RequiredArgsConstructor; |
| 11 | 8 |
import lombok.extern.slf4j.Slf4j; |
... | ... | @@ -68,17 +65,115 @@ |
| 68 | 65 |
} |
| 69 | 66 |
|
| 70 | 67 |
@Bean |
| 68 |
+ @Order(2) |
|
| 69 |
+ public CommandLineRunner getRunnerBeanForLmsQueue() {
|
|
| 70 |
+ try {
|
|
| 71 |
+ String[] svcArray = ServiceUtil.getServiceNames(serverConfig.getStringArray("LMS.SERVICE_LIST"));
|
|
| 72 |
+ if (svcArray == null || svcArray.length == 0) {
|
|
| 73 |
+ log.info("LMS service list is empty");
|
|
| 74 |
+ } else {
|
|
| 75 |
+ if (ServiceUtil.isDuplicate(svcArray)) {
|
|
| 76 |
+ log.info("LMS service list is duplicated");
|
|
| 77 |
+ } else {
|
|
| 78 |
+ for (String svc : svcArray) {
|
|
| 79 |
+ log.info("SERVICE CREATE : {}", svc);
|
|
| 80 |
+ QueueInfo queueInfo = QueueInfo.builder().queueName(svc).serviceType("LMS").queuePath(serverConfig.getString("LMS.QUEUE_PATH")).build();
|
|
| 81 |
+ LmsWriteQueue lmsWriteQueue = new LmsWriteQueue(queueInfo); |
|
| 82 |
+ LmsReadQueue lmsReadQueue = new LmsReadQueue(queueInfo); |
|
| 83 |
+ QueueServerService queueServerService = new QueueServerService(svc, lmsWriteQueue, lmsReadQueue); |
|
| 84 |
+ queueServerService.start(); |
|
| 85 |
+ } |
|
| 86 |
+ } |
|
| 87 |
+ } |
|
| 88 |
+ } catch (Exception e) {
|
|
| 89 |
+ throw new RuntimeException(e); |
|
| 90 |
+ } |
|
| 91 |
+ return args -> System.out.println("Runner Bean #2");
|
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ @Bean |
|
| 95 |
+ @Order(2) |
|
| 96 |
+ public CommandLineRunner getRunnerBeanForMmsQueue() {
|
|
| 97 |
+ try {
|
|
| 98 |
+ String[] svcArray = ServiceUtil.getServiceNames(serverConfig.getStringArray("MMS.SERVICE_LIST"));
|
|
| 99 |
+ if (svcArray == null || svcArray.length == 0) {
|
|
| 100 |
+ log.info("MMS service list is empty");
|
|
| 101 |
+ } else {
|
|
| 102 |
+ if (ServiceUtil.isDuplicate(svcArray)) {
|
|
| 103 |
+ log.info("MMS service list is duplicated");
|
|
| 104 |
+ } else {
|
|
| 105 |
+ for (String svc : svcArray) {
|
|
| 106 |
+ log.info("SERVICE CREATE : {}", svc);
|
|
| 107 |
+ QueueInfo queueInfo = QueueInfo.builder().queueName(svc).serviceType("MMS").queuePath(serverConfig.getString("MMS.QUEUE_PATH")).build();
|
|
| 108 |
+ MmsWriteQueue mmsWriteQueue = new MmsWriteQueue(queueInfo); |
|
| 109 |
+ MmsReadQueue mmsReadQueue = new MmsReadQueue(queueInfo); |
|
| 110 |
+ QueueServerService queueServerService = new QueueServerService(svc, mmsWriteQueue, mmsReadQueue); |
|
| 111 |
+ queueServerService.start(); |
|
| 112 |
+ } |
|
| 113 |
+ } |
|
| 114 |
+ } |
|
| 115 |
+ } catch (Exception e) {
|
|
| 116 |
+ throw new RuntimeException(e); |
|
| 117 |
+ } |
|
| 118 |
+ return args -> System.out.println("Runner Bean #2");
|
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ @Bean |
|
| 71 | 122 |
@Order(3) |
| 72 | 123 |
public CommandLineRunner getRunnerBeanForSmsCollector() {
|
| 73 | 124 |
try {
|
| 74 | 125 |
String serviceName = "SMS_COLLECTOR"; |
| 75 | 126 |
String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE"); |
| 76 | 127 |
int port = serverConfig.getInt(serviceName + ".SERVICE_PORT"); |
| 128 |
+// CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port); |
|
| 77 | 129 |
CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port); |
| 78 | 130 |
collectServerService.start(); |
| 79 | 131 |
} catch (Exception e) {
|
| 80 | 132 |
throw new RuntimeException(e); |
| 81 | 133 |
} |
| 82 |
- return args -> System.out.println("Runner Bean #2");
|
|
| 134 |
+ return args -> System.out.println("Runner Bean SmsCollector #3");
|
|
| 135 |
+ } |
|
| 136 |
+ |
|
| 137 |
+ @Bean |
|
| 138 |
+ @Order(3) |
|
| 139 |
+ public CommandLineRunner getRunnerBeanForLmsCollector() {
|
|
| 140 |
+ try {
|
|
| 141 |
+ String serviceName = "LMS_COLLECTOR"; |
|
| 142 |
+ String serviceType = serverConfig.getString(serviceName + ".SERVICE_TYPE"); |
|
| 143 |
+ int port = serverConfig.getInt(serviceName + ".SERVICE_PORT"); |
|
| 144 |
+// CollectBackServerService collectServerService = new CollectBackServerService(serviceName, serviceType, port); |
|
| 145 |
+ CollectServerService collectServerService = new CollectServerService(serviceName, serviceType, port); |
|
| 146 |
+ collectServerService.start(); |
|
| 147 |
+ } catch (Exception e) {
|
|
| 148 |
+ throw new RuntimeException(e); |
|
| 149 |
+ } |
|
| 150 |
+ return args -> System.out.println("Runner Bean SmsCollector #3");
|
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ @Bean |
|
| 154 |
+ @Order(3) |
|
| 155 |
+ public CommandLineRunner getRunnerBeanForReporter() {
|
|
| 156 |
+ try {
|
|
| 157 |
+ String serviceName = "REPORTER"; |
|
| 158 |
+ int port = serverConfig.getInt(serviceName + ".SERVICE_PORT"); |
|
| 159 |
+ ReportServerService reportServerService = new ReportServerService(serviceName, port); |
|
| 160 |
+ reportServerService.start(); |
|
| 161 |
+ } catch (Exception e) {
|
|
| 162 |
+ throw new RuntimeException(e); |
|
| 163 |
+ } |
|
| 164 |
+ return args -> System.out.println("Runner Bean Reporter #3");
|
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 167 |
+ @Bean |
|
| 168 |
+ @Order(4) |
|
| 169 |
+ public CommandLineRunner getRunnerBeanForReportQueue() {
|
|
| 170 |
+ try {
|
|
| 171 |
+ String serviceName = "REPORT_QUEUE"; |
|
| 172 |
+ ReportQueueServerService reportQueueServerService = new ReportQueueServerService(serviceName); |
|
| 173 |
+ reportQueueServerService.start(); |
|
| 174 |
+ } catch (Exception e) {
|
|
| 175 |
+ throw new RuntimeException(e); |
|
| 176 |
+ } |
|
| 177 |
+ return args -> System.out.println("Runner Bean ReporterQueue #4");
|
|
| 83 | 178 |
} |
| 84 | 179 |
} |
--- src/main/java/com/munjaon/server/config/ServiceCode.java
+++ src/main/java/com/munjaon/server/config/ServiceCode.java
... | ... | @@ -25,6 +25,15 @@ |
| 25 | 25 |
MSG_ERROR_MEDIA_SUBJECT(500, "LMS, MMS Subject is Null OR is Over Limit Byte"), |
| 26 | 26 |
MSG_ERROR_MEDIA_MESSAGE(501, "LMS, MMS Message is Null OR is Over Limit Byte"), |
| 27 | 27 |
|
| 28 |
+ /* 카카오 알림톡, 친구톡 메시지 */ |
|
| 29 |
+ |
|
| 30 |
+ /* 리포트 관련 에러 메시지 */ |
|
| 31 |
+ MSG_ERROR_REPORT(800, "REPORT DATA is Null OR is invalid"), |
|
| 32 |
+ MSG_ERROR_REPORT_MSG_ID(801, "MSG_ID is Null OR is Over Limit Byte"), |
|
| 33 |
+ MSG_ERROR_REPORT_AGENT_CODE(802, "AGENT_CODE is Null OR is Over Limit Byte"), |
|
| 34 |
+ MSG_ERROR_REPORT_SEND_TIME(803, "SEND_TIME is Null OR is Over Limit Byte"), |
|
| 35 |
+ MSG_ERROR_REPORT_TELECOM(804, "TELECOM is Null OR is Over Limit Byte"), |
|
| 36 |
+ MSG_ERROR_REPORT_RESULT(805, "RESULT is Null OR is Over Limit Byte"), |
|
| 28 | 37 |
|
| 29 | 38 |
ETC_ERROR(999, "ETC ERROR"); |
| 30 | 39 |
|
+++ src/main/java/com/munjaon/server/queue/config/ReportConfig.java
... | ... | @@ -0,0 +1,46 @@ |
| 1 | +package com.munjaon.server.queue.config; | |
| 2 | + | |
| 3 | +public final class ReportConfig { | |
| 4 | + /* USER_ID (Length : 20 / Position : 0) */ | |
| 5 | + public static final int USER_ID_LENGTH = 20; | |
| 6 | + public static final int USER_ID_POSITION = 0; | |
| 7 | + | |
| 8 | + /* WRITE_COUNT (Length : 10 / Position : 20) */ | |
| 9 | + public static final int WRITE_COUNT_LENGTH = 10; | |
| 10 | + public static final int WRITE_COUNT_POSITION = USER_ID_POSITION + USER_ID_LENGTH; | |
| 11 | + | |
| 12 | + /* READ_COUNT (Length : 10 / Position : 30) */ | |
| 13 | + public static final int READ_COUNT_LENGTH = 10; | |
| 14 | + public static final int READ_COUNT_POSITION = WRITE_COUNT_POSITION + WRITE_COUNT_LENGTH; | |
| 15 | + | |
| 16 | + /* Head 길이 */ | |
| 17 | + public static final int HEAD_LENGTH = READ_COUNT_POSITION + READ_COUNT_LENGTH; | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + /* MSG_ID (Length : 20 / Position : 0) */ | |
| 22 | + public static final int MSG_ID_LENGTH = 20; | |
| 23 | + public static final int MSG_ID_POSITION = 0; | |
| 24 | + | |
| 25 | + /* AGENT_CODE (Length : 2 / Position : 20) */ | |
| 26 | + public static final int AGENT_CODE_LENGTH = 2; | |
| 27 | + public static final int AGENT_CODE_POSITION = MSG_ID_POSITION + MSG_ID_LENGTH; | |
| 28 | + | |
| 29 | + /* SEND_TIME (Length : 14 / Position : 22) */ | |
| 30 | + public static final int SEND_TIME_LENGTH = 14; | |
| 31 | + public static final int SEND_TIME_POSITION = AGENT_CODE_POSITION + AGENT_CODE_LENGTH; | |
| 32 | + | |
| 33 | + /* TELECOM (Length : 3 / Position : 36) */ | |
| 34 | + public static final int TELECOM_LENGTH = 3; | |
| 35 | + public static final int TELECOM_POSITION = SEND_TIME_POSITION + SEND_TIME_LENGTH; | |
| 36 | + | |
| 37 | + /* RESULT (Length : 5 / Position : 39) */ | |
| 38 | + public static final int RESULT_LENGTH = 5; | |
| 39 | + public static final int RESULT_POSITION = TELECOM_POSITION + TELECOM_LENGTH; | |
| 40 | + | |
| 41 | + /* Report 길이 */ | |
| 42 | + public static final int BODY_LENGTH = RESULT_POSITION + RESULT_LENGTH; | |
| 43 | + | |
| 44 | + // 큐에 저장하기전에 바이트를 채울 문자 | |
| 45 | + public static final byte SET_DEFAULT_BYTE = (byte) 0x20; | |
| 46 | +} |
+++ src/main/java/com/munjaon/server/queue/mapper/LmsMapper.java
... | ... | @@ -0,0 +1,7 @@ |
| 1 | +package com.munjaon.server.queue.mapper; | |
| 2 | + | |
| 3 | +import com.munjaon.server.queue.dto.BasicMessageDto; | |
| 4 | + | |
| 5 | +public interface LmsMapper { | |
| 6 | + int insert(BasicMessageDto messageDto); | |
| 7 | +} |
+++ src/main/java/com/munjaon/server/queue/pool/LmsReadQueue.java
... | ... | @@ -0,0 +1,39 @@ |
| 1 | +package com.munjaon.server.queue.pool; | |
| 2 | + | |
| 3 | +import com.munjaon.server.queue.config.MediaBodyConfig; | |
| 4 | +import com.munjaon.server.queue.config.QueueConstants; | |
| 5 | +import com.munjaon.server.queue.dto.BasicMessageDto; | |
| 6 | +import com.munjaon.server.queue.dto.QueueInfo; | |
| 7 | +import com.munjaon.server.util.MessageUtil; | |
| 8 | + | |
| 9 | +import java.nio.ByteBuffer; | |
| 10 | + | |
| 11 | +public class LmsReadQueue extends ReadQueue { | |
| 12 | + public LmsReadQueue(QueueInfo queueInfo) throws Exception { | |
| 13 | + this.queueInfo = queueInfo; | |
| 14 | +// initQueue(); | |
| 15 | + } | |
| 16 | + | |
| 17 | + @Override | |
| 18 | + void popBuffer() throws Exception { | |
| 19 | + this.channel.position(MessageUtil.calcReadPosition(this.popCounter, MediaBodyConfig.LMS_SUM_BYTE_LENGTH)); | |
| 20 | + this.channel.read(this.dataBuffer); | |
| 21 | + } | |
| 22 | + | |
| 23 | + @Override | |
| 24 | + void getBytesForExtendMessage(BasicMessageDto messageDto) { | |
| 25 | + MessageUtil.getBytesForMediaMessage(this.dataBuffer, messageDto); | |
| 26 | + } | |
| 27 | + | |
| 28 | + @Override | |
| 29 | + void initDataBuffer() { | |
| 30 | + if (this.dataBuffer == null) { | |
| 31 | + this.dataBuffer = ByteBuffer.allocateDirect(MediaBodyConfig.LMS_SUM_BYTE_LENGTH); | |
| 32 | + } | |
| 33 | + this.dataBuffer.clear(); | |
| 34 | + for(int loopCnt = 0; loopCnt < MediaBodyConfig.LMS_SUM_BYTE_LENGTH; loopCnt++){ | |
| 35 | + this.dataBuffer.put(QueueConstants.SET_DEFAULT_BYTE); | |
| 36 | + } | |
| 37 | + this.dataBuffer.position(0); | |
| 38 | + } | |
| 39 | +} |
--- src/main/java/com/munjaon/server/queue/pool/LmsWriteQueue.java
+++ src/main/java/com/munjaon/server/queue/pool/LmsWriteQueue.java
... | ... | @@ -4,11 +4,17 @@ |
| 4 | 4 |
import com.munjaon.server.queue.config.MediaBodyConfig; |
| 5 | 5 |
import com.munjaon.server.queue.config.QueueConstants; |
| 6 | 6 |
import com.munjaon.server.queue.dto.BasicMessageDto; |
| 7 |
+import com.munjaon.server.queue.dto.QueueInfo; |
|
| 7 | 8 |
import com.munjaon.server.util.MessageUtil; |
| 8 | 9 |
|
| 9 | 10 |
import java.nio.ByteBuffer; |
| 10 | 11 |
|
| 11 | 12 |
public class LmsWriteQueue extends WriteQueue {
|
| 13 |
+ public LmsWriteQueue(QueueInfo queueInfo) throws Exception {
|
|
| 14 |
+ this.queueInfo = queueInfo; |
|
| 15 |
+ /* 큐초기화 */ |
|
| 16 |
+// initQueue(); |
|
| 17 |
+ } |
|
| 12 | 18 |
|
| 13 | 19 |
@Override |
| 14 | 20 |
public int isValidateMessageForExtend(BasicMessageDto messageDto) {
|
+++ src/main/java/com/munjaon/server/queue/pool/MmsReadQueue.java
... | ... | @@ -0,0 +1,40 @@ |
| 1 | +package com.munjaon.server.queue.pool; | |
| 2 | + | |
| 3 | +import com.munjaon.server.queue.config.MediaBodyConfig; | |
| 4 | +import com.munjaon.server.queue.config.QueueConstants; | |
| 5 | +import com.munjaon.server.queue.dto.BasicMessageDto; | |
| 6 | +import com.munjaon.server.queue.dto.QueueInfo; | |
| 7 | +import com.munjaon.server.util.MessageUtil; | |
| 8 | + | |
| 9 | +import java.nio.ByteBuffer; | |
| 10 | + | |
| 11 | +public class MmsReadQueue extends ReadQueue { | |
| 12 | + public MmsReadQueue(QueueInfo queueInfo) throws Exception { | |
| 13 | + this.queueInfo = queueInfo; | |
| 14 | +// initQueue(); | |
| 15 | + } | |
| 16 | + | |
| 17 | + @Override | |
| 18 | + void popBuffer() throws Exception { | |
| 19 | + this.channel.position(MessageUtil.calcReadPosition(this.popCounter, MediaBodyConfig.MMS_SUM_BYTE_LENGTH)); | |
| 20 | + this.channel.read(this.dataBuffer); | |
| 21 | + } | |
| 22 | + | |
| 23 | + @Override | |
| 24 | + void getBytesForExtendMessage(BasicMessageDto messageDto) { | |
| 25 | + MessageUtil.getBytesForMediaMessage(this.dataBuffer, messageDto); | |
| 26 | + MessageUtil.getBytesForMmsMessage(this.dataBuffer, messageDto); | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + void initDataBuffer() { | |
| 31 | + if (this.dataBuffer == null) { | |
| 32 | + this.dataBuffer = ByteBuffer.allocateDirect(MediaBodyConfig.MMS_SUM_BYTE_LENGTH); | |
| 33 | + } | |
| 34 | + this.dataBuffer.clear(); | |
| 35 | + for(int loopCnt = 0; loopCnt < MediaBodyConfig.MMS_SUM_BYTE_LENGTH; loopCnt++){ | |
| 36 | + this.dataBuffer.put(QueueConstants.SET_DEFAULT_BYTE); | |
| 37 | + } | |
| 38 | + this.dataBuffer.position(0); | |
| 39 | + } | |
| 40 | +} |
--- src/main/java/com/munjaon/server/queue/pool/MmsWriteQueue.java
+++ src/main/java/com/munjaon/server/queue/pool/MmsWriteQueue.java
... | ... | @@ -4,11 +4,17 @@ |
| 4 | 4 |
import com.munjaon.server.queue.config.MediaBodyConfig; |
| 5 | 5 |
import com.munjaon.server.queue.config.QueueConstants; |
| 6 | 6 |
import com.munjaon.server.queue.dto.BasicMessageDto; |
| 7 |
+import com.munjaon.server.queue.dto.QueueInfo; |
|
| 7 | 8 |
import com.munjaon.server.util.MessageUtil; |
| 8 | 9 |
|
| 9 | 10 |
import java.nio.ByteBuffer; |
| 10 | 11 |
|
| 11 | 12 |
public class MmsWriteQueue extends WriteQueue {
|
| 13 |
+ public MmsWriteQueue(QueueInfo queueInfo) throws Exception {
|
|
| 14 |
+ this.queueInfo = queueInfo; |
|
| 15 |
+ /* 큐초기화 */ |
|
| 16 |
+// initQueue(); |
|
| 17 |
+ } |
|
| 12 | 18 |
|
| 13 | 19 |
@Override |
| 14 | 20 |
public int isValidateMessageForExtend(BasicMessageDto messageDto) {
|
+++ src/main/java/com/munjaon/server/queue/pool/ReportQueue.java
... | ... | @@ -0,0 +1,213 @@ |
| 1 | +package com.munjaon.server.queue.pool; | |
| 2 | + | |
| 3 | +import com.munjaon.server.config.ServiceCode; | |
| 4 | +import com.munjaon.server.queue.config.QueueConstants; | |
| 5 | +import com.munjaon.server.queue.config.ReportConfig; | |
| 6 | +import com.munjaon.server.server.dto.ReportDto; | |
| 7 | +import com.munjaon.server.util.FileUtil; | |
| 8 | +import com.munjaon.server.util.MessageUtil; | |
| 9 | +import lombok.Getter; | |
| 10 | + | |
| 11 | +import java.io.File; | |
| 12 | +import java.io.IOException; | |
| 13 | +import java.io.RandomAccessFile; | |
| 14 | +import java.nio.ByteBuffer; | |
| 15 | +import java.nio.channels.FileChannel; | |
| 16 | + | |
| 17 | +public class ReportQueue { | |
| 18 | + private final Object lockObject = new Object(); // Lock Object | |
| 19 | + @Getter | |
| 20 | + private String queuePathFile; // 사용자 큐 파일 | |
| 21 | + @Getter | |
| 22 | + private String userId; // 사용자 아이디 | |
| 23 | + @Getter | |
| 24 | + private int writeCounter = 0; // 쓰기 카운트 | |
| 25 | + @Getter | |
| 26 | + private int readCounter = 0; // 읽기 카운트 | |
| 27 | + private FileChannel channel = null; // Queue File Channel | |
| 28 | + private ByteBuffer headBuffer = ByteBuffer.allocateDirect(ReportConfig.HEAD_LENGTH); // Queue Head Buffer | |
| 29 | + private ByteBuffer bodyBuffer = ByteBuffer.allocateDirect(ReportConfig.BODY_LENGTH); // Queue Body Buffer | |
| 30 | + private byte[] byteArray = null; | |
| 31 | + | |
| 32 | + public ReportQueue(String queuePathFile, String userId) throws Exception { | |
| 33 | + this.queuePathFile = queuePathFile; | |
| 34 | + this.userId = userId; | |
| 35 | + | |
| 36 | + initQueue(); | |
| 37 | + } | |
| 38 | + | |
| 39 | + private void initQueue() throws Exception { | |
| 40 | + /* 2. 경로 체크 및 생성 */ | |
| 41 | + FileUtil.mkdirs(this.queuePathFile); | |
| 42 | + File file = new File(this.queuePathFile + File.separator + this.userId + ".queue"); | |
| 43 | + this.channel = new RandomAccessFile(file, "rw").getChannel(); | |
| 44 | + if (file.length() == 0) { | |
| 45 | + this.writeCounter = 0; | |
| 46 | + this.readCounter = 0; | |
| 47 | + writeHeader(); | |
| 48 | + } else { | |
| 49 | + readHeader(); | |
| 50 | + } | |
| 51 | + } | |
| 52 | + | |
| 53 | + public void pushReportToQueue(ReportDto reportDto) throws Exception { | |
| 54 | + synchronized (lockObject) { | |
| 55 | + if (ServiceCode.OK.getCode() != MessageUtil.isValidateMessageForReport(reportDto)) { | |
| 56 | + return; | |
| 57 | + } | |
| 58 | + | |
| 59 | + /* 1. buffer 초기화 및 데이터 Set */ | |
| 60 | + initBodyBuffer(); | |
| 61 | + MessageUtil.setBytesForReport(this.bodyBuffer, reportDto); | |
| 62 | + /* 2. Header 정보 다시 읽기 */ | |
| 63 | + readHeader(); | |
| 64 | + /* 3. 파일채널에 쓰기 */ | |
| 65 | + this.channel.position(MessageUtil.calcWritePositionForReport(this.writeCounter, ReportConfig.BODY_LENGTH)); | |
| 66 | + this.bodyBuffer.flip(); | |
| 67 | + this.channel.write(this.bodyBuffer); | |
| 68 | + /* 4 Push 카운터 증가 및 head 저장 */ | |
| 69 | + this.writeCounter = this.writeCounter + 1; | |
| 70 | + writeHeader(); | |
| 71 | + } | |
| 72 | + } | |
| 73 | + | |
| 74 | + public ReportDto popReportFromQueue() throws Exception { | |
| 75 | + synchronized (lockObject) { | |
| 76 | + /* 1. Header 정보 다시 읽기 */ | |
| 77 | + readHeader(); | |
| 78 | + /* 2. 읽을 데이터가 없는지 체크 */ | |
| 79 | + if (this.writeCounter <= this.readCounter) { | |
| 80 | + return null; | |
| 81 | + } | |
| 82 | + /* 3. 채널에서 읽기 */ | |
| 83 | + this.bodyBuffer.clear(); | |
| 84 | + this.channel.position(MessageUtil.calcWritePositionForReport(this.readCounter, ReportConfig.BODY_LENGTH)); | |
| 85 | + this.channel.read(this.bodyBuffer); | |
| 86 | + | |
| 87 | + return MessageUtil.getReportFromBuffer(this.bodyBuffer); | |
| 88 | + } | |
| 89 | + } | |
| 90 | + | |
| 91 | + public boolean isTruncateQueue(int maxWriteCount) { | |
| 92 | + boolean truncate = false; | |
| 93 | + synchronized (lockObject) { | |
| 94 | + if (this.writeCounter >= maxWriteCount) { | |
| 95 | + if (this.writeCounter == this.readCounter) { | |
| 96 | + truncate = true; | |
| 97 | + } | |
| 98 | + } | |
| 99 | + } | |
| 100 | + | |
| 101 | + return truncate; | |
| 102 | + } | |
| 103 | + | |
| 104 | + public boolean isWriteLimit(int maxWriteCount) { | |
| 105 | + boolean isLimit = false; | |
| 106 | + synchronized (lockObject) { | |
| 107 | + if (this.writeCounter >= maxWriteCount) { | |
| 108 | + isLimit = true; | |
| 109 | + } | |
| 110 | + } | |
| 111 | + | |
| 112 | + return isLimit; | |
| 113 | + } | |
| 114 | + | |
| 115 | + public void truncateQueue() throws Exception { | |
| 116 | + synchronized (lockObject) { | |
| 117 | + if (isOpen()) { | |
| 118 | + this.channel.truncate(0); | |
| 119 | + this.writeCounter = 0; | |
| 120 | + this.readCounter = 0; | |
| 121 | + writeHeader(); | |
| 122 | + } | |
| 123 | + } | |
| 124 | + } | |
| 125 | + | |
| 126 | + private void readHeader() throws Exception { | |
| 127 | + synchronized (lockObject) { | |
| 128 | + initHeadBuffer(); | |
| 129 | + this.channel.position(0); | |
| 130 | + this.channel.read(this.headBuffer); | |
| 131 | + this.byteArray = new byte[ReportConfig.USER_ID_LENGTH]; | |
| 132 | + // USER_ID | |
| 133 | + this.headBuffer.position(ReportConfig.USER_ID_POSITION); | |
| 134 | + this.headBuffer.get(this.byteArray); | |
| 135 | + this.userId = (new String(this.byteArray)).trim(); | |
| 136 | + // 쓰기 카운트 가져오기 | |
| 137 | + this.byteArray = new byte[ReportConfig.WRITE_COUNT_LENGTH]; | |
| 138 | + this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION); | |
| 139 | + this.headBuffer.get(this.byteArray); | |
| 140 | + this.writeCounter = Integer.parseInt((new String(this.byteArray)).trim()); | |
| 141 | + // 읽기 카운트 가져오기 | |
| 142 | + this.byteArray = new byte[ReportConfig.READ_COUNT_LENGTH]; | |
| 143 | + this.headBuffer.position(ReportConfig.READ_COUNT_POSITION); | |
| 144 | + this.headBuffer.get(this.byteArray); | |
| 145 | + this.readCounter = Integer.parseInt((new String(this.byteArray)).trim()); | |
| 146 | + } | |
| 147 | + } | |
| 148 | + | |
| 149 | + public void addReadCounter() throws Exception { | |
| 150 | + synchronized (lockObject) { | |
| 151 | + /* 읽기 카운트 증가 */ | |
| 152 | + this.readCounter = this.readCounter + 1; | |
| 153 | + | |
| 154 | + initHeadBuffer(); | |
| 155 | + this.channel.position(ReportConfig.USER_ID_POSITION); | |
| 156 | + this.headBuffer.put(this.userId.getBytes()); | |
| 157 | + this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION); | |
| 158 | + this.headBuffer.put(Integer.toString(this.writeCounter).getBytes()); | |
| 159 | + this.headBuffer.position(ReportConfig.READ_COUNT_POSITION); | |
| 160 | + this.headBuffer.put(Integer.toString(this.readCounter).getBytes()); | |
| 161 | + this.headBuffer.flip(); | |
| 162 | + this.channel.write(this.headBuffer); | |
| 163 | + } | |
| 164 | + } | |
| 165 | + | |
| 166 | + private void writeHeader() throws Exception { | |
| 167 | + synchronized (lockObject) { | |
| 168 | + initHeadBuffer(); | |
| 169 | + this.channel.position(ReportConfig.USER_ID_POSITION); | |
| 170 | + this.headBuffer.put(this.userId.getBytes()); | |
| 171 | + this.headBuffer.position(ReportConfig.WRITE_COUNT_POSITION); | |
| 172 | + this.headBuffer.put(Integer.toString(this.writeCounter).getBytes()); | |
| 173 | + this.headBuffer.position(ReportConfig.READ_COUNT_POSITION); | |
| 174 | + this.headBuffer.put(Integer.toString(this.readCounter).getBytes()); | |
| 175 | + this.headBuffer.flip(); | |
| 176 | + this.channel.write(this.headBuffer); | |
| 177 | + } | |
| 178 | + } | |
| 179 | + | |
| 180 | + private void initHeadBuffer() { | |
| 181 | + this.headBuffer.clear(); | |
| 182 | + for(int loopCnt = 0; loopCnt < ReportConfig.HEAD_LENGTH; loopCnt++){ | |
| 183 | + this.headBuffer.put(ReportConfig.SET_DEFAULT_BYTE); | |
| 184 | + } | |
| 185 | + this.headBuffer.position(0); | |
| 186 | + } | |
| 187 | + | |
| 188 | + public void initBodyBuffer() { | |
| 189 | + this.bodyBuffer.clear(); | |
| 190 | + for(int loopCnt = 0; loopCnt < ReportConfig.BODY_LENGTH; loopCnt++){ | |
| 191 | + this.bodyBuffer.put(QueueConstants.SET_DEFAULT_BYTE); | |
| 192 | + } | |
| 193 | + this.bodyBuffer.position(0); | |
| 194 | + } | |
| 195 | + | |
| 196 | + public void close() throws IOException { | |
| 197 | + try { | |
| 198 | + if (isOpen()) { | |
| 199 | + channel.close(); | |
| 200 | + } | |
| 201 | + } catch(IOException e) { | |
| 202 | + throw e; | |
| 203 | + } | |
| 204 | + } | |
| 205 | + | |
| 206 | + public boolean isOpen() { | |
| 207 | + if (this.channel == null) { | |
| 208 | + return false; | |
| 209 | + } | |
| 210 | + | |
| 211 | + return this.channel.isOpen(); | |
| 212 | + } | |
| 213 | +} |
--- src/main/java/com/munjaon/server/queue/service/LmsQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/LmsQueueService.java
... | ... | @@ -1,6 +1,8 @@ |
| 1 | 1 |
package com.munjaon.server.queue.service; |
| 2 | 2 |
|
| 3 |
+import com.munjaon.server.cache.service.SerialNoService; |
|
| 3 | 4 |
import com.munjaon.server.queue.dto.BasicMessageDto; |
| 5 |
+import com.munjaon.server.queue.mapper.LmsMapper; |
|
| 4 | 6 |
import com.munjaon.server.queue.pool.LmsMemoryQueue; |
| 5 | 7 |
import com.munjaon.server.queue.pool.LmsQueuePool; |
| 6 | 8 |
import com.munjaon.server.queue.pool.WriteQueue; |
... | ... | @@ -12,8 +14,10 @@ |
| 12 | 14 |
@Service |
| 13 | 15 |
@RequiredArgsConstructor |
| 14 | 16 |
public class LmsQueueService implements QueueAction {
|
| 17 |
+ private final LmsMapper lmsMapper; |
|
| 15 | 18 |
private final LmsQueuePool queueInstance = LmsQueuePool.getInstance(); |
| 16 | 19 |
private final LmsMemoryQueue memoryQueue = LmsMemoryQueue.getInstance(); |
| 20 |
+ private final SerialNoService serialNoService; |
|
| 17 | 21 |
|
| 18 | 22 |
@Override |
| 19 | 23 |
public int getQueueSize() {
|
... | ... | @@ -51,7 +55,12 @@ |
| 51 | 55 |
|
| 52 | 56 |
@Override |
| 53 | 57 |
public int saveMessageToTable(BasicMessageDto data) {
|
| 54 |
- return 0; |
|
| 58 |
+ String serialNo = serialNoService.getSerialNo(); |
|
| 59 |
+ String groupSerialNo = serialNo.replace("MSGID", "MGRP");
|
|
| 60 |
+ data.setId(serialNo); |
|
| 61 |
+ data.setMsgGroupID(groupSerialNo); |
|
| 62 |
+ log.debug("Save message to table : {}", data);
|
|
| 63 |
+ return lmsMapper.insert(data); |
|
| 55 | 64 |
} |
| 56 | 65 |
|
| 57 | 66 |
@Override |
--- src/main/java/com/munjaon/server/queue/service/SmsQueueService.java
+++ src/main/java/com/munjaon/server/queue/service/SmsQueueService.java
... | ... | @@ -56,7 +56,7 @@ |
| 56 | 56 |
@Override |
| 57 | 57 |
public int saveMessageToTable(BasicMessageDto data) {
|
| 58 | 58 |
String serialNo = serialNoService.getSerialNo(); |
| 59 |
- String groupSerialNo = serialNo.replace("MSGID", "MSGGID");
|
|
| 59 |
+ String groupSerialNo = serialNo.replace("MSGID", "MGRP");
|
|
| 60 | 60 |
data.setId(serialNo); |
| 61 | 61 |
data.setMsgGroupID(groupSerialNo); |
| 62 | 62 |
log.debug("Save message to table : {}", data);
|
--- src/main/java/com/munjaon/server/scheduler/service/CacheScheduleService.java
+++ src/main/java/com/munjaon/server/scheduler/service/CacheScheduleService.java
... | ... | @@ -4,7 +4,6 @@ |
| 4 | 4 |
import lombok.RequiredArgsConstructor; |
| 5 | 5 |
import lombok.extern.slf4j.Slf4j; |
| 6 | 6 |
import org.springframework.beans.factory.annotation.Value; |
| 7 |
-import org.springframework.scheduling.annotation.Scheduled; |
|
| 8 | 7 |
import org.springframework.stereotype.Component; |
| 9 | 8 |
|
| 10 | 9 |
@Slf4j |
... | ... | @@ -20,7 +19,7 @@ |
| 20 | 19 |
/* 사용자 설정 테이블 마지막 업데이트 시간 */ |
| 21 | 20 |
private String config_last_modified_time = null; |
| 22 | 21 |
|
| 23 |
- @Scheduled(cron="0/5 * * * * *") |
|
| 22 |
+// @Scheduled(cron="0/5 * * * * *") |
|
| 24 | 23 |
public void doService() throws Exception {
|
| 25 | 24 |
doMemberService(); |
| 26 | 25 |
} |
--- src/main/java/com/munjaon/server/server/config/ServerConfig.java
+++ src/main/java/com/munjaon/server/server/config/ServerConfig.java
... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 |
/* 서버 연결후 로그인 만료 시간 */ |
| 7 | 7 |
public static final int LIMIT_BIND_TIMEOUT = 5000; |
| 8 | 8 |
/* Session Check 만료 시간 */ |
| 9 |
- public static final int LIMIT_LINK_CHECK_TIMEOUT = 35000; |
|
| 9 |
+ public static final int LIMIT_LINK_CHECK_TIMEOUT = 10000; |
|
| 10 | 10 |
|
| 11 | 11 |
/* 서버 프로퍼티 reload interval 시간 */ |
| 12 | 12 |
public static final Long INTERVAL_PROPERTY_RELOAD_TIME = 3000L; |
--- src/main/java/com/munjaon/server/server/dto/ConnectUserDto.java
+++ src/main/java/com/munjaon/server/server/dto/ConnectUserDto.java
... | ... | @@ -24,6 +24,7 @@ |
| 24 | 24 |
private String remoteIP; |
| 25 | 25 |
/* 요금제(선불 : P / 후불 : A) */ |
| 26 | 26 |
private final String feeType = "A"; |
| 27 |
+ private int command; //요청 command |
|
| 27 | 28 |
|
| 28 | 29 |
private MemberDto memberDto; |
| 29 | 30 |
|
--- src/main/java/com/munjaon/server/server/dto/ReportUserDto.java
+++ src/main/java/com/munjaon/server/server/dto/ReportUserDto.java
... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 |
package com.munjaon.server.server.dto; |
| 2 | 2 |
|
| 3 | 3 |
import com.munjaon.server.cache.dto.MemberDto; |
| 4 |
+import com.munjaon.server.queue.pool.ReportQueue; |
|
| 4 | 5 |
import com.munjaon.server.server.config.ServerConfig; |
| 5 | 6 |
import lombok.Builder; |
| 6 | 7 |
import lombok.Getter; |
... | ... | @@ -12,16 +13,14 @@ |
| 12 | 13 |
@Builder |
| 13 | 14 |
@ToString |
| 14 | 15 |
public class ReportUserDto {
|
| 15 |
- /* 로그인여부 */ |
|
| 16 |
- private boolean isLogin; |
|
| 17 |
- /* 마지막 통신 시간 */ |
|
| 18 |
- private Long lastTrafficTime; |
|
| 19 |
- /* 사용자 ID */ |
|
| 20 |
- private String userId; |
|
| 21 |
- /* 사용자 접속 IP */ |
|
| 22 |
- private String remoteIP; |
|
| 23 |
- |
|
| 24 |
- private MemberDto memberDto; |
|
| 16 |
+ private boolean isLogin; //로그인여부 |
|
| 17 |
+ private Long lastTrafficTime; //마지막 통신 시간 |
|
| 18 |
+ private String userId; //사용자 ID |
|
| 19 |
+ private String remoteIP; //사용자 접속 IP |
|
| 20 |
+ private String queuePath; //저장큐경로 |
|
| 21 |
+ private ReportQueue reportQueue; //ReportQueue |
|
| 22 |
+ private MemberDto memberDto; //사용자 정보 |
|
| 23 |
+ private int command; //요청 command |
|
| 25 | 24 |
|
| 26 | 25 |
public int isAlive() {
|
| 27 | 26 |
if (isLogin) {
|
--- src/main/java/com/munjaon/server/server/packet/LinkCheck.java
+++ src/main/java/com/munjaon/server/server/packet/LinkCheck.java
... | ... | @@ -23,7 +23,7 @@ |
| 23 | 23 |
public static ByteBuffer makeLinkCheckAckBuffer() {
|
| 24 | 24 |
ByteBuffer buffer = ByteBuffer.allocate(Header.HEADER_LENGTH + LINK_CHECK_ACK_BODY_LENGTH); |
| 25 | 25 |
Packet.setDefaultByte(buffer); |
| 26 |
- Header.putHeader(buffer, Header.COMMAND_LINK_CHECK, LINK_CHECK_ACK_BODY_LENGTH); |
|
| 26 |
+ Header.putHeader(buffer, Header.COMMAND_LINK_CHECK_ACK, LINK_CHECK_ACK_BODY_LENGTH); |
|
| 27 | 27 |
buffer.put(LINK_CHECK_ACK_BODY_POSITION, LINK_CHECK_ACK_VALUE.getBytes()); |
| 28 | 28 |
|
| 29 | 29 |
return buffer; |
+++ src/main/java/com/munjaon/server/server/packet/LmsMessage.java
... | ... | @@ -0,0 +1,87 @@ |
| 1 | +package com.munjaon.server.server.packet; | |
| 2 | + | |
| 3 | +import com.munjaon.server.util.CommonUtil; | |
| 4 | + | |
| 5 | +import java.nio.ByteBuffer; | |
| 6 | + | |
| 7 | +public final class LmsMessage { | |
| 8 | + public static final int DELIVER_LMS_BODY_LENGTH = 2119; | |
| 9 | + public static final int DELIVER_LMS_ACK_BODY_LENGTH = 21; | |
| 10 | + | |
| 11 | + /* DELIVER */ | |
| 12 | + /* SUBJECT */ | |
| 13 | + public static final int DELIVER_SUBJECT_LENGTH = 40; | |
| 14 | + public static final int DELIVER_SUBJECT_POSITION = CommonMessage.DELIVER_MSG_TYPE_POSITION + CommonMessage.DELIVER_MSG_TYPE_LENGTH; | |
| 15 | + /* MESSAGE */ | |
| 16 | + public static final int DELIVER_MESSAGE_LENGTH = 2000; | |
| 17 | + public static final int DELIVER_MESSAGE_POSITION = DELIVER_SUBJECT_POSITION + DELIVER_SUBJECT_LENGTH; | |
| 18 | + | |
| 19 | + public static void putSubjectForDeliver(ByteBuffer buffer, String subject) { | |
| 20 | + if (buffer == null || subject == null) { | |
| 21 | + return; | |
| 22 | + } | |
| 23 | + subject = CommonUtil.cutString(subject, DELIVER_SUBJECT_LENGTH); | |
| 24 | + buffer.put(DELIVER_SUBJECT_POSITION, subject.getBytes()); | |
| 25 | + } | |
| 26 | + public static String getSubjectForDeliver(ByteBuffer buffer) { | |
| 27 | + if (buffer == null) { | |
| 28 | + return null; | |
| 29 | + } | |
| 30 | + | |
| 31 | + buffer.position(DELIVER_SUBJECT_POSITION); | |
| 32 | + byte[] destArray = new byte[DELIVER_SUBJECT_LENGTH]; | |
| 33 | + buffer.get(destArray); | |
| 34 | + | |
| 35 | + return Packet.getString(destArray); | |
| 36 | + } | |
| 37 | + | |
| 38 | + public static void putMessageForDeliver(ByteBuffer buffer, String message) { | |
| 39 | + if (buffer == null || message == null) { | |
| 40 | + return; | |
| 41 | + } | |
| 42 | + message = CommonUtil.cutString(message, DELIVER_MESSAGE_LENGTH); | |
| 43 | + buffer.put(DELIVER_MESSAGE_POSITION, message.getBytes()); | |
| 44 | + } | |
| 45 | + public static String getMessageForDeliver(ByteBuffer buffer) { | |
| 46 | + if (buffer == null) { | |
| 47 | + return null; | |
| 48 | + } | |
| 49 | + | |
| 50 | + buffer.position(DELIVER_MESSAGE_POSITION); | |
| 51 | + byte[] destArray = new byte[DELIVER_MESSAGE_LENGTH]; | |
| 52 | + buffer.get(destArray); | |
| 53 | + | |
| 54 | + return Packet.getString(destArray); | |
| 55 | + } | |
| 56 | + | |
| 57 | + public static ByteBuffer makeDeliverAckBuffer(String msgId, String status) { | |
| 58 | + ByteBuffer buffer = ByteBuffer.allocate(Header.HEADER_LENGTH + DELIVER_LMS_ACK_BODY_LENGTH); | |
| 59 | + Packet.setDefaultByte(buffer); | |
| 60 | + Header.putHeader(buffer, Header.COMMAND_DELIVER_ACK, DELIVER_LMS_ACK_BODY_LENGTH); | |
| 61 | + buffer.put(CommonMessage.DELIVER_ACK_MESSAGE_ID_POSITION, msgId.getBytes()); | |
| 62 | + buffer.put(CommonMessage.DELIVER_ACK_RESULT_POSITION, status.getBytes()); | |
| 63 | + | |
| 64 | + return buffer; | |
| 65 | + } | |
| 66 | +// public static void makeDataForDeliver(ByteBuffer buffer, MunjaonMsg data) { | |
| 67 | +// if (buffer == null || data == null) { | |
| 68 | +// return; | |
| 69 | +// } | |
| 70 | +// /* MSG_ID */ | |
| 71 | +// CommonMessage.putMessageIdForDeliver(buffer, data.getMsgId()); | |
| 72 | +// /* SENDER */ | |
| 73 | +// CommonMessage.putSenderForDeliver(buffer, data.getSendPhone()); | |
| 74 | +// /* RECEIVER */ | |
| 75 | +// CommonMessage.putReceiverForDeliver(buffer, data.getRecvPhone()); | |
| 76 | +// /* RESERVE_TIME */ | |
| 77 | +// CommonMessage.putReserveTimeForDeliver(buffer, data.getReserveDate()); | |
| 78 | +// /* REQUEST_TIME */ | |
| 79 | +// CommonMessage.putRequestTimeForDeliver(buffer, data.getRequestDate()); | |
| 80 | +// /* MSG_TYPE */ | |
| 81 | +// CommonMessage.putMsgTypeForDeliver(buffer, data.getMsgType()); | |
| 82 | +// /* Subject */ | |
| 83 | +// putSubjectForDeliver(buffer, data.getSubject()); | |
| 84 | +// /* MSG */ | |
| 85 | +// putMessageForDeliver(buffer, data.getMessage()); | |
| 86 | +// } | |
| 87 | +} |
+++ src/main/java/com/munjaon/server/server/packet/MmsMessage.java
... | ... | @@ -0,0 +1,104 @@ |
| 1 | +package com.munjaon.server.server.packet; | |
| 2 | + | |
| 3 | +import com.munjaon.server.util.CommonUtil; | |
| 4 | + | |
| 5 | +import java.nio.ByteBuffer; | |
| 6 | + | |
| 7 | +public final class MmsMessage { | |
| 8 | + public static final int DELIVER_MMS_BODY_LENGTH = 2120; | |
| 9 | + public static final int DELIVER_MMS_ACK_BODY_LENGTH = 21; | |
| 10 | + | |
| 11 | + /* DELIVER */ | |
| 12 | + /* SUBJECT */ | |
| 13 | + public static final int DELIVER_SUBJECT_LENGTH = 40; | |
| 14 | + public static final int DELIVER_SUBJECT_POSITION = CommonMessage.DELIVER_MSG_TYPE_POSITION + CommonMessage.DELIVER_MSG_TYPE_LENGTH; | |
| 15 | + /* MESSAGE */ | |
| 16 | + public static final int DELIVER_MESSAGE_LENGTH = 2000; | |
| 17 | + public static final int DELIVER_MESSAGE_POSITION = DELIVER_SUBJECT_POSITION + DELIVER_SUBJECT_LENGTH; | |
| 18 | + /* FILECOUNT */ | |
| 19 | + public static final int DELIVER_FILECOUNT_LENGTH = 1; | |
| 20 | + public static final int DELIVER_FILECOUNT_POSITION = DELIVER_MESSAGE_POSITION + DELIVER_MESSAGE_LENGTH; | |
| 21 | + | |
| 22 | + public static void putSubjectForDeliver(ByteBuffer buffer, String subject) { | |
| 23 | + if (buffer == null || subject == null) { | |
| 24 | + return; | |
| 25 | + } | |
| 26 | + subject = CommonUtil.cutString(subject, DELIVER_SUBJECT_LENGTH); | |
| 27 | + buffer.put(DELIVER_SUBJECT_POSITION, subject.getBytes()); | |
| 28 | + } | |
| 29 | + public static String getSubjectForDeliver(ByteBuffer buffer) { | |
| 30 | + if (buffer == null) { | |
| 31 | + return null; | |
| 32 | + } | |
| 33 | + | |
| 34 | + buffer.position(DELIVER_SUBJECT_POSITION); | |
| 35 | + byte[] destArray = new byte[DELIVER_SUBJECT_LENGTH]; | |
| 36 | + buffer.get(destArray); | |
| 37 | + | |
| 38 | + return Packet.getString(destArray); | |
| 39 | + } | |
| 40 | + | |
| 41 | + public static void putMessageForDeliver(ByteBuffer buffer, String message) { | |
| 42 | + if (buffer == null || message == null) { | |
| 43 | + return; | |
| 44 | + } | |
| 45 | + message = CommonUtil.cutString(message, DELIVER_MESSAGE_LENGTH); | |
| 46 | + buffer.put(DELIVER_MESSAGE_POSITION, message.getBytes()); | |
| 47 | + } | |
| 48 | + public static String getMessageForDeliver(ByteBuffer buffer) { | |
| 49 | + if (buffer == null) { | |
| 50 | + return null; | |
| 51 | + } | |
| 52 | + | |
| 53 | + buffer.position(DELIVER_MESSAGE_POSITION); | |
| 54 | + byte[] destArray = new byte[DELIVER_MESSAGE_LENGTH]; | |
| 55 | + buffer.get(destArray); | |
| 56 | + | |
| 57 | + return Packet.getString(destArray); | |
| 58 | + } | |
| 59 | + | |
| 60 | + public static void putFileCountForDeliver(ByteBuffer buffer, int fileCount) { | |
| 61 | + putFileCountForDeliver(buffer, Integer.toString(fileCount)); | |
| 62 | + } | |
| 63 | + | |
| 64 | + public static void putFileCountForDeliver(ByteBuffer buffer, String fileCount) { | |
| 65 | + if (buffer == null || fileCount == null) { | |
| 66 | + return; | |
| 67 | + } | |
| 68 | + buffer.put(DELIVER_FILECOUNT_POSITION, fileCount.getBytes()); | |
| 69 | + } | |
| 70 | + public static String getFileCountForDeliver(ByteBuffer buffer) { | |
| 71 | + if (buffer == null) { | |
| 72 | + return null; | |
| 73 | + } | |
| 74 | + | |
| 75 | + buffer.position(DELIVER_FILECOUNT_POSITION); | |
| 76 | + byte[] destArray = new byte[DELIVER_FILECOUNT_LENGTH]; | |
| 77 | + buffer.get(destArray); | |
| 78 | + | |
| 79 | + return Packet.getString(destArray); | |
| 80 | + } | |
| 81 | +// public static void makeDataForDeliver(ByteBuffer buffer, MunjaonMsg data) { | |
| 82 | +// if (buffer == null || data == null) { | |
| 83 | +// return; | |
| 84 | +// } | |
| 85 | +// /* MSG_ID */ | |
| 86 | +// CommonMessage.putMessageIdForDeliver(buffer, data.getMsgId()); | |
| 87 | +// /* SENDER */ | |
| 88 | +// CommonMessage.putSenderForDeliver(buffer, data.getSendPhone()); | |
| 89 | +// /* RECEIVER */ | |
| 90 | +// CommonMessage.putReceiverForDeliver(buffer, data.getRecvPhone()); | |
| 91 | +// /* RESERVE_TIME */ | |
| 92 | +// CommonMessage.putReserveTimeForDeliver(buffer, data.getReserveDate()); | |
| 93 | +// /* REQUEST_TIME */ | |
| 94 | +// CommonMessage.putRequestTimeForDeliver(buffer, data.getRequestDate()); | |
| 95 | +// /* MSG_TYPE */ | |
| 96 | +// CommonMessage.putMsgTypeForDeliver(buffer, data.getMsgType()); | |
| 97 | +// /* Subject */ | |
| 98 | +// putSubjectForDeliver(buffer, data.getSubject()); | |
| 99 | +// /* MSG */ | |
| 100 | +// putMessageForDeliver(buffer, data.getMessage()); | |
| 101 | +// /* FileCount */ | |
| 102 | +// putFileCountForDeliver(buffer, data.getFileCount()); | |
| 103 | +// } | |
| 104 | +} |
--- src/main/java/com/munjaon/server/server/packet/Packet.java
+++ src/main/java/com/munjaon/server/server/packet/Packet.java
... | ... | @@ -48,10 +48,21 @@ |
| 48 | 48 |
if (dest.capacity() != (srcHead.capacity() + srcBody.capacity())) {
|
| 49 | 49 |
return; |
| 50 | 50 |
} |
| 51 |
- byte[] srcHeadArray = srcHead.array(); |
|
| 52 |
- byte[] srcBodyArray = srcBody.array(); |
|
| 51 |
+ for (int i = 0; i < srcHead.capacity(); i++) {
|
|
| 52 |
+ dest.put(i, srcHead.get(i)); |
|
| 53 |
+ } |
|
| 54 |
+ for (int i = 0; i < srcBody.capacity(); i++) {
|
|
| 55 |
+ dest.put((i + srcHead.capacity()), srcBody.get(i)); |
|
| 56 |
+ } |
|
| 57 |
+ } |
|
| 53 | 58 |
|
| 54 |
- dest.put(0, srcHeadArray); |
|
| 55 |
- dest.put(srcHeadArray.length, srcBodyArray); |
|
| 59 |
+ public static void printBuffer(ByteBuffer buffer) {
|
|
| 60 |
+ if (buffer == null) {
|
|
| 61 |
+ return; |
|
| 62 |
+ } |
|
| 63 |
+ byte[] srcArray = buffer.array(); |
|
| 64 |
+ for (int i = 0; i < srcArray.length; i++) {
|
|
| 65 |
+ System.out.print(srcArray[i] + " "); |
|
| 66 |
+ } |
|
| 56 | 67 |
} |
| 57 | 68 |
} |
+++ src/main/java/com/munjaon/server/server/packet/Report.java
... | ... | @@ -0,0 +1,160 @@ |
| 1 | +package com.munjaon.server.server.packet; | |
| 2 | + | |
| 3 | +import com.munjaon.server.server.dto.ReportDto; | |
| 4 | + | |
| 5 | +import java.nio.ByteBuffer; | |
| 6 | + | |
| 7 | +public final class Report { | |
| 8 | + /* MSG_ID (Length : 20 / Position : 0) */ | |
| 9 | + public static final int REPORT_MSG_ID_LENGTH = 20; | |
| 10 | + public static final int REPORT_MSG_ID_POSITION = Header.HEADER_LENGTH; | |
| 11 | + | |
| 12 | + /* AGENT_CODE (Length : 2 / Position : 20) */ | |
| 13 | + public static final int REPORT_AGENT_CODE_LENGTH = 2; | |
| 14 | + public static final int REPORT_AGENT_CODE_POSITION = REPORT_MSG_ID_POSITION + REPORT_MSG_ID_LENGTH; | |
| 15 | + | |
| 16 | + /* SEND_TIME (Length : 14 / Position : 22) */ | |
| 17 | + public static final int REPORT_SEND_TIME_LENGTH = 14; | |
| 18 | + public static final int REPORT_SEND_TIME_POSITION = REPORT_AGENT_CODE_POSITION + REPORT_AGENT_CODE_LENGTH; | |
| 19 | + | |
| 20 | + /* TELECOM (Length : 3 / Position : 36) */ | |
| 21 | + public static final int REPORT_TELECOM_LENGTH = 3; | |
| 22 | + public static final int REPORT_TELECOM_POSITION = REPORT_SEND_TIME_POSITION + REPORT_SEND_TIME_LENGTH; | |
| 23 | + | |
| 24 | + /* RESULT (Length : 5 / Position : 39) */ | |
| 25 | + public static final int REPORT_RESULT_LENGTH = 5; | |
| 26 | + public static final int REPORT_RESULT_POSITION = REPORT_TELECOM_POSITION + REPORT_TELECOM_LENGTH; | |
| 27 | + | |
| 28 | + /* Report 길이 */ | |
| 29 | + public static final int REPORT_BODY_LENGTH = REPORT_RESULT_POSITION + REPORT_RESULT_LENGTH; | |
| 30 | + | |
| 31 | + /* RESULT (Length : 5 / Position : 39) */ | |
| 32 | + public static final int REPORT_ACK_RESULT_LENGTH = 1; | |
| 33 | + public static final int REPORT_ACK_RESULT_POSITION = 0; | |
| 34 | + | |
| 35 | + public static final int REPORT_ACK_BODY_LENGTH = REPORT_ACK_RESULT_POSITION + REPORT_ACK_RESULT_LENGTH; | |
| 36 | + | |
| 37 | + public static void putReport(final ByteBuffer buffer, final ReportDto reportDto) { | |
| 38 | + if (reportDto == null) { | |
| 39 | + return; | |
| 40 | + } | |
| 41 | + putMsgId(buffer, reportDto.getMsgId()); | |
| 42 | + putAgentCode(buffer, reportDto.getAgentCode()); | |
| 43 | + putSendTime(buffer, reportDto.getRsltDate()); | |
| 44 | + putTelecom(buffer, reportDto.getRsltNet()); | |
| 45 | + putResult(buffer, reportDto.getRsltCode()); | |
| 46 | + } | |
| 47 | + public static void putMsgId(final ByteBuffer buffer, String msgId) { | |
| 48 | + if (buffer == null || msgId == null) { | |
| 49 | + return; | |
| 50 | + } | |
| 51 | + buffer.put(REPORT_MSG_ID_POSITION, msgId.getBytes()); | |
| 52 | + } | |
| 53 | + | |
| 54 | + public static String getMsgId(final ByteBuffer buffer) { | |
| 55 | + if (buffer == null) { | |
| 56 | + return null; | |
| 57 | + } | |
| 58 | + | |
| 59 | + buffer.position(REPORT_MSG_ID_POSITION); | |
| 60 | + byte[] destArray = new byte[REPORT_MSG_ID_LENGTH]; | |
| 61 | + buffer.get(destArray); | |
| 62 | + | |
| 63 | + return Packet.getString(destArray); | |
| 64 | + } | |
| 65 | + | |
| 66 | + public static void putAgentCode(final ByteBuffer buffer, String agentCode) { | |
| 67 | + if (buffer == null || agentCode == null) { | |
| 68 | + return; | |
| 69 | + } | |
| 70 | + buffer.put(REPORT_AGENT_CODE_POSITION, agentCode.getBytes()); | |
| 71 | + } | |
| 72 | + | |
| 73 | + public static String getAgentCode(final ByteBuffer buffer) { | |
| 74 | + if (buffer == null) { | |
| 75 | + return null; | |
| 76 | + } | |
| 77 | + | |
| 78 | + buffer.position(REPORT_AGENT_CODE_POSITION); | |
| 79 | + byte[] destArray = new byte[REPORT_AGENT_CODE_LENGTH]; | |
| 80 | + buffer.get(destArray); | |
| 81 | + | |
| 82 | + return Packet.getString(destArray); | |
| 83 | + } | |
| 84 | + | |
| 85 | + public static void putSendTime(final ByteBuffer buffer, String sendTime) { | |
| 86 | + if (buffer == null || sendTime == null) { | |
| 87 | + return; | |
| 88 | + } | |
| 89 | + buffer.put(REPORT_SEND_TIME_POSITION, sendTime.getBytes()); | |
| 90 | + } | |
| 91 | + | |
| 92 | + public static String getSendTime(final ByteBuffer buffer) { | |
| 93 | + if (buffer == null) { | |
| 94 | + return null; | |
| 95 | + } | |
| 96 | + | |
| 97 | + buffer.position(REPORT_SEND_TIME_POSITION); | |
| 98 | + byte[] destArray = new byte[REPORT_SEND_TIME_LENGTH]; | |
| 99 | + buffer.get(destArray); | |
| 100 | + | |
| 101 | + return Packet.getString(destArray); | |
| 102 | + } | |
| 103 | + | |
| 104 | + public static void putTelecom(final ByteBuffer buffer, String telecom) { | |
| 105 | + if (buffer == null || telecom == null) { | |
| 106 | + return; | |
| 107 | + } | |
| 108 | + buffer.put(REPORT_TELECOM_POSITION, telecom.getBytes()); | |
| 109 | + } | |
| 110 | + | |
| 111 | + public static String getTelecom(final ByteBuffer buffer) { | |
| 112 | + if (buffer == null) { | |
| 113 | + return null; | |
| 114 | + } | |
| 115 | + | |
| 116 | + buffer.position(REPORT_TELECOM_POSITION); | |
| 117 | + byte[] destArray = new byte[REPORT_TELECOM_LENGTH]; | |
| 118 | + buffer.get(destArray); | |
| 119 | + | |
| 120 | + return Packet.getString(destArray); | |
| 121 | + } | |
| 122 | + | |
| 123 | + public static void putResult(final ByteBuffer buffer, String result) { | |
| 124 | + if (buffer == null || result == null) { | |
| 125 | + return; | |
| 126 | + } | |
| 127 | + buffer.put(REPORT_RESULT_POSITION, result.getBytes()); | |
| 128 | + } | |
| 129 | + | |
| 130 | + public static String getResult(final ByteBuffer buffer) { | |
| 131 | + if (buffer == null) { | |
| 132 | + return null; | |
| 133 | + } | |
| 134 | + | |
| 135 | + buffer.position(REPORT_RESULT_POSITION); | |
| 136 | + byte[] destArray = new byte[REPORT_RESULT_LENGTH]; | |
| 137 | + buffer.get(destArray); | |
| 138 | + | |
| 139 | + return Packet.getString(destArray); | |
| 140 | + } | |
| 141 | + | |
| 142 | + public static void putResultAck(final ByteBuffer buffer, String result) { | |
| 143 | + if (buffer == null || result == null) { | |
| 144 | + return; | |
| 145 | + } | |
| 146 | + buffer.put(REPORT_ACK_RESULT_POSITION, result.getBytes()); | |
| 147 | + } | |
| 148 | + | |
| 149 | + public static String getResultAck(final ByteBuffer buffer) { | |
| 150 | + if (buffer == null) { | |
| 151 | + return null; | |
| 152 | + } | |
| 153 | + | |
| 154 | + buffer.position(REPORT_ACK_RESULT_POSITION); | |
| 155 | + byte[] destArray = new byte[REPORT_ACK_RESULT_LENGTH]; | |
| 156 | + buffer.get(destArray); | |
| 157 | + | |
| 158 | + return Packet.getString(destArray); | |
| 159 | + } | |
| 160 | +} |
+++ src/main/java/com/munjaon/server/server/queue/CollectUserQueue.java
... | ... | @@ -0,0 +1,118 @@ |
| 1 | +package com.munjaon.server.server.queue; | |
| 2 | + | |
| 3 | +import com.munjaon.server.server.dto.ConnectUserDto; | |
| 4 | + | |
| 5 | +import java.util.ArrayList; | |
| 6 | +import java.util.LinkedHashMap; | |
| 7 | +import java.util.List; | |
| 8 | +import java.util.Map; | |
| 9 | + | |
| 10 | +public class CollectUserQueue { | |
| 11 | + private final Object lockObject = new Object(); // Lock Object | |
| 12 | + private final Map<String, ConnectUserDto> smstUserQueue = new LinkedHashMap<>(); | |
| 13 | + private final Map<String, ConnectUserDto> lmsUserQueue = new LinkedHashMap<>(); | |
| 14 | + private final Map<String, ConnectUserDto> mmsUserQueue = new LinkedHashMap<>(); | |
| 15 | + private final Map<String, ConnectUserDto> katUserQueue = new LinkedHashMap<>(); | |
| 16 | + private final Map<String, ConnectUserDto> kftUserQueue = new LinkedHashMap<>(); | |
| 17 | + private static CollectUserQueue collectUserQueue; | |
| 18 | + | |
| 19 | + private CollectUserQueue() {} | |
| 20 | + | |
| 21 | + public synchronized static CollectUserQueue getInstance() { | |
| 22 | + if (collectUserQueue == null) { | |
| 23 | + collectUserQueue = new CollectUserQueue(); | |
| 24 | + } | |
| 25 | + return collectUserQueue; | |
| 26 | + } | |
| 27 | + | |
| 28 | + public void putUser(final String serviceType, final ConnectUserDto user) { | |
| 29 | + synchronized (lockObject) { | |
| 30 | + switch (serviceType) { | |
| 31 | + case "SMS" : smstUserQueue.put(user.getUserId(), user); break; | |
| 32 | + case "LMS" : lmsUserQueue.put(user.getUserId(), user); break; | |
| 33 | + case "MMS" : mmsUserQueue.put(user.getUserId(), user); break; | |
| 34 | + case "KAT" : katUserQueue.put(user.getUserId(), user); break; | |
| 35 | + case "KFT" : kftUserQueue.put(user.getUserId(), user); break; | |
| 36 | + default: break; | |
| 37 | + } | |
| 38 | + } | |
| 39 | + } | |
| 40 | + | |
| 41 | + public ConnectUserDto getUser(final String serviceType, final String userId) { | |
| 42 | + synchronized (lockObject) { | |
| 43 | + switch (serviceType) { | |
| 44 | + case "SMS" : return smstUserQueue.get(userId); | |
| 45 | + case "LMS" : return lmsUserQueue.get(userId); | |
| 46 | + case "MMS" : return mmsUserQueue.get(userId); | |
| 47 | + case "KAT" : return katUserQueue.get(userId); | |
| 48 | + case "KFT" : return kftUserQueue.get(userId); | |
| 49 | + default: return null; | |
| 50 | + } | |
| 51 | + } | |
| 52 | + } | |
| 53 | + | |
| 54 | + public List<ConnectUserDto> getUsers(final String serviceType) { | |
| 55 | + synchronized (lockObject) { | |
| 56 | + switch (serviceType) { | |
| 57 | + case "SMS" : return new ArrayList<>(smstUserQueue.values()); | |
| 58 | + case "LMS" : return new ArrayList<>(lmsUserQueue.values()); | |
| 59 | + case "MMS" : return new ArrayList<>(mmsUserQueue.values()); | |
| 60 | + case "KAT" : return new ArrayList<>(katUserQueue.values()); | |
| 61 | + case "KFT" : return new ArrayList<>(kftUserQueue.values()); | |
| 62 | + default: return null; | |
| 63 | + } | |
| 64 | + } | |
| 65 | + } | |
| 66 | + | |
| 67 | + public boolean isExist(final String serviceType, final String userId) { | |
| 68 | + synchronized (lockObject) { | |
| 69 | + switch (serviceType) { | |
| 70 | + case "SMS" : return smstUserQueue.containsKey(userId); | |
| 71 | + case "LMS" : return lmsUserQueue.containsKey(userId); | |
| 72 | + case "MMS" : return mmsUserQueue.containsKey(userId); | |
| 73 | + case "KAT" : return katUserQueue.containsKey(userId); | |
| 74 | + case "KFT" : return kftUserQueue.containsKey(userId); | |
| 75 | + default: return false; | |
| 76 | + } | |
| 77 | + } | |
| 78 | + } | |
| 79 | + | |
| 80 | + public void removeUser(final String serviceType, final String userId) { | |
| 81 | + synchronized (lockObject) { | |
| 82 | + switch (serviceType) { | |
| 83 | + case "SMS" : smstUserQueue.remove(userId); break; | |
| 84 | + case "LMS" : lmsUserQueue.remove(userId); break; | |
| 85 | + case "MMS" : mmsUserQueue.remove(userId); break; | |
| 86 | + case "KAT" : katUserQueue.remove(userId); break; | |
| 87 | + case "KFT" : kftUserQueue.remove(userId); break; | |
| 88 | + default: break; | |
| 89 | + } | |
| 90 | + } | |
| 91 | + } | |
| 92 | + | |
| 93 | + public int size(final String serviceType) { | |
| 94 | + synchronized (lockObject) { | |
| 95 | + switch (serviceType) { | |
| 96 | + case "SMS" : return smstUserQueue.size(); | |
| 97 | + case "LMS" : return lmsUserQueue.size(); | |
| 98 | + case "MMS" : return mmsUserQueue.size(); | |
| 99 | + case "KAT" : return katUserQueue.size(); | |
| 100 | + case "KFT" : return kftUserQueue.size(); | |
| 101 | + default: return 0; | |
| 102 | + } | |
| 103 | + } | |
| 104 | + } | |
| 105 | + | |
| 106 | + public boolean isEmpty(final String serviceType) { | |
| 107 | + synchronized (lockObject) { | |
| 108 | + switch (serviceType) { | |
| 109 | + case "SMS" : return smstUserQueue.isEmpty(); | |
| 110 | + case "LMS" : return lmsUserQueue.isEmpty(); | |
| 111 | + case "MMS" : return mmsUserQueue.isEmpty(); | |
| 112 | + case "KAT" : return katUserQueue.isEmpty(); | |
| 113 | + case "KFT" : return kftUserQueue.isEmpty(); | |
| 114 | + default: return true; | |
| 115 | + } | |
| 116 | + } | |
| 117 | + } | |
| 118 | +} |
+++ src/main/java/com/munjaon/server/server/queue/ReportUserQueue.java
... | ... | @@ -0,0 +1,76 @@ |
| 1 | +package com.munjaon.server.server.queue; | |
| 2 | + | |
| 3 | +import com.munjaon.server.queue.pool.ReportQueue; | |
| 4 | +import com.munjaon.server.server.dto.ReportUserDto; | |
| 5 | + | |
| 6 | +import java.io.IOException; | |
| 7 | +import java.util.ArrayList; | |
| 8 | +import java.util.LinkedHashMap; | |
| 9 | +import java.util.List; | |
| 10 | +import java.util.Map; | |
| 11 | + | |
| 12 | +public class ReportUserQueue { | |
| 13 | + private final Object lockObject = new Object(); // Lock Object | |
| 14 | + private final Map<String, ReportUserDto> connectUserQueue = new LinkedHashMap<>(); | |
| 15 | + private static ReportUserQueue reportUserQueue; | |
| 16 | + | |
| 17 | + private ReportUserQueue() {} | |
| 18 | + | |
| 19 | + public synchronized static ReportUserQueue getInstance() { | |
| 20 | + if (reportUserQueue == null) { | |
| 21 | + reportUserQueue = new ReportUserQueue(); | |
| 22 | + } | |
| 23 | + | |
| 24 | + return reportUserQueue; | |
| 25 | + } | |
| 26 | + | |
| 27 | + public void putUser(final ReportUserDto user) { | |
| 28 | + synchronized (lockObject) { | |
| 29 | + connectUserQueue.put(user.getUserId(), user); | |
| 30 | + } | |
| 31 | + } | |
| 32 | + | |
| 33 | + public ReportUserDto getUser(final String userId) { | |
| 34 | + synchronized (lockObject) { | |
| 35 | + return connectUserQueue.get(userId); | |
| 36 | + } | |
| 37 | + } | |
| 38 | + | |
| 39 | + public List<ReportUserDto> getUsers() { | |
| 40 | + synchronized (lockObject) { | |
| 41 | + return new ArrayList<>(connectUserQueue.values()); | |
| 42 | + } | |
| 43 | + } | |
| 44 | + | |
| 45 | + public boolean isExist(final String userId) { | |
| 46 | + synchronized (lockObject) { | |
| 47 | + return connectUserQueue.containsKey(userId); | |
| 48 | + } | |
| 49 | + } | |
| 50 | + | |
| 51 | + public void removeUser(final String userId) { | |
| 52 | + synchronized (lockObject) { | |
| 53 | + ReportUserDto userDto = connectUserQueue.remove(userId); | |
| 54 | + if (userDto != null) { | |
| 55 | + ReportQueue queue = userDto.getReportQueue(); | |
| 56 | + try { | |
| 57 | + queue.close(); | |
| 58 | + } catch (IOException e) { | |
| 59 | + throw new RuntimeException(e); | |
| 60 | + } | |
| 61 | + } | |
| 62 | + } | |
| 63 | + } | |
| 64 | + | |
| 65 | + public int size() { | |
| 66 | + synchronized (lockObject) { | |
| 67 | + return connectUserQueue.size(); | |
| 68 | + } | |
| 69 | + } | |
| 70 | + | |
| 71 | + public boolean isEmpty() { | |
| 72 | + synchronized (lockObject) { | |
| 73 | + return connectUserQueue.isEmpty(); | |
| 74 | + } | |
| 75 | + } | |
| 76 | +} |
+++ src/main/java/com/munjaon/server/server/sample/Main.java
... | ... | @@ -0,0 +1,34 @@ |
| 1 | +package com.munjaon.server.server.sample; | |
| 2 | + | |
| 3 | +import java.util.ArrayList; | |
| 4 | +import java.util.List; | |
| 5 | +import java.util.concurrent.ExecutionException; | |
| 6 | +import java.util.concurrent.ExecutorService; | |
| 7 | +import java.util.concurrent.Executors; | |
| 8 | +import java.util.concurrent.Future; | |
| 9 | + | |
| 10 | +public class Main { | |
| 11 | + public static void main(String[] args) throws InterruptedException, ExecutionException { | |
| 12 | + ExecutorService e = Executors.newFixedThreadPool(5); | |
| 13 | + // 리턴 값이 필요 없는 경우 | |
| 14 | +// for (int i = 0; i < 5; i++) { | |
| 15 | +// e.execute(new MyRunnable(i * 20)); | |
| 16 | +// } | |
| 17 | + // 작업을 기다려야 할 경우 | |
| 18 | + //e.shutdown(); | |
| 19 | + | |
| 20 | + // 리턴 값이 필요한 경우 | |
| 21 | + List<Future<Integer>> l = new ArrayList<>(); | |
| 22 | + for (int i = 0; i < 5; i++) { | |
| 23 | + Future<Integer> f = e.submit(new MyCallable(i * 20)); | |
| 24 | + l.add(f); | |
| 25 | + } | |
| 26 | + | |
| 27 | + // 작업이 완료 되길 기다려야 할 경우 | |
| 28 | + int n = 0; | |
| 29 | + for (int i = 0; i < 5; i++) { | |
| 30 | + Future<Integer> f = l.get(i); | |
| 31 | + n += f.get(); | |
| 32 | + } | |
| 33 | + } | |
| 34 | +} |
+++ src/main/java/com/munjaon/server/server/sample/MyCallable.java
... | ... | @@ -0,0 +1,27 @@ |
| 1 | +package com.munjaon.server.server.sample; | |
| 2 | + | |
| 3 | +import java.util.concurrent.Callable; | |
| 4 | + | |
| 5 | +public class MyCallable implements Callable { | |
| 6 | + private final int s; | |
| 7 | + | |
| 8 | + public MyCallable(int s) { | |
| 9 | + this.s = s; | |
| 10 | + } | |
| 11 | + | |
| 12 | + @Override | |
| 13 | + public Integer call() throws Exception { | |
| 14 | + int in = 0; | |
| 15 | + for (int i = 0; i < 20; i++) { | |
| 16 | + System.out.println(s + " : MyCallable"); | |
| 17 | + System.out.println(i + s); | |
| 18 | + in += i + s; | |
| 19 | + try { | |
| 20 | + Thread.sleep(1000L); | |
| 21 | + } catch (InterruptedException e) { | |
| 22 | + throw new RuntimeException(e); | |
| 23 | + } | |
| 24 | + } | |
| 25 | + return in; | |
| 26 | + } | |
| 27 | +} |
+++ src/main/java/com/munjaon/server/server/sample/MyRunnable.java
... | ... | @@ -0,0 +1,20 @@ |
| 1 | +package com.munjaon.server.server.sample; | |
| 2 | + | |
| 3 | +public class MyRunnable implements Runnable { | |
| 4 | + private final int s; | |
| 5 | + public MyRunnable(int s) { | |
| 6 | + this.s = s; | |
| 7 | + } | |
| 8 | + | |
| 9 | + public void run() { | |
| 10 | + for (int i = 0; i < 20; i++) { | |
| 11 | + System.out.println(s + " : MyRunnable"); | |
| 12 | + System.out.println(i + s); | |
| 13 | + try { | |
| 14 | + Thread.sleep(1000L); | |
| 15 | + } catch (InterruptedException e) { | |
| 16 | + throw new RuntimeException(e); | |
| 17 | + } | |
| 18 | + } | |
| 19 | + } | |
| 20 | +} |
+++ src/main/java/com/munjaon/server/server/service/CollectBackServerService.java
... | ... | @@ -0,0 +1,403 @@ |
| 1 | +package com.munjaon.server.server.service; | |
| 2 | + | |
| 3 | +import com.munjaon.server.cache.dto.MemberDto; | |
| 4 | +import com.munjaon.server.cache.enums.CacheService; | |
| 5 | +import com.munjaon.server.cache.service.MemberService; | |
| 6 | +import com.munjaon.server.queue.dto.BasicMessageDto; | |
| 7 | +import com.munjaon.server.queue.enums.QueueTypeWorker; | |
| 8 | +import com.munjaon.server.server.dto.ConnectUserDto; | |
| 9 | +import com.munjaon.server.server.dto.HeaderDto; | |
| 10 | +import com.munjaon.server.server.packet.*; | |
| 11 | +import com.munjaon.server.util.LogUtil; | |
| 12 | +import lombok.Getter; | |
| 13 | +import org.json.simple.JSONObject; | |
| 14 | + | |
| 15 | +import java.io.IOException; | |
| 16 | +import java.net.InetSocketAddress; | |
| 17 | +import java.net.Socket; | |
| 18 | +import java.net.SocketAddress; | |
| 19 | +import java.nio.ByteBuffer; | |
| 20 | +import java.nio.channels.SelectionKey; | |
| 21 | +import java.nio.channels.Selector; | |
| 22 | +import java.nio.channels.ServerSocketChannel; | |
| 23 | +import java.nio.channels.SocketChannel; | |
| 24 | +import java.time.LocalDateTime; | |
| 25 | +import java.time.format.DateTimeFormatter; | |
| 26 | +import java.util.Iterator; | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | +import java.util.concurrent.ConcurrentHashMap; | |
| 30 | +import java.util.concurrent.ExecutorService; | |
| 31 | +import java.util.concurrent.Executors; | |
| 32 | + | |
| 33 | +public class CollectBackServerService extends Service { | |
| 34 | + private final InetSocketAddress listenAddress; | |
| 35 | + private CollectorThreadService threadService; | |
| 36 | + private Selector selector; | |
| 37 | + private final String serviceType; | |
| 38 | + | |
| 39 | + public CollectBackServerService(String serviceName, String serviceType, int port) { | |
| 40 | + super(serviceName); | |
| 41 | + this.listenAddress = new InetSocketAddress(port); | |
| 42 | + this.serviceType = serviceType; | |
| 43 | + } | |
| 44 | + @Override | |
| 45 | + public void checkReady() { | |
| 46 | + QueueTypeWorker worker = QueueTypeWorker.find(this.serviceType); | |
| 47 | + if (worker != null && worker.getQueueSize() > 0) { | |
| 48 | + this.IS_READY_YN = true; | |
| 49 | + } else { | |
| 50 | + this.IS_READY_YN = false; | |
| 51 | + } | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + public void initResources() { | |
| 56 | + try { | |
| 57 | + initCollectChannel(); | |
| 58 | + threadService = new CollectorThreadService(8, this.serviceType, logger); | |
| 59 | + } catch (IOException e) { | |
| 60 | + saveSystemLog(e); | |
| 61 | + throw new RuntimeException(e); | |
| 62 | + } | |
| 63 | + } | |
| 64 | + | |
| 65 | + private void initCollectChannel() throws IOException { | |
| 66 | + selector = Selector.open(); | |
| 67 | + /* 채널 생성 */ | |
| 68 | + ServerSocketChannel serverChannel = ServerSocketChannel.open(); | |
| 69 | + /* non-Blocking 설정 */ | |
| 70 | + serverChannel.configureBlocking(false); | |
| 71 | + /* 서버 ip, port 설정 */ | |
| 72 | + serverChannel.socket().bind(listenAddress); | |
| 73 | + /* 채널에 accept 대기 설정 */ | |
| 74 | + serverChannel.register(selector, SelectionKey.OP_ACCEPT); | |
| 75 | + } | |
| 76 | + | |
| 77 | + private void closeCollectChannel() throws IOException { | |
| 78 | + selector.close(); | |
| 79 | + } | |
| 80 | + | |
| 81 | + @Override | |
| 82 | + public void releaseResources() { | |
| 83 | + try { | |
| 84 | + closeCollectChannel(); | |
| 85 | + threadService.close(); | |
| 86 | + } catch (IOException e) { | |
| 87 | + saveSystemLog(e); | |
| 88 | + throw new RuntimeException(e); | |
| 89 | + } | |
| 90 | + } | |
| 91 | + | |
| 92 | + @Override | |
| 93 | + public void doService() { | |
| 94 | + while (isRun()) { | |
| 95 | + try { | |
| 96 | + execInterest(); | |
| 97 | + } catch (Exception e) { | |
| 98 | + throw new RuntimeException(e); | |
| 99 | + } | |
| 100 | + } | |
| 101 | + } | |
| 102 | + | |
| 103 | + private void execInterest() throws IOException { | |
| 104 | + if (selector.select(1000) == 0) { | |
| 105 | + return ; | |
| 106 | + } | |
| 107 | + Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); | |
| 108 | + while (keys.hasNext()) { | |
| 109 | + SelectionKey key = keys.next(); | |
| 110 | + if (key.isValid()) { | |
| 111 | + if (key.isAcceptable()) { // 접속일 경우.. | |
| 112 | + saveSystemLog("isAcceptable"); | |
| 113 | + threadService.submit(selector, key, 1); | |
| 114 | + } else if (key.isReadable()) { // 수신일 경우.. | |
| 115 | + saveSystemLog("isReadable"); | |
| 116 | + threadService.submit(selector, key, 2); | |
| 117 | + } else if (key.isWritable()) { // 발신일 경우.. | |
| 118 | + saveSystemLog("isWritable"); | |
| 119 | + threadService.submit(selector, key, 3); | |
| 120 | + } | |
| 121 | + } | |
| 122 | + /* 키 셋에서 제거. */ | |
| 123 | + keys.remove(); | |
| 124 | + } | |
| 125 | + } | |
| 126 | + | |
| 127 | + @Override | |
| 128 | + public JSONObject monitorService() { | |
| 129 | + return null; | |
| 130 | + } | |
| 131 | + | |
| 132 | + private static class CollectorThreadService { | |
| 133 | + @Getter | |
| 134 | + private String serviceType; | |
| 135 | + @Getter | |
| 136 | + private final int maxCore; | |
| 137 | + private final ExecutorService executor; | |
| 138 | + private final Map<String, ConnectUserDto> connectUserMap = new ConcurrentHashMap<>(); | |
| 139 | + private final LogUtil logger; | |
| 140 | + | |
| 141 | + public CollectorThreadService(String serviceType, LogUtil logger) { | |
| 142 | + this(Runtime.getRuntime().availableProcessors(), serviceType, logger); | |
| 143 | + } | |
| 144 | + | |
| 145 | + public CollectorThreadService(int maxCore, String serviceType, LogUtil logger) { | |
| 146 | + this.maxCore = maxCore; | |
| 147 | + this.executor = Executors.newFixedThreadPool(maxCore); | |
| 148 | + this.logger = logger; | |
| 149 | + } | |
| 150 | + | |
| 151 | + public void submit(Selector selector, SelectionKey key, int interestOps) { | |
| 152 | + executor.submit(() -> { | |
| 153 | + switch (interestOps) { | |
| 154 | + case 1 : accept(selector, key); break; | |
| 155 | + case 2 : read(selector, key); break; | |
| 156 | + case 3 : write(selector, key); break; | |
| 157 | + default : break; | |
| 158 | + } | |
| 159 | + }); | |
| 160 | + } | |
| 161 | + | |
| 162 | + private void accept(Selector selector, SelectionKey key) { | |
| 163 | + try { | |
| 164 | + /* 키 채널을 가져온다. */ | |
| 165 | + ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); | |
| 166 | + /* accept을 해서 Socket 채널을 가져온다. */ | |
| 167 | + SocketChannel channel = serverChannel.accept(); | |
| 168 | + channel.configureBlocking(false); | |
| 169 | + /* 소켓 취득 */ | |
| 170 | + Socket socket = channel.socket(); | |
| 171 | + SocketAddress remoteAddr = socket.getRemoteSocketAddress(); | |
| 172 | + saveSystemLog("accept : " + Thread.currentThread().getName()); | |
| 173 | + saveSystemLog("Connected to: " + remoteAddr); | |
| 174 | + // Socket 채널을 channel에 수신 등록한다 | |
| 175 | + channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).build()); | |
| 176 | + } catch (Exception e) { | |
| 177 | + throw new RuntimeException(e); | |
| 178 | + } | |
| 179 | + } | |
| 180 | + | |
| 181 | +// private void read(Selector selector, SelectionKey key) { | |
| 182 | +// try { | |
| 183 | +// saveSystemLog("read : " + Thread.currentThread().getName()); | |
| 184 | +// // 키 채널을 가져온다. | |
| 185 | +// SocketChannel channel = (SocketChannel) key.channel(); | |
| 186 | +// ConnectUserDto userDto = (ConnectUserDto) key.attachment(); | |
| 187 | +// | |
| 188 | +// int size = -1; | |
| 189 | +// | |
| 190 | +// ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH); | |
| 191 | +// ByteBuffer bindBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH); | |
| 192 | +// ByteBuffer linkBuffer = ByteBuffer.allocate(LinkCheck.LINK_CHECK_BODY_LENGTH); | |
| 193 | +// ByteBuffer msgBuffer = ByteBuffer.allocate(SmsMessage.DELIVER_SMS_BODY_LENGTH); | |
| 194 | +// try { | |
| 195 | +// size = channel.read(headBuffer); | |
| 196 | +// String command = Header.getCommand(headBuffer); | |
| 197 | +// System.out.println("command : " + command); | |
| 198 | +// if ("1".equals(command)) { | |
| 199 | +// size = channel.read(bindBuffer); | |
| 200 | +// } else if ("3".equals(command)) { | |
| 201 | +// size = channel.read(msgBuffer); | |
| 202 | +// } else if ("7".equals(command)) { | |
| 203 | +// size = channel.read(linkBuffer); | |
| 204 | +// } else { | |
| 205 | +// size = -1; | |
| 206 | +// } | |
| 207 | +// | |
| 208 | +// System.out.println("size : " + size); | |
| 209 | +// } catch (IOException e) {} | |
| 210 | +// if (size < 0) { | |
| 211 | +// expireConnectUser(key); | |
| 212 | +// } else if (size > 0) { | |
| 213 | +//// String command = Header.getCommand(buffer); | |
| 214 | +//// saveSystemLog("command : " + command); | |
| 215 | +//// switch (Integer.parseInt(command)) { | |
| 216 | +//// case 1 : recvBind(channel, buffer, userDto); break; | |
| 217 | +//// case 3 : recvDeliver(channel, buffer, userDto); break; | |
| 218 | +//// case 7 : recvLinkCheck(key); break; | |
| 219 | +//// default: expireConnectUser(key); break; | |
| 220 | +//// } | |
| 221 | +// } | |
| 222 | +// } catch (Exception e) { | |
| 223 | +// e.printStackTrace(); | |
| 224 | +// } | |
| 225 | +// System.out.println("read"); | |
| 226 | +// } | |
| 227 | + | |
| 228 | + private void read(Selector selector, SelectionKey key) { | |
| 229 | + try { | |
| 230 | + saveSystemLog("read : " + Thread.currentThread().getName()); | |
| 231 | + // 키 채널을 가져온다. | |
| 232 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 233 | + ConnectUserDto userDto = (ConnectUserDto) key.attachment(); | |
| 234 | + | |
| 235 | + int size = -1; | |
| 236 | + ByteBuffer buffer = ByteBuffer.allocate(256); | |
| 237 | + try { | |
| 238 | + size = channel.read(buffer); | |
| 239 | + } catch (IOException e) {} | |
| 240 | + if (size < 0) { | |
| 241 | + expireConnectUser(key); | |
| 242 | + } else if (size > 0) { | |
| 243 | + String command = Header.getCommand(buffer); | |
| 244 | + saveSystemLog("command : " + command); | |
| 245 | + switch (Integer.parseInt(command)) { | |
| 246 | + case 1 : recvBind(channel, buffer, userDto); break; | |
| 247 | + case 3 : recvDeliver(channel, buffer, userDto); break; | |
| 248 | + case 7 : recvLinkCheck(key); break; | |
| 249 | + default: expireConnectUser(key); break; | |
| 250 | + } | |
| 251 | + } | |
| 252 | + } catch (Exception e) { | |
| 253 | + e.printStackTrace(); | |
| 254 | + } | |
| 255 | + System.out.println("read"); | |
| 256 | + } | |
| 257 | + | |
| 258 | + private void recvBind(SocketChannel channel, ByteBuffer buffer, ConnectUserDto userDto) { | |
| 259 | + String resultCode = "00"; | |
| 260 | + try { | |
| 261 | + String id = Bind.getBindId(buffer); | |
| 262 | + String pwd = Bind.getBindPwd(buffer); | |
| 263 | + saveSystemLog("id : " + id); | |
| 264 | + saveSystemLog("pwd : " + pwd); | |
| 265 | + if (id == null || pwd == null) { | |
| 266 | + resultCode = "50"; | |
| 267 | + } else { | |
| 268 | + if (connectUserMap.containsKey(id)) { | |
| 269 | + resultCode = "60"; | |
| 270 | + } else { | |
| 271 | + MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService(); | |
| 272 | + MemberDto memberDto = null; | |
| 273 | + if (svc != null) { | |
| 274 | + memberDto = svc.get(id); | |
| 275 | + } | |
| 276 | + if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) { | |
| 277 | + resultCode = "20"; | |
| 278 | + } else { | |
| 279 | + userDto.setUserId(id); | |
| 280 | + userDto.setLogin(true); | |
| 281 | + userDto.setMemberDto(memberDto); | |
| 282 | + } | |
| 283 | + } | |
| 284 | + } | |
| 285 | + } catch (Exception e) { | |
| 286 | + resultCode = "10"; | |
| 287 | + e.printStackTrace(); | |
| 288 | + } | |
| 289 | + | |
| 290 | + try { | |
| 291 | + saveSystemLog("resultCode : " + resultCode); | |
| 292 | + channel.write(Bind.makeBindAckBuffer(resultCode)); | |
| 293 | + } catch (IOException e) { | |
| 294 | + e.printStackTrace(); | |
| 295 | + } | |
| 296 | + } | |
| 297 | + | |
| 298 | + private void recvDeliver(SocketChannel channel, ByteBuffer buffer, ConnectUserDto userDto) throws IOException { | |
| 299 | + BasicMessageDto messageDto = new BasicMessageDto(); | |
| 300 | + messageDto.setRouterSeq("40"); | |
| 301 | + messageDto.setServiceType("4"); | |
| 302 | + messageDto.setUserId(userDto.getUserId()); | |
| 303 | + messageDto.setRemoteIP(userDto.getRemoteIP()); | |
| 304 | + messageDto.setSendStatus("0"); | |
| 305 | + messageDto.setUserMsgID(CommonMessage.getMessageIdForDeliver(buffer)); | |
| 306 | + messageDto.setUserSender(CommonMessage.getSenderForDeliver(buffer)); | |
| 307 | + messageDto.setUserReceiver(CommonMessage.getReceiverForDeliver(buffer)); | |
| 308 | + messageDto.setReserveDt(CommonMessage.getReserveTimeForDeliver(buffer)); | |
| 309 | + messageDto.setRequestDt(CommonMessage.getRequestTimeForDeliver(buffer)); | |
| 310 | + messageDto.setUnitCost("10.4"); | |
| 311 | + messageDto.setUserMessage(SmsMessage.getMessageForDeliver(buffer)); | |
| 312 | + | |
| 313 | + QueueTypeWorker worker = QueueTypeWorker.find("SMS"); | |
| 314 | + if (worker != null) { | |
| 315 | + worker.pushQueue(messageDto); | |
| 316 | + channel.write(SmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus())); | |
| 317 | + } | |
| 318 | + } | |
| 319 | + private void recvLinkCheck(SelectionKey key) throws IOException { | |
| 320 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 321 | + channel.write(LinkCheck.makeLinkCheckAckBuffer()); | |
| 322 | + } | |
| 323 | + private void expireConnectUser(SelectionKey key) { | |
| 324 | + if (key == null || !key.isValid()) { | |
| 325 | + return; | |
| 326 | + } | |
| 327 | + try { | |
| 328 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 329 | + ConnectUserDto userDto = (ConnectUserDto) key.attachment(); | |
| 330 | + if (userDto != null && userDto.getUserId() != null) { | |
| 331 | + connectUserMap.remove(userDto.getUserId()); | |
| 332 | + key.attach(null); | |
| 333 | + } | |
| 334 | + // 소켓 채널 닫기 | |
| 335 | + channel.close(); | |
| 336 | + // 키 닫기 | |
| 337 | + key.cancel(); | |
| 338 | + } catch (IOException e) { | |
| 339 | + e.printStackTrace(); | |
| 340 | + } | |
| 341 | + } | |
| 342 | + private HeaderDto getHeader(SocketChannel channel) { | |
| 343 | + HeaderDto headerDto = HeaderDto.builder().build(); | |
| 344 | + int size = -1; | |
| 345 | + ByteBuffer buffer = ByteBuffer.allocate(Header.HEADER_LENGTH); | |
| 346 | + if (channel != null) { | |
| 347 | + try { | |
| 348 | + saveSystemLog("Key is valid : "); | |
| 349 | +// SocketChannel channel = (SocketChannel) key.channel(); | |
| 350 | + size = channel.read(buffer); | |
| 351 | + } catch (IOException e) {} | |
| 352 | + } | |
| 353 | + | |
| 354 | + if (size < 0) { | |
| 355 | + saveSystemLog("Is Error : "); | |
| 356 | + headerDto.setError(true); | |
| 357 | + } else { | |
| 358 | + saveSystemLog("version : " + Header.getVersion(buffer)); | |
| 359 | + saveSystemLog("Command : " + Header.getCommand(buffer)); | |
| 360 | + saveSystemLog("BodyLength : " + Header.getBodyLength(buffer)); | |
| 361 | + headerDto.setVersion(Header.getVersion(buffer)); | |
| 362 | + headerDto.setCommand(Integer.parseInt(Header.getCommand(buffer))); | |
| 363 | + headerDto.setBodyLength(Integer.parseInt(Header.getBodyLength(buffer))); | |
| 364 | + } | |
| 365 | + | |
| 366 | + saveSystemLog("READ HEADER : " + size); | |
| 367 | + | |
| 368 | + return headerDto; | |
| 369 | + } | |
| 370 | + | |
| 371 | + private void write(Selector selector, SelectionKey key) { | |
| 372 | + System.out.println("write"); | |
| 373 | + } | |
| 374 | + | |
| 375 | + private void saveSystemLog(Object obj) { | |
| 376 | + saveLog(obj, true); | |
| 377 | + } | |
| 378 | + | |
| 379 | + private void saveLog(Object obj) { | |
| 380 | + saveLog(obj, false); | |
| 381 | + } | |
| 382 | + | |
| 383 | + private void saveLog(Object obj, boolean isConsoleOutput) { | |
| 384 | + if (isConsoleOutput) { | |
| 385 | + System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{" + serviceType + "}} " + obj); | |
| 386 | + } | |
| 387 | + | |
| 388 | + if (logger == null) { | |
| 389 | + return; | |
| 390 | + } | |
| 391 | + | |
| 392 | + logger.log(obj); | |
| 393 | + } | |
| 394 | + | |
| 395 | + public void close() { | |
| 396 | + List<Runnable> unfinishedTasks = executor.shutdownNow(); | |
| 397 | + connectUserMap.clear(); | |
| 398 | + if (!unfinishedTasks.isEmpty()) { | |
| 399 | + saveSystemLog("Not all tasks finished before calling close: " + unfinishedTasks.size()); | |
| 400 | + } | |
| 401 | + } | |
| 402 | + } | |
| 403 | +} |
--- src/main/java/com/munjaon/server/server/service/CollectServerService.java
+++ src/main/java/com/munjaon/server/server/service/CollectServerService.java
... | ... | @@ -1,13 +1,9 @@ |
| 1 | 1 |
package com.munjaon.server.server.service; |
| 2 | 2 |
|
| 3 |
-import com.munjaon.server.cache.dto.MemberDto; |
|
| 4 |
-import com.munjaon.server.cache.enums.CacheService; |
|
| 5 |
-import com.munjaon.server.cache.service.MemberService; |
|
| 6 |
-import com.munjaon.server.queue.dto.BasicMessageDto; |
|
| 7 | 3 |
import com.munjaon.server.queue.enums.QueueTypeWorker; |
| 8 | 4 |
import com.munjaon.server.server.dto.ConnectUserDto; |
| 9 |
-import com.munjaon.server.server.dto.HeaderDto; |
|
| 10 |
-import com.munjaon.server.server.packet.*; |
|
| 5 |
+import com.munjaon.server.server.queue.CollectUserQueue; |
|
| 6 |
+import com.munjaon.server.server.task.CollectReadTask; |
|
| 11 | 7 |
import com.munjaon.server.util.LogUtil; |
| 12 | 8 |
import lombok.Getter; |
| 13 | 9 |
import org.json.simple.JSONObject; |
... | ... | @@ -16,31 +12,33 @@ |
| 16 | 12 |
import java.net.InetSocketAddress; |
| 17 | 13 |
import java.net.Socket; |
| 18 | 14 |
import java.net.SocketAddress; |
| 19 |
-import java.nio.ByteBuffer; |
|
| 20 | 15 |
import java.nio.channels.SelectionKey; |
| 21 | 16 |
import java.nio.channels.Selector; |
| 22 | 17 |
import java.nio.channels.ServerSocketChannel; |
| 23 | 18 |
import java.nio.channels.SocketChannel; |
| 24 | 19 |
import java.time.LocalDateTime; |
| 25 | 20 |
import java.time.format.DateTimeFormatter; |
| 21 |
+import java.util.ArrayList; |
|
| 26 | 22 |
import java.util.Iterator; |
| 27 | 23 |
import java.util.List; |
| 28 | 24 |
import java.util.Map; |
| 29 |
-import java.util.concurrent.ConcurrentHashMap; |
|
| 30 |
-import java.util.concurrent.ExecutorService; |
|
| 31 |
-import java.util.concurrent.Executors; |
|
| 25 |
+import java.util.concurrent.*; |
|
| 32 | 26 |
|
| 33 | 27 |
public class CollectServerService extends Service {
|
| 34 | 28 |
private final InetSocketAddress listenAddress; |
| 35 |
- private CollectorThreadService threadService; |
|
| 29 |
+ private CollectThreadService threadService; |
|
| 30 |
+ private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance(); |
|
| 36 | 31 |
private Selector selector; |
| 37 | 32 |
private final String serviceType; |
| 33 |
+ private int readMaxCore; |
|
| 38 | 34 |
|
| 39 | 35 |
public CollectServerService(String serviceName, String serviceType, int port) {
|
| 40 | 36 |
super(serviceName); |
| 37 |
+ this.readMaxCore = Integer.parseInt(getProp("READ_MAX_CORE").trim());
|
|
| 41 | 38 |
this.listenAddress = new InetSocketAddress(port); |
| 42 | 39 |
this.serviceType = serviceType; |
| 43 | 40 |
} |
| 41 |
+ |
|
| 44 | 42 |
@Override |
| 45 | 43 |
public void checkReady() {
|
| 46 | 44 |
QueueTypeWorker worker = QueueTypeWorker.find(this.serviceType); |
... | ... | @@ -55,7 +53,7 @@ |
| 55 | 53 |
public void initResources() {
|
| 56 | 54 |
try {
|
| 57 | 55 |
initCollectChannel(); |
| 58 |
- threadService = new CollectorThreadService(8, this.serviceType, logger); |
|
| 56 |
+ threadService = new CollectThreadService(readMaxCore, this.serviceType, logger); |
|
| 59 | 57 |
} catch (IOException e) {
|
| 60 | 58 |
saveSystemLog(e); |
| 61 | 59 |
throw new RuntimeException(e); |
... | ... | @@ -105,22 +103,78 @@ |
| 105 | 103 |
return ; |
| 106 | 104 |
} |
| 107 | 105 |
Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); |
| 106 |
+ List<Future<ConnectUserDto>> list = new ArrayList<>(); |
|
| 108 | 107 |
while (keys.hasNext()) {
|
| 109 | 108 |
SelectionKey key = keys.next(); |
| 109 |
+ /* 키 셋에서 제거. */ |
|
| 110 |
+ keys.remove(); |
|
| 111 |
+ |
|
| 110 | 112 |
if (key.isValid()) {
|
| 111 | 113 |
if (key.isAcceptable()) { // 접속일 경우..
|
| 112 | 114 |
saveSystemLog("isAcceptable");
|
| 113 |
- threadService.submit(selector, key, 1); |
|
| 115 |
+ accept(selector, key); |
|
| 114 | 116 |
} else if (key.isReadable()) { // 수신일 경우..
|
| 115 | 117 |
saveSystemLog("isReadable");
|
| 116 |
- threadService.submit(selector, key, 2); |
|
| 117 |
- } else if (key.isWritable()) { // 발신일 경우..
|
|
| 118 |
- saveSystemLog("isWritable");
|
|
| 119 |
- threadService.submit(selector, key, 3); |
|
| 118 |
+ Future<ConnectUserDto> future = threadService.submit(new CollectReadTask(selector, key, this.serviceType, logger)); |
|
| 119 |
+ list.add(future); |
|
| 120 |
+// threadService.submit(selector, key, 2); |
|
| 120 | 121 |
} |
| 122 |
+ } else {
|
|
| 123 |
+ expireConnectUser(key); |
|
| 121 | 124 |
} |
| 122 |
- /* 키 셋에서 제거. */ |
|
| 123 |
- keys.remove(); |
|
| 125 |
+ } |
|
| 126 |
+ for (Future<ConnectUserDto> future : list) {
|
|
| 127 |
+ ConnectUserDto connectUserDto = null; |
|
| 128 |
+ try {
|
|
| 129 |
+ connectUserDto = future.get(); |
|
| 130 |
+ } catch (InterruptedException e) {
|
|
| 131 |
+ } catch (ExecutionException e) {
|
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ if (connectUserDto == null) {
|
|
| 135 |
+ saveSystemLog("Future : " + future);
|
|
| 136 |
+ } else {
|
|
| 137 |
+ saveSystemLog("Future : " + connectUserDto.toString());
|
|
| 138 |
+ } |
|
| 139 |
+ } |
|
| 140 |
+ } |
|
| 141 |
+ |
|
| 142 |
+ private void accept(Selector selector, SelectionKey key) {
|
|
| 143 |
+ try {
|
|
| 144 |
+ /* 키 채널을 가져온다. */ |
|
| 145 |
+ ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); |
|
| 146 |
+ /* accept을 해서 Socket 채널을 가져온다. */ |
|
| 147 |
+ SocketChannel channel = serverChannel.accept(); |
|
| 148 |
+ channel.configureBlocking(false); |
|
| 149 |
+ /* 소켓 취득 */ |
|
| 150 |
+ Socket socket = channel.socket(); |
|
| 151 |
+ SocketAddress remoteAddr = socket.getRemoteSocketAddress(); |
|
| 152 |
+ saveSystemLog("Connected to: " + remoteAddr);
|
|
| 153 |
+ // Socket 채널을 channel에 수신 등록한다 |
|
| 154 |
+ channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().serviceType(this.serviceType).lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).build()); |
|
| 155 |
+ } catch (Exception e) {
|
|
| 156 |
+ throw new RuntimeException(e); |
|
| 157 |
+ } |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ private void expireConnectUser(SelectionKey key) {
|
|
| 161 |
+ if (key == null || !key.isValid()) {
|
|
| 162 |
+ return; |
|
| 163 |
+ } |
|
| 164 |
+ try {
|
|
| 165 |
+ SocketChannel channel = (SocketChannel) key.channel(); |
|
| 166 |
+ ConnectUserDto userDto = (ConnectUserDto) key.attachment(); |
|
| 167 |
+ if (userDto != null && userDto.getUserId() != null) {
|
|
| 168 |
+ collectUserQueue.removeUser(this.serviceType, userDto.getUserId()); |
|
| 169 |
+// connectUserMap.remove(userDto.getUserId()); |
|
| 170 |
+ key.attach(null); |
|
| 171 |
+ } |
|
| 172 |
+ // 소켓 채널 닫기 |
|
| 173 |
+ channel.close(); |
|
| 174 |
+ // 키 닫기 |
|
| 175 |
+ key.cancel(); |
|
| 176 |
+ } catch (IOException e) {
|
|
| 177 |
+ e.printStackTrace(); |
|
| 124 | 178 |
} |
| 125 | 179 |
} |
| 126 | 180 |
|
... | ... | @@ -129,7 +183,7 @@ |
| 129 | 183 |
return null; |
| 130 | 184 |
} |
| 131 | 185 |
|
| 132 |
- private static class CollectorThreadService {
|
|
| 186 |
+ private static class CollectThreadService {
|
|
| 133 | 187 |
@Getter |
| 134 | 188 |
private String serviceType; |
| 135 | 189 |
@Getter |
... | ... | @@ -138,238 +192,18 @@ |
| 138 | 192 |
private final Map<String, ConnectUserDto> connectUserMap = new ConcurrentHashMap<>(); |
| 139 | 193 |
private final LogUtil logger; |
| 140 | 194 |
|
| 141 |
- public CollectorThreadService(String serviceType, LogUtil logger) {
|
|
| 195 |
+ public CollectThreadService(String serviceType, LogUtil logger) {
|
|
| 142 | 196 |
this(Runtime.getRuntime().availableProcessors(), serviceType, logger); |
| 143 | 197 |
} |
| 144 | 198 |
|
| 145 |
- public CollectorThreadService(int maxCore, String serviceType, LogUtil logger) {
|
|
| 199 |
+ public CollectThreadService(int maxCore, String serviceType, LogUtil logger) {
|
|
| 146 | 200 |
this.maxCore = maxCore; |
| 147 | 201 |
this.executor = Executors.newFixedThreadPool(maxCore); |
| 148 | 202 |
this.logger = logger; |
| 149 | 203 |
} |
| 150 | 204 |
|
| 151 |
- public void submit(Selector selector, SelectionKey key, int interestOps) {
|
|
| 152 |
- executor.submit(() -> {
|
|
| 153 |
- switch (interestOps) {
|
|
| 154 |
- case 1 : accept(selector, key); break; |
|
| 155 |
- case 2 : read(selector, key); break; |
|
| 156 |
- case 3 : write(selector, key); break; |
|
| 157 |
- default : break; |
|
| 158 |
- } |
|
| 159 |
- }); |
|
| 160 |
- } |
|
| 161 |
- |
|
| 162 |
- private void accept(Selector selector, SelectionKey key) {
|
|
| 163 |
- try {
|
|
| 164 |
- /* 키 채널을 가져온다. */ |
|
| 165 |
- ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); |
|
| 166 |
- /* accept을 해서 Socket 채널을 가져온다. */ |
|
| 167 |
- SocketChannel channel = serverChannel.accept(); |
|
| 168 |
- channel.configureBlocking(false); |
|
| 169 |
- /* 소켓 취득 */ |
|
| 170 |
- Socket socket = channel.socket(); |
|
| 171 |
- SocketAddress remoteAddr = socket.getRemoteSocketAddress(); |
|
| 172 |
- saveSystemLog("accept : " + Thread.currentThread().getName());
|
|
| 173 |
- saveSystemLog("Connected to: " + remoteAddr);
|
|
| 174 |
- // Socket 채널을 channel에 수신 등록한다 |
|
| 175 |
- channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).build()); |
|
| 176 |
- } catch (Exception e) {
|
|
| 177 |
- throw new RuntimeException(e); |
|
| 178 |
- } |
|
| 179 |
- } |
|
| 180 |
- |
|
| 181 |
-// private void read(Selector selector, SelectionKey key) {
|
|
| 182 |
-// try {
|
|
| 183 |
-// saveSystemLog("read : " + Thread.currentThread().getName());
|
|
| 184 |
-// // 키 채널을 가져온다. |
|
| 185 |
-// SocketChannel channel = (SocketChannel) key.channel(); |
|
| 186 |
-// ConnectUserDto userDto = (ConnectUserDto) key.attachment(); |
|
| 187 |
-// |
|
| 188 |
-// int size = -1; |
|
| 189 |
-// |
|
| 190 |
-// ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH); |
|
| 191 |
-// ByteBuffer bindBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH); |
|
| 192 |
-// ByteBuffer linkBuffer = ByteBuffer.allocate(LinkCheck.LINK_CHECK_BODY_LENGTH); |
|
| 193 |
-// ByteBuffer msgBuffer = ByteBuffer.allocate(SmsMessage.DELIVER_SMS_BODY_LENGTH); |
|
| 194 |
-// try {
|
|
| 195 |
-// size = channel.read(headBuffer); |
|
| 196 |
-// String command = Header.getCommand(headBuffer); |
|
| 197 |
-// System.out.println("command : " + command);
|
|
| 198 |
-// if ("1".equals(command)) {
|
|
| 199 |
-// size = channel.read(bindBuffer); |
|
| 200 |
-// } else if ("3".equals(command)) {
|
|
| 201 |
-// size = channel.read(msgBuffer); |
|
| 202 |
-// } else if ("7".equals(command)) {
|
|
| 203 |
-// size = channel.read(linkBuffer); |
|
| 204 |
-// } else {
|
|
| 205 |
-// size = -1; |
|
| 206 |
-// } |
|
| 207 |
-// |
|
| 208 |
-// System.out.println("size : " + size);
|
|
| 209 |
-// } catch (IOException e) {}
|
|
| 210 |
-// if (size < 0) {
|
|
| 211 |
-// expireConnectUser(key); |
|
| 212 |
-// } else if (size > 0) {
|
|
| 213 |
-//// String command = Header.getCommand(buffer); |
|
| 214 |
-//// saveSystemLog("command : " + command);
|
|
| 215 |
-//// switch (Integer.parseInt(command)) {
|
|
| 216 |
-//// case 1 : recvBind(channel, buffer, userDto); break; |
|
| 217 |
-//// case 3 : recvDeliver(channel, buffer, userDto); break; |
|
| 218 |
-//// case 7 : recvLinkCheck(key); break; |
|
| 219 |
-//// default: expireConnectUser(key); break; |
|
| 220 |
-//// } |
|
| 221 |
-// } |
|
| 222 |
-// } catch (Exception e) {
|
|
| 223 |
-// e.printStackTrace(); |
|
| 224 |
-// } |
|
| 225 |
-// System.out.println("read");
|
|
| 226 |
-// } |
|
| 227 |
- |
|
| 228 |
- private void read(Selector selector, SelectionKey key) {
|
|
| 229 |
- try {
|
|
| 230 |
- saveSystemLog("read : " + Thread.currentThread().getName());
|
|
| 231 |
- // 키 채널을 가져온다. |
|
| 232 |
- SocketChannel channel = (SocketChannel) key.channel(); |
|
| 233 |
- ConnectUserDto userDto = (ConnectUserDto) key.attachment(); |
|
| 234 |
- |
|
| 235 |
- int size = -1; |
|
| 236 |
- ByteBuffer buffer = ByteBuffer.allocate(256); |
|
| 237 |
- try {
|
|
| 238 |
- size = channel.read(buffer); |
|
| 239 |
- } catch (IOException e) {}
|
|
| 240 |
- if (size < 0) {
|
|
| 241 |
- expireConnectUser(key); |
|
| 242 |
- } else if (size > 0) {
|
|
| 243 |
- String command = Header.getCommand(buffer); |
|
| 244 |
- saveSystemLog("command : " + command);
|
|
| 245 |
- switch (Integer.parseInt(command)) {
|
|
| 246 |
- case 1 : recvBind(channel, buffer, userDto); break; |
|
| 247 |
- case 3 : recvDeliver(channel, buffer, userDto); break; |
|
| 248 |
- case 7 : recvLinkCheck(key); break; |
|
| 249 |
- default: expireConnectUser(key); break; |
|
| 250 |
- } |
|
| 251 |
- } |
|
| 252 |
- } catch (Exception e) {
|
|
| 253 |
- e.printStackTrace(); |
|
| 254 |
- } |
|
| 255 |
- System.out.println("read");
|
|
| 256 |
- } |
|
| 257 |
- |
|
| 258 |
- private void recvBind(SocketChannel channel, ByteBuffer buffer, ConnectUserDto userDto) {
|
|
| 259 |
- String resultCode = "00"; |
|
| 260 |
- try {
|
|
| 261 |
- String id = Bind.getBindId(buffer); |
|
| 262 |
- String pwd = Bind.getBindPwd(buffer); |
|
| 263 |
- saveSystemLog("id : " + id);
|
|
| 264 |
- saveSystemLog("pwd : " + pwd);
|
|
| 265 |
- if (id == null || pwd == null) {
|
|
| 266 |
- resultCode = "50"; |
|
| 267 |
- } else {
|
|
| 268 |
- if (connectUserMap.containsKey(id)) {
|
|
| 269 |
- resultCode = "60"; |
|
| 270 |
- } else {
|
|
| 271 |
- MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService(); |
|
| 272 |
- MemberDto memberDto = null; |
|
| 273 |
- if (svc != null) {
|
|
| 274 |
- memberDto = svc.get(id); |
|
| 275 |
- } |
|
| 276 |
- if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) {
|
|
| 277 |
- resultCode = "20"; |
|
| 278 |
- } else {
|
|
| 279 |
- userDto.setUserId(id); |
|
| 280 |
- userDto.setLogin(true); |
|
| 281 |
- userDto.setMemberDto(memberDto); |
|
| 282 |
- } |
|
| 283 |
- } |
|
| 284 |
- } |
|
| 285 |
- } catch (Exception e) {
|
|
| 286 |
- resultCode = "10"; |
|
| 287 |
- e.printStackTrace(); |
|
| 288 |
- } |
|
| 289 |
- |
|
| 290 |
- try {
|
|
| 291 |
- saveSystemLog("resultCode : " + resultCode);
|
|
| 292 |
- channel.write(Bind.makeBindAckBuffer(resultCode)); |
|
| 293 |
- } catch (IOException e) {
|
|
| 294 |
- e.printStackTrace(); |
|
| 295 |
- } |
|
| 296 |
- } |
|
| 297 |
- |
|
| 298 |
- private void recvDeliver(SocketChannel channel, ByteBuffer buffer, ConnectUserDto userDto) throws IOException {
|
|
| 299 |
- BasicMessageDto messageDto = new BasicMessageDto(); |
|
| 300 |
- messageDto.setRouterSeq("40");
|
|
| 301 |
- messageDto.setServiceType("4");
|
|
| 302 |
- messageDto.setUserId(userDto.getUserId()); |
|
| 303 |
- messageDto.setRemoteIP(userDto.getRemoteIP()); |
|
| 304 |
- messageDto.setSendStatus("0");
|
|
| 305 |
- messageDto.setUserMsgID(CommonMessage.getMessageIdForDeliver(buffer)); |
|
| 306 |
- messageDto.setUserSender(CommonMessage.getSenderForDeliver(buffer)); |
|
| 307 |
- messageDto.setUserReceiver(CommonMessage.getReceiverForDeliver(buffer)); |
|
| 308 |
- messageDto.setReserveDt(CommonMessage.getReserveTimeForDeliver(buffer)); |
|
| 309 |
- messageDto.setRequestDt(CommonMessage.getRequestTimeForDeliver(buffer)); |
|
| 310 |
- messageDto.setUnitCost("10.4");
|
|
| 311 |
- messageDto.setUserMessage(SmsMessage.getMessageForDeliver(buffer)); |
|
| 312 |
- |
|
| 313 |
- QueueTypeWorker worker = QueueTypeWorker.find("SMS");
|
|
| 314 |
- if (worker != null) {
|
|
| 315 |
- worker.pushQueue(messageDto); |
|
| 316 |
- channel.write(SmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus())); |
|
| 317 |
- } |
|
| 318 |
- } |
|
| 319 |
- private void recvLinkCheck(SelectionKey key) throws IOException {
|
|
| 320 |
- SocketChannel channel = (SocketChannel) key.channel(); |
|
| 321 |
- channel.write(LinkCheck.makeLinkCheckAckBuffer()); |
|
| 322 |
- } |
|
| 323 |
- private void expireConnectUser(SelectionKey key) {
|
|
| 324 |
- if (key == null || !key.isValid()) {
|
|
| 325 |
- return; |
|
| 326 |
- } |
|
| 327 |
- try {
|
|
| 328 |
- SocketChannel channel = (SocketChannel) key.channel(); |
|
| 329 |
- ConnectUserDto userDto = (ConnectUserDto) key.attachment(); |
|
| 330 |
- if (userDto != null && userDto.getUserId() != null) {
|
|
| 331 |
- connectUserMap.remove(userDto.getUserId()); |
|
| 332 |
- key.attach(null); |
|
| 333 |
- } |
|
| 334 |
- // 소켓 채널 닫기 |
|
| 335 |
- channel.close(); |
|
| 336 |
- // 키 닫기 |
|
| 337 |
- key.cancel(); |
|
| 338 |
- } catch (IOException e) {
|
|
| 339 |
- e.printStackTrace(); |
|
| 340 |
- } |
|
| 341 |
- } |
|
| 342 |
- private HeaderDto getHeader(SocketChannel channel) {
|
|
| 343 |
- HeaderDto headerDto = HeaderDto.builder().build(); |
|
| 344 |
- int size = -1; |
|
| 345 |
- ByteBuffer buffer = ByteBuffer.allocate(Header.HEADER_LENGTH); |
|
| 346 |
- if (channel != null) {
|
|
| 347 |
- try {
|
|
| 348 |
- saveSystemLog("Key is valid : ");
|
|
| 349 |
-// SocketChannel channel = (SocketChannel) key.channel(); |
|
| 350 |
- size = channel.read(buffer); |
|
| 351 |
- } catch (IOException e) {}
|
|
| 352 |
- } |
|
| 353 |
- |
|
| 354 |
- if (size < 0) {
|
|
| 355 |
- saveSystemLog("Is Error : ");
|
|
| 356 |
- headerDto.setError(true); |
|
| 357 |
- } else {
|
|
| 358 |
- saveSystemLog("version : " + Header.getVersion(buffer));
|
|
| 359 |
- saveSystemLog("Command : " + Header.getCommand(buffer));
|
|
| 360 |
- saveSystemLog("BodyLength : " + Header.getBodyLength(buffer));
|
|
| 361 |
- headerDto.setVersion(Header.getVersion(buffer)); |
|
| 362 |
- headerDto.setCommand(Integer.parseInt(Header.getCommand(buffer))); |
|
| 363 |
- headerDto.setBodyLength(Integer.parseInt(Header.getBodyLength(buffer))); |
|
| 364 |
- } |
|
| 365 |
- |
|
| 366 |
- saveSystemLog("READ HEADER : " + size);
|
|
| 367 |
- |
|
| 368 |
- return headerDto; |
|
| 369 |
- } |
|
| 370 |
- |
|
| 371 |
- private void write(Selector selector, SelectionKey key) {
|
|
| 372 |
- System.out.println("write");
|
|
| 205 |
+ public Future<ConnectUserDto> submit(CollectReadTask collectReadTask) {
|
|
| 206 |
+ return executor.submit(collectReadTask); |
|
| 373 | 207 |
} |
| 374 | 208 |
|
| 375 | 209 |
private void saveSystemLog(Object obj) {
|
+++ src/main/java/com/munjaon/server/server/service/ReportQueueServerService.java
... | ... | @@ -0,0 +1,116 @@ |
| 1 | +package com.munjaon.server.server.service; | |
| 2 | + | |
| 3 | +import com.munjaon.server.server.dto.ReportUserDto; | |
| 4 | +import com.munjaon.server.server.queue.ReportUserQueue; | |
| 5 | +import com.munjaon.server.server.task.ReportQueueTask; | |
| 6 | +import com.munjaon.server.util.LogUtil; | |
| 7 | +import lombok.Getter; | |
| 8 | +import org.json.simple.JSONObject; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | +import java.time.format.DateTimeFormatter; | |
| 12 | +import java.util.List; | |
| 13 | +import java.util.concurrent.ExecutorService; | |
| 14 | +import java.util.concurrent.Executors; | |
| 15 | + | |
| 16 | +public class ReportQueueServerService extends Service { | |
| 17 | + private final ReportUserQueue reportUserQueue = ReportUserQueue.getInstance(); | |
| 18 | + private QueueThreadService threadService; | |
| 19 | + private int queueMaxCore; | |
| 20 | + | |
| 21 | + public ReportQueueServerService(String serviceName) { | |
| 22 | + super(serviceName); | |
| 23 | + this.queueMaxCore = Integer.parseInt(getProp("QUEUE_MAX_CORE").trim()); | |
| 24 | + } | |
| 25 | + | |
| 26 | + @Override | |
| 27 | + public void checkReady() { | |
| 28 | + this.IS_READY_YN = true; | |
| 29 | + } | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public void initResources() { | |
| 33 | + threadService = new QueueThreadService(queueMaxCore, logger); | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Override | |
| 37 | + public void releaseResources() { | |
| 38 | + threadService.close(); | |
| 39 | + } | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + public void doService() { | |
| 43 | + while (isRun()) { | |
| 44 | + try { | |
| 45 | + doQueueService(); | |
| 46 | + Thread.sleep(100); | |
| 47 | + } catch (Exception e) { | |
| 48 | + throw new RuntimeException(e); | |
| 49 | + } | |
| 50 | + } | |
| 51 | + } | |
| 52 | + | |
| 53 | + private void doQueueService() { | |
| 54 | + List<ReportUserDto> reportUserList = reportUserQueue.getUsers(); | |
| 55 | + if (reportUserList == null || reportUserList.size() == 0) { | |
| 56 | + return; | |
| 57 | + } | |
| 58 | + | |
| 59 | + for (ReportUserDto reportUserDto : reportUserList) { | |
| 60 | + threadService.execute(new ReportQueueTask(reportUserDto, logger)); | |
| 61 | + } | |
| 62 | + } | |
| 63 | + | |
| 64 | + @Override | |
| 65 | + public JSONObject monitorService() { | |
| 66 | + return null; | |
| 67 | + } | |
| 68 | + | |
| 69 | + private static class QueueThreadService { | |
| 70 | + @Getter | |
| 71 | + private final int maxCore; | |
| 72 | + private final ExecutorService executor; | |
| 73 | + private final LogUtil logger; | |
| 74 | + | |
| 75 | + public QueueThreadService(LogUtil logger) { | |
| 76 | + this(Runtime.getRuntime().availableProcessors(), logger); | |
| 77 | + } | |
| 78 | + | |
| 79 | + public QueueThreadService(int maxCore, LogUtil logger) { | |
| 80 | + this.maxCore = maxCore; | |
| 81 | + this.executor = Executors.newFixedThreadPool(maxCore); | |
| 82 | + this.logger = logger; | |
| 83 | + } | |
| 84 | + | |
| 85 | + public void execute(ReportQueueTask runnable) { | |
| 86 | + executor.execute(runnable); | |
| 87 | + } | |
| 88 | + | |
| 89 | + private void saveSystemLog(Object obj) { | |
| 90 | + saveLog(obj, true); | |
| 91 | + } | |
| 92 | + | |
| 93 | + private void saveLog(Object obj) { | |
| 94 | + saveLog(obj, false); | |
| 95 | + } | |
| 96 | + | |
| 97 | + private void saveLog(Object obj, boolean isConsoleOutput) { | |
| 98 | + if (isConsoleOutput) { | |
| 99 | + System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{QueueThreadService}} " + obj); | |
| 100 | + } | |
| 101 | + | |
| 102 | + if (logger == null) { | |
| 103 | + return; | |
| 104 | + } | |
| 105 | + | |
| 106 | + logger.log(obj); | |
| 107 | + } | |
| 108 | + | |
| 109 | + public void close() { | |
| 110 | + List<Runnable> unfinishedTasks = executor.shutdownNow(); | |
| 111 | + if (!unfinishedTasks.isEmpty()) { | |
| 112 | + saveSystemLog("Not all tasks finished before calling close: " + unfinishedTasks.size()); | |
| 113 | + } | |
| 114 | + } | |
| 115 | + } | |
| 116 | +} |
--- src/main/java/com/munjaon/server/server/service/ReportServerService.java
+++ src/main/java/com/munjaon/server/server/service/ReportServerService.java
... | ... | @@ -1,37 +1,58 @@ |
| 1 | 1 |
package com.munjaon.server.server.service; |
| 2 | 2 |
|
| 3 |
+import com.munjaon.server.queue.pool.ReportQueue; |
|
| 3 | 4 |
import com.munjaon.server.server.dto.ConnectUserDto; |
| 5 |
+import com.munjaon.server.server.dto.ReportDto; |
|
| 6 |
+import com.munjaon.server.server.dto.ReportUserDto; |
|
| 7 |
+import com.munjaon.server.server.packet.Header; |
|
| 8 |
+import com.munjaon.server.server.packet.LinkCheck; |
|
| 9 |
+import com.munjaon.server.server.packet.Packet; |
|
| 10 |
+import com.munjaon.server.server.packet.Report; |
|
| 11 |
+import com.munjaon.server.server.queue.ReportUserQueue; |
|
| 12 |
+import com.munjaon.server.server.task.ReportReadTask; |
|
| 4 | 13 |
import com.munjaon.server.util.LogUtil; |
| 5 | 14 |
import lombok.Getter; |
| 6 | 15 |
import org.json.simple.JSONObject; |
| 7 | 16 |
|
| 17 |
+import java.io.File; |
|
| 8 | 18 |
import java.io.IOException; |
| 9 | 19 |
import java.net.InetSocketAddress; |
| 10 | 20 |
import java.net.Socket; |
| 11 | 21 |
import java.net.SocketAddress; |
| 22 |
+import java.nio.ByteBuffer; |
|
| 12 | 23 |
import java.nio.channels.SelectionKey; |
| 13 | 24 |
import java.nio.channels.Selector; |
| 14 | 25 |
import java.nio.channels.ServerSocketChannel; |
| 15 | 26 |
import java.nio.channels.SocketChannel; |
| 16 | 27 |
import java.time.LocalDateTime; |
| 17 | 28 |
import java.time.format.DateTimeFormatter; |
| 29 |
+import java.util.ArrayList; |
|
| 18 | 30 |
import java.util.Iterator; |
| 19 | 31 |
import java.util.List; |
| 20 |
-import java.util.Map; |
|
| 21 |
-import java.util.concurrent.ConcurrentHashMap; |
|
| 32 |
+import java.util.concurrent.ExecutionException; |
|
| 22 | 33 |
import java.util.concurrent.ExecutorService; |
| 23 | 34 |
import java.util.concurrent.Executors; |
| 35 |
+import java.util.concurrent.Future; |
|
| 24 | 36 |
|
| 25 | 37 |
public class ReportServerService extends Service {
|
| 26 | 38 |
private final InetSocketAddress listenAddress; |
| 27 |
- private final Map<String, ConnectUserDto> connectUserMap = new ConcurrentHashMap<>(); |
|
| 28 |
- private ReporterThreadService threadService; |
|
| 39 |
+ private ReadThreadService threadService; |
|
| 40 |
+ private final ReportUserQueue reportUserQueue = ReportUserQueue.getInstance(); |
|
| 29 | 41 |
private Selector selector; |
| 42 |
+ private int readMaxCore; |
|
| 43 |
+ private int queueMaxCore; |
|
| 44 |
+// private long CHECKED_INTEREST_TIME = System.currentTimeMillis(); |
|
| 45 |
+// private final long LIMIT_CYCLE_TIME = 3000; |
|
| 46 |
+ |
|
| 47 |
+// private final ByteBuffer reportBuffer = ByteBuffer.allocateDirect(Header.HEADER_LENGTH + Report.REPORT_BODY_LENGTH); |
|
| 30 | 48 |
|
| 31 | 49 |
public ReportServerService(String serviceName, int port) {
|
| 32 | 50 |
super(serviceName); |
| 51 |
+ this.readMaxCore = Integer.parseInt(getProp("READ_MAX_CORE").trim());
|
|
| 52 |
+ this.queueMaxCore = Integer.parseInt(getProp("QUEUE_MAX_CORE").trim());
|
|
| 33 | 53 |
this.listenAddress = new InetSocketAddress(port); |
| 34 | 54 |
} |
| 55 |
+ |
|
| 35 | 56 |
@Override |
| 36 | 57 |
public void checkReady() {
|
| 37 | 58 |
this.IS_READY_YN = true; |
... | ... | @@ -41,7 +62,7 @@ |
| 41 | 62 |
public void initResources() {
|
| 42 | 63 |
try {
|
| 43 | 64 |
initReportChannel(); |
| 44 |
- threadService = new ReporterThreadService(8, logger); |
|
| 65 |
+ threadService = new ReadThreadService(8, logger); |
|
| 45 | 66 |
} catch (IOException e) {
|
| 46 | 67 |
saveSystemLog(e); |
| 47 | 68 |
throw new RuntimeException(e); |
... | ... | @@ -78,6 +99,67 @@ |
| 78 | 99 |
@Override |
| 79 | 100 |
public void doService() {
|
| 80 | 101 |
|
| 102 |
+ while (isRun()) {
|
|
| 103 |
+ try {
|
|
| 104 |
+ execInterest(); |
|
| 105 |
+ checkInterest(); |
|
| 106 |
+ } catch (Exception e) {
|
|
| 107 |
+ saveSystemLog(e.toString()); |
|
| 108 |
+ } |
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ private void checkInterest() throws IOException, InterruptedException {
|
|
| 113 |
+// if (System.currentTimeMillis() - CHECKED_INTEREST_TIME < LIMIT_CYCLE_TIME) {
|
|
| 114 |
+// return; |
|
| 115 |
+// } |
|
| 116 |
+// /* 체크시간 업데이트 */ |
|
| 117 |
+// CHECKED_INTEREST_TIME = System.currentTimeMillis(); |
|
| 118 |
+ |
|
| 119 |
+ Iterator<SelectionKey> keys = selector.keys().iterator(); |
|
| 120 |
+ while (keys.hasNext()) {
|
|
| 121 |
+ SelectionKey key = keys.next(); |
|
| 122 |
+ if (key.isValid()) {
|
|
| 123 |
+ ReportUserDto reportUserDto = (ReportUserDto) key.attachment(); |
|
| 124 |
+ if (reportUserDto == null) {
|
|
| 125 |
+ continue; |
|
| 126 |
+ } |
|
| 127 |
+ SocketChannel channel = (SocketChannel) key.channel(); // 키 채널을 가져온다. |
|
| 128 |
+ if (reportUserDto.isAlive() == 1) {
|
|
| 129 |
+ if (reportUserDto.getUserId() != null) {
|
|
| 130 |
+ reportUserQueue.removeUser(reportUserDto.getUserId()); |
|
| 131 |
+ } |
|
| 132 |
+ Socket socket = channel.socket(); // 소켓 취득 |
|
| 133 |
+ channel.close(); // 소켓 채널 닫기 |
|
| 134 |
+ socket.close(); // 소켓 닫기 |
|
| 135 |
+ key.attach(null); // 키 닫기 |
|
| 136 |
+ key.cancel(); |
|
| 137 |
+ } else if (reportUserDto.isAlive() == 2) {
|
|
| 138 |
+ channel.write(LinkCheck.makeLinkCheckBuffer()); |
|
| 139 |
+ } else {
|
|
| 140 |
+ if (reportUserDto.isLogin()) {
|
|
| 141 |
+ ReportQueue reportQueue = reportUserDto.getReportQueue(); |
|
| 142 |
+ try {
|
|
| 143 |
+ ReportDto reportDto = reportQueue.popReportFromQueue(); |
|
| 144 |
+ if (reportDto == null) {
|
|
| 145 |
+ saveSystemLog("reportQueue.popReportFromQueue() : null");
|
|
| 146 |
+ continue; |
|
| 147 |
+ } |
|
| 148 |
+ saveSystemLog("reportQueue.popReportFromQueue() : " + reportDto.toString());
|
|
| 149 |
+ ByteBuffer reportBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Report.REPORT_BODY_LENGTH); |
|
| 150 |
+ Packet.setDefaultByte(reportBuffer); |
|
| 151 |
+ Header.putHeader(reportBuffer, Header.COMMAND_REPORT, Report.REPORT_BODY_LENGTH); |
|
| 152 |
+ Report.putReport(reportBuffer, reportDto); |
|
| 153 |
+ channel.write(reportBuffer); |
|
| 154 |
+ } catch (Exception e) {
|
|
| 155 |
+ e.printStackTrace(); |
|
| 156 |
+ } |
|
| 157 |
+ } |
|
| 158 |
+ } |
|
| 159 |
+ } else {
|
|
| 160 |
+ expireConnectUser(key); |
|
| 161 |
+ } |
|
| 162 |
+ } |
|
| 81 | 163 |
} |
| 82 | 164 |
|
| 83 | 165 |
private void execInterest() throws IOException {
|
... | ... | @@ -85,19 +167,39 @@ |
| 85 | 167 |
return ; |
| 86 | 168 |
} |
| 87 | 169 |
Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); |
| 170 |
+ List<Future<ReportUserDto>> list = new ArrayList<>(); |
|
| 88 | 171 |
while (keys.hasNext()) {
|
| 89 | 172 |
SelectionKey key = keys.next(); |
| 173 |
+ /* 키 셋에서 제거. */ |
|
| 174 |
+ keys.remove(); |
|
| 175 |
+ |
|
| 90 | 176 |
if (key.isValid()) {
|
| 91 | 177 |
if (key.isAcceptable()) { // 접속일 경우..
|
| 92 | 178 |
saveSystemLog("isAcceptable");
|
| 93 | 179 |
accept(selector, key); |
| 94 | 180 |
} else if (key.isReadable()) { // 수신일 경우..
|
| 95 | 181 |
saveSystemLog("isReadable");
|
| 182 |
+ Future<ReportUserDto> future = threadService.submit(new ReportReadTask(selector, key, logger)); |
|
| 183 |
+ list.add(future); |
|
| 96 | 184 |
// threadService.submit(selector, key, 2); |
| 97 | 185 |
} |
| 186 |
+ } else {
|
|
| 187 |
+ expireConnectUser(key); |
|
| 98 | 188 |
} |
| 99 |
- /* 키 셋에서 제거. */ |
|
| 100 |
- keys.remove(); |
|
| 189 |
+ } |
|
| 190 |
+ for (Future<ReportUserDto> future : list) {
|
|
| 191 |
+ ReportUserDto reportUserDto = null; |
|
| 192 |
+ try {
|
|
| 193 |
+ reportUserDto = future.get(); |
|
| 194 |
+ } catch (InterruptedException e) {
|
|
| 195 |
+ } catch (ExecutionException e) {
|
|
| 196 |
+ } |
|
| 197 |
+ |
|
| 198 |
+ if (reportUserDto == null) {
|
|
| 199 |
+ saveSystemLog("Future : " + future);
|
|
| 200 |
+ } else {
|
|
| 201 |
+ saveSystemLog("Future : " + reportUserDto.toString());
|
|
| 202 |
+ } |
|
| 101 | 203 |
} |
| 102 | 204 |
} |
| 103 | 205 |
|
... | ... | @@ -113,41 +215,11 @@ |
| 113 | 215 |
SocketAddress remoteAddr = socket.getRemoteSocketAddress(); |
| 114 | 216 |
saveSystemLog("Connected to: " + remoteAddr);
|
| 115 | 217 |
// Socket 채널을 channel에 수신 등록한다 |
| 116 |
- channel.register(selector, SelectionKey.OP_READ, ConnectUserDto.builder().lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).build()); |
|
| 218 |
+ channel.register(selector, SelectionKey.OP_READ, ReportUserDto.builder().lastTrafficTime(System.currentTimeMillis()).remoteIP(remoteAddr.toString()).queuePath(System.getProperty("ROOTPATH") + File.separator + getProp("QUEUE_PATH")).build());
|
|
| 117 | 219 |
} catch (Exception e) {
|
| 118 | 220 |
throw new RuntimeException(e); |
| 119 | 221 |
} |
| 120 | 222 |
} |
| 121 |
- |
|
| 122 |
-// private void read(Selector selector, SelectionKey key) {
|
|
| 123 |
-// try {
|
|
| 124 |
-// saveSystemLog("read : " + Thread.currentThread().getName());
|
|
| 125 |
-// // 키 채널을 가져온다. |
|
| 126 |
-// SocketChannel channel = (SocketChannel) key.channel(); |
|
| 127 |
-// ConnectUserDto userDto = (ConnectUserDto) key.attachment(); |
|
| 128 |
-// |
|
| 129 |
-// int size = -1; |
|
| 130 |
-// ByteBuffer buffer = ByteBuffer.allocate(256); |
|
| 131 |
-// try {
|
|
| 132 |
-// size = channel.read(buffer); |
|
| 133 |
-// } catch (IOException e) {}
|
|
| 134 |
-// if (size < 0) {
|
|
| 135 |
-// expireConnectUser(key); |
|
| 136 |
-// } else if (size > 0) {
|
|
| 137 |
-// String command = Header.getCommand(buffer); |
|
| 138 |
-// saveSystemLog("command : " + command);
|
|
| 139 |
-// switch (Integer.parseInt(command)) {
|
|
| 140 |
-// case 1 : recvBind(channel, buffer, userDto); break; |
|
| 141 |
-// case 6 : recvDeliver(channel, buffer, userDto); break; |
|
| 142 |
-// case 8 : recvLinkCheck(key); break; |
|
| 143 |
-// default: expireConnectUser(key); break; |
|
| 144 |
-// } |
|
| 145 |
-// } |
|
| 146 |
-// } catch (Exception e) {
|
|
| 147 |
-// e.printStackTrace(); |
|
| 148 |
-// } |
|
| 149 |
-// System.out.println("read");
|
|
| 150 |
-// } |
|
| 151 | 223 |
|
| 152 | 224 |
private void expireConnectUser(SelectionKey key) {
|
| 153 | 225 |
if (key == null || !key.isValid()) {
|
... | ... | @@ -157,7 +229,8 @@ |
| 157 | 229 |
SocketChannel channel = (SocketChannel) key.channel(); |
| 158 | 230 |
ConnectUserDto userDto = (ConnectUserDto) key.attachment(); |
| 159 | 231 |
if (userDto != null && userDto.getUserId() != null) {
|
| 160 |
- connectUserMap.remove(userDto.getUserId()); |
|
| 232 |
+ reportUserQueue.removeUser(userDto.getUserId()); |
|
| 233 |
+// connectUserMap.remove(userDto.getUserId()); |
|
| 161 | 234 |
key.attach(null); |
| 162 | 235 |
} |
| 163 | 236 |
// 소켓 채널 닫기 |
... | ... | @@ -174,27 +247,26 @@ |
| 174 | 247 |
return null; |
| 175 | 248 |
} |
| 176 | 249 |
|
| 177 |
- private static class ReporterThreadService {
|
|
| 250 |
+ private static class ReadThreadService {
|
|
| 178 | 251 |
@Getter |
| 179 | 252 |
private final int maxCore; |
| 180 | 253 |
private final ExecutorService executor; |
| 181 |
- private final Map<String, ConnectUserDto> connectUserMap = new ConcurrentHashMap<>(); |
|
| 254 |
+ private ReportUserQueue reportUserQueue = ReportUserQueue.getInstance(); |
|
| 182 | 255 |
private final LogUtil logger; |
| 183 | 256 |
|
| 184 |
- public ReporterThreadService(LogUtil logger) {
|
|
| 257 |
+ public ReadThreadService(LogUtil logger) {
|
|
| 185 | 258 |
this(Runtime.getRuntime().availableProcessors(), logger); |
| 186 | 259 |
} |
| 187 | 260 |
|
| 188 |
- public ReporterThreadService(int maxCore, LogUtil logger) {
|
|
| 261 |
+ public ReadThreadService(int maxCore, LogUtil logger) {
|
|
| 189 | 262 |
this.maxCore = maxCore; |
| 190 | 263 |
this.executor = Executors.newFixedThreadPool(maxCore); |
| 191 | 264 |
this.logger = logger; |
| 192 | 265 |
} |
| 193 | 266 |
|
| 194 |
- private void write(Selector selector, SelectionKey key) {
|
|
| 195 |
- System.out.println("write");
|
|
| 267 |
+ public Future<ReportUserDto> submit(ReportReadTask reportReadTask) {
|
|
| 268 |
+ return executor.submit(reportReadTask); |
|
| 196 | 269 |
} |
| 197 |
- |
|
| 198 | 270 |
private void saveSystemLog(Object obj) {
|
| 199 | 271 |
saveLog(obj, true); |
| 200 | 272 |
} |
... | ... | @@ -217,7 +289,6 @@ |
| 217 | 289 |
|
| 218 | 290 |
public void close() {
|
| 219 | 291 |
List<Runnable> unfinishedTasks = executor.shutdownNow(); |
| 220 |
- connectUserMap.clear(); |
|
| 221 | 292 |
if (!unfinishedTasks.isEmpty()) {
|
| 222 | 293 |
saveSystemLog("Not all tasks finished before calling close: " + unfinishedTasks.size());
|
| 223 | 294 |
} |
+++ src/main/java/com/munjaon/server/server/task/CollectReadTask.java
... | ... | @@ -0,0 +1,269 @@ |
| 1 | +package com.munjaon.server.server.task; | |
| 2 | + | |
| 3 | +import com.munjaon.server.cache.dto.MemberDto; | |
| 4 | +import com.munjaon.server.cache.enums.CacheService; | |
| 5 | +import com.munjaon.server.cache.service.MemberService; | |
| 6 | +import com.munjaon.server.queue.dto.BasicMessageDto; | |
| 7 | +import com.munjaon.server.queue.enums.QueueTypeWorker; | |
| 8 | +import com.munjaon.server.server.dto.ConnectUserDto; | |
| 9 | +import com.munjaon.server.server.packet.*; | |
| 10 | +import com.munjaon.server.server.queue.CollectUserQueue; | |
| 11 | +import com.munjaon.server.util.LogUtil; | |
| 12 | + | |
| 13 | +import java.io.IOException; | |
| 14 | +import java.nio.ByteBuffer; | |
| 15 | +import java.nio.channels.SelectionKey; | |
| 16 | +import java.nio.channels.Selector; | |
| 17 | +import java.nio.channels.SocketChannel; | |
| 18 | +import java.text.SimpleDateFormat; | |
| 19 | +import java.time.LocalDateTime; | |
| 20 | +import java.time.format.DateTimeFormatter; | |
| 21 | +import java.util.concurrent.Callable; | |
| 22 | + | |
| 23 | +public class CollectReadTask implements Callable<ConnectUserDto> { | |
| 24 | + public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]"); | |
| 25 | + public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]"; | |
| 26 | + | |
| 27 | + private Selector selector; | |
| 28 | + private SelectionKey key; | |
| 29 | + private CollectUserQueue collectUserQueue = CollectUserQueue.getInstance(); | |
| 30 | + private ConnectUserDto connectUserDto; | |
| 31 | + private String serviceType; | |
| 32 | + private final LogUtil logger; | |
| 33 | + | |
| 34 | + public CollectReadTask(Selector selector, SelectionKey key, String serviceType, LogUtil logger) { | |
| 35 | + this.selector = selector; | |
| 36 | + this.key = key; | |
| 37 | + this.connectUserDto = (ConnectUserDto) key.attachment(); | |
| 38 | + this.serviceType = serviceType; | |
| 39 | + this.logger = logger; | |
| 40 | + } | |
| 41 | + | |
| 42 | + @Override | |
| 43 | + public ConnectUserDto call() throws Exception { | |
| 44 | + int size = -1; | |
| 45 | + try { | |
| 46 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 47 | + /* 1. Head 읽기 */ | |
| 48 | + ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH); | |
| 49 | + try { | |
| 50 | + size = channel.read(headBuffer); | |
| 51 | + } catch (IOException e) {} | |
| 52 | + /* 2. Body 읽기 */ | |
| 53 | + if (size > 0) { | |
| 54 | +// Packet.printBuffer(headBuffer); | |
| 55 | + String command = Header.getCommand(headBuffer); | |
| 56 | + switch (Integer.parseInt(command)) { | |
| 57 | + case 1 : recvBind(channel, headBuffer); break; | |
| 58 | + case 3 : recvDeliver(channel, headBuffer); break; | |
| 59 | + case 7 : recvLinkCheck(channel); break; | |
| 60 | + default: expireConnectUser(); break; | |
| 61 | + } | |
| 62 | + } else { | |
| 63 | + expireConnectUser(); | |
| 64 | + } | |
| 65 | + } catch (Exception e) { | |
| 66 | + size = -1; | |
| 67 | + e.printStackTrace(); | |
| 68 | + } | |
| 69 | + | |
| 70 | + /* 읽은 데이터가 없는 경우 command -1 */ | |
| 71 | + if (size <= 0) { | |
| 72 | + connectUserDto.setCommand(-1); | |
| 73 | + } | |
| 74 | + | |
| 75 | + return connectUserDto; | |
| 76 | + } | |
| 77 | + | |
| 78 | + private void recvDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException { | |
| 79 | + switch (this.serviceType) { | |
| 80 | + case "SMS": | |
| 81 | + recvSmsDeliver(channel, headBuffer); | |
| 82 | + break; | |
| 83 | + case "LMS": | |
| 84 | + recvLmsDeliver(channel, headBuffer); | |
| 85 | + break; | |
| 86 | + case "MMS": | |
| 87 | + recvMmsDeliver(channel, headBuffer); | |
| 88 | + break; | |
| 89 | + case "KAT": | |
| 90 | + recvKatDeliver(channel, headBuffer); | |
| 91 | + break; | |
| 92 | + case "KFT": | |
| 93 | + recvKftDeliver(channel, headBuffer); | |
| 94 | + break; | |
| 95 | + default:break; | |
| 96 | + } | |
| 97 | + } | |
| 98 | + | |
| 99 | + public BasicMessageDto recvCommonMessage(ByteBuffer deliverBuffer) { | |
| 100 | + if (deliverBuffer == null) { | |
| 101 | + return null; | |
| 102 | + } | |
| 103 | + BasicMessageDto messageDto = new BasicMessageDto(); | |
| 104 | + messageDto.setRouterSeq("40"); | |
| 105 | + messageDto.setServiceType("4"); | |
| 106 | + messageDto.setUserId(connectUserDto.getUserId()); | |
| 107 | + messageDto.setRemoteIP(connectUserDto.getRemoteIP()); | |
| 108 | + messageDto.setSendStatus("0"); | |
| 109 | + messageDto.setUserMsgID(CommonMessage.getMessageIdForDeliver(deliverBuffer)); | |
| 110 | + messageDto.setUserSender(CommonMessage.getSenderForDeliver(deliverBuffer)); | |
| 111 | + messageDto.setUserReceiver(CommonMessage.getReceiverForDeliver(deliverBuffer)); | |
| 112 | + messageDto.setReserveDt(CommonMessage.getReserveTimeForDeliver(deliverBuffer)); | |
| 113 | + messageDto.setRequestDt(CommonMessage.getRequestTimeForDeliver(deliverBuffer)); | |
| 114 | + messageDto.setUnitCost("10.4"); | |
| 115 | + | |
| 116 | + return messageDto; | |
| 117 | + } | |
| 118 | + private void recvSmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException { | |
| 119 | + try { | |
| 120 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(SmsMessage.DELIVER_SMS_BODY_LENGTH); | |
| 121 | + channel.read(bodyBuffer); | |
| 122 | + ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + SmsMessage.DELIVER_SMS_BODY_LENGTH); | |
| 123 | + Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer); | |
| 124 | + | |
| 125 | +// Packet.printBuffer(deliverBuffer); | |
| 126 | + BasicMessageDto messageDto = recvCommonMessage(deliverBuffer); | |
| 127 | + messageDto.setUserMessage(SmsMessage.getMessageForDeliver(deliverBuffer)); | |
| 128 | + System.out.println("BasicMessageDto : " + messageDto.toString()); | |
| 129 | + QueueTypeWorker worker = QueueTypeWorker.find("SMS"); | |
| 130 | + if (worker != null) { | |
| 131 | + worker.pushQueue(messageDto); | |
| 132 | + channel.write(SmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus())); | |
| 133 | + } | |
| 134 | + } catch (Exception e) { | |
| 135 | + e.printStackTrace(); | |
| 136 | + } | |
| 137 | + } | |
| 138 | + | |
| 139 | + private void recvLmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException { | |
| 140 | + try { | |
| 141 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(LmsMessage.DELIVER_LMS_BODY_LENGTH); | |
| 142 | + channel.read(bodyBuffer); | |
| 143 | + ByteBuffer deliverBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + LmsMessage.DELIVER_LMS_BODY_LENGTH); | |
| 144 | + Packet.mergeBuffers(deliverBuffer, headBuffer, bodyBuffer); | |
| 145 | + | |
| 146 | + BasicMessageDto messageDto = recvCommonMessage(deliverBuffer); | |
| 147 | + messageDto.setUserSubject(LmsMessage.getSubjectForDeliver(deliverBuffer)); | |
| 148 | + messageDto.setUserMessage(LmsMessage.getMessageForDeliver(deliverBuffer)); | |
| 149 | + | |
| 150 | + QueueTypeWorker worker = QueueTypeWorker.find("LMS"); | |
| 151 | + if (worker != null) { | |
| 152 | + worker.pushQueue(messageDto); | |
| 153 | + channel.write(LmsMessage.makeDeliverAckBuffer(messageDto.getUserMsgID(), messageDto.getSendStatus())); | |
| 154 | + } | |
| 155 | + } catch (Exception e) { | |
| 156 | + e.printStackTrace(); | |
| 157 | + } | |
| 158 | + } | |
| 159 | + | |
| 160 | + private void recvMmsDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException { | |
| 161 | + | |
| 162 | + } | |
| 163 | + | |
| 164 | + private void recvKatDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException { | |
| 165 | + | |
| 166 | + } | |
| 167 | + | |
| 168 | + private void recvKftDeliver(SocketChannel channel, ByteBuffer headBuffer) throws IOException { | |
| 169 | + | |
| 170 | + } | |
| 171 | + | |
| 172 | + private void recvLinkCheck(SocketChannel channel) throws IOException { | |
| 173 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(LinkCheck.LINK_CHECK_ACK_BODY_LENGTH); | |
| 174 | + channel.read(bodyBuffer); | |
| 175 | +// SocketChannel channel = (SocketChannel) key.channel(); | |
| 176 | + channel.write(LinkCheck.makeLinkCheckAckBuffer()); | |
| 177 | + } | |
| 178 | + | |
| 179 | + private void recvBind(SocketChannel channel, ByteBuffer headBuffer) { | |
| 180 | + String resultCode = "00"; | |
| 181 | + try { | |
| 182 | + ByteBuffer bindBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Bind.BIND_BODY_LENGTH); | |
| 183 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH); | |
| 184 | + channel.read(bodyBuffer); | |
| 185 | + Packet.mergeBuffers(bindBuffer, headBuffer, bodyBuffer); | |
| 186 | + | |
| 187 | + String id = Bind.getBindId(bindBuffer); | |
| 188 | + String pwd = Bind.getBindPwd(bindBuffer); | |
| 189 | + saveSystemLog("Bind id : " + id); | |
| 190 | + saveSystemLog("Bind pwd : " + pwd); | |
| 191 | + if (id == null || pwd == null) { | |
| 192 | + resultCode = "50"; | |
| 193 | + } else { | |
| 194 | + if (collectUserQueue.isExist(this.serviceType, id)) { | |
| 195 | + resultCode = "60"; | |
| 196 | + } else { | |
| 197 | + MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService(); | |
| 198 | + MemberDto memberDto = null; | |
| 199 | + if (svc != null) { | |
| 200 | + memberDto = svc.get(id); | |
| 201 | + } | |
| 202 | + if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) { | |
| 203 | + resultCode = "20"; | |
| 204 | + } else { | |
| 205 | + connectUserDto.setUserId(id); | |
| 206 | + connectUserDto.setLogin(true); | |
| 207 | + connectUserDto.setMemberDto(memberDto); | |
| 208 | + /* 세션통신 시간 업데이트 */ | |
| 209 | + connectUserDto.updateLastTrafficTime(); | |
| 210 | + } | |
| 211 | + } | |
| 212 | + } | |
| 213 | + } catch (Exception e) { | |
| 214 | + resultCode = "10"; | |
| 215 | + e.printStackTrace(); | |
| 216 | + } | |
| 217 | + | |
| 218 | + try { | |
| 219 | + saveSystemLog("Bind ResultCode : " + resultCode); | |
| 220 | + channel.write(Bind.makeBindAckBuffer(resultCode)); | |
| 221 | + if ("00".equals(resultCode) == false) { | |
| 222 | + expireConnectUser(); | |
| 223 | + } | |
| 224 | + } catch (IOException e) { | |
| 225 | + e.printStackTrace(); | |
| 226 | + } | |
| 227 | + } | |
| 228 | + | |
| 229 | + private void expireConnectUser() { | |
| 230 | + if (key == null || !key.isValid()) { | |
| 231 | + return; | |
| 232 | + } | |
| 233 | + try { | |
| 234 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 235 | + if (connectUserDto != null) { | |
| 236 | + if (connectUserDto.getUserId() != null) { | |
| 237 | + collectUserQueue.removeUser(connectUserDto.getServiceType(), connectUserDto.getUserId()); | |
| 238 | + } | |
| 239 | + key.attach(null); | |
| 240 | + } | |
| 241 | + // 소켓 채널 닫기 | |
| 242 | + channel.close(); | |
| 243 | + // 키 닫기 | |
| 244 | + key.cancel(); | |
| 245 | + } catch (IOException e) { | |
| 246 | + e.printStackTrace(); | |
| 247 | + } | |
| 248 | + } | |
| 249 | + | |
| 250 | + private void saveSystemLog(Object obj) { | |
| 251 | + saveLog(obj, true); | |
| 252 | + } | |
| 253 | + | |
| 254 | + private void saveLog(Object obj) { | |
| 255 | + saveLog(obj, false); | |
| 256 | + } | |
| 257 | + | |
| 258 | + private void saveLog(Object obj, boolean isConsoleOutput) { | |
| 259 | + if (isConsoleOutput) { | |
| 260 | + System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{COLLECT_READ_TASK}} " + obj); | |
| 261 | + } | |
| 262 | + | |
| 263 | + if (logger == null) { | |
| 264 | + return; | |
| 265 | + } | |
| 266 | + | |
| 267 | + logger.log(obj); | |
| 268 | + } | |
| 269 | +} |
+++ src/main/java/com/munjaon/server/server/task/ReportQueueTask.java
... | ... | @@ -0,0 +1,85 @@ |
| 1 | +package com.munjaon.server.server.task; | |
| 2 | + | |
| 3 | +import com.munjaon.server.cache.enums.CacheService; | |
| 4 | +import com.munjaon.server.cache.service.ReportService; | |
| 5 | +import com.munjaon.server.queue.pool.ReportQueue; | |
| 6 | +import com.munjaon.server.server.dto.ReportDto; | |
| 7 | +import com.munjaon.server.server.dto.ReportUserDto; | |
| 8 | +import com.munjaon.server.util.LogUtil; | |
| 9 | + | |
| 10 | +import java.text.SimpleDateFormat; | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | +import java.time.format.DateTimeFormatter; | |
| 13 | +import java.util.HashMap; | |
| 14 | +import java.util.List; | |
| 15 | +import java.util.Map; | |
| 16 | + | |
| 17 | +public class ReportQueueTask implements Runnable { | |
| 18 | + public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]"); | |
| 19 | + public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]"; | |
| 20 | + | |
| 21 | + private ReportUserDto reportUserDto; | |
| 22 | + private final LogUtil logger; | |
| 23 | + | |
| 24 | + public ReportQueueTask(ReportUserDto reportUserDto, LogUtil logger) { | |
| 25 | + this.reportUserDto = reportUserDto; | |
| 26 | + this.logger = logger; | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + public void run() { | |
| 31 | + if (reportUserDto == null || reportUserDto.getUserId() == null) { | |
| 32 | + return; | |
| 33 | + } | |
| 34 | + if (reportUserDto.getReportQueue() == null || reportUserDto.getReportQueue().isOpen() == false) { | |
| 35 | + return; | |
| 36 | + } | |
| 37 | + | |
| 38 | + ReportQueue reportQueue = reportUserDto.getReportQueue(); | |
| 39 | + ReportService reportService = (ReportService) CacheService.REPORT_SERVICE.getService(); | |
| 40 | + List<ReportDto> list = reportService.getReportListForUser(reportUserDto.getUserId()); | |
| 41 | + if (list == null || list.isEmpty()) { | |
| 42 | + return; | |
| 43 | + } | |
| 44 | + | |
| 45 | + StringBuilder builder = new StringBuilder(); | |
| 46 | + for (ReportDto dto : list) { | |
| 47 | + try { | |
| 48 | + if (builder.isEmpty()) { | |
| 49 | + builder.append(dto.getMsgId()); | |
| 50 | + } else { | |
| 51 | + builder.append(",").append(dto.getMsgId()); | |
| 52 | + } | |
| 53 | + saveSystemLog("reportDto : " + dto.toString()); | |
| 54 | + reportQueue.pushReportToQueue(dto); | |
| 55 | + } catch (Exception e) { | |
| 56 | + throw new RuntimeException(e); | |
| 57 | + } | |
| 58 | + } | |
| 59 | + | |
| 60 | + Map<String, String> reqMap = new HashMap<>(); | |
| 61 | + reqMap.put("userId", reportUserDto.getUserId()); | |
| 62 | + reqMap.put("msgId", builder.toString()); | |
| 63 | + reportService.deleteBulkReport(reqMap); | |
| 64 | + } | |
| 65 | + | |
| 66 | + private void saveSystemLog(Object obj) { | |
| 67 | + saveLog(obj, true); | |
| 68 | + } | |
| 69 | + | |
| 70 | + private void saveLog(Object obj) { | |
| 71 | + saveLog(obj, false); | |
| 72 | + } | |
| 73 | + | |
| 74 | + private void saveLog(Object obj, boolean isConsoleOutput) { | |
| 75 | + if (isConsoleOutput) { | |
| 76 | + System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{REPORT_QUEUE_TASK}} " + obj); | |
| 77 | + } | |
| 78 | + | |
| 79 | + if (logger == null) { | |
| 80 | + return; | |
| 81 | + } | |
| 82 | + | |
| 83 | + logger.log(obj); | |
| 84 | + } | |
| 85 | +} |
+++ src/main/java/com/munjaon/server/server/task/ReportReadTask.java
... | ... | @@ -0,0 +1,199 @@ |
| 1 | +package com.munjaon.server.server.task; | |
| 2 | + | |
| 3 | +import com.munjaon.server.cache.dto.MemberDto; | |
| 4 | +import com.munjaon.server.cache.enums.CacheService; | |
| 5 | +import com.munjaon.server.cache.service.MemberService; | |
| 6 | +import com.munjaon.server.queue.pool.ReportQueue; | |
| 7 | +import com.munjaon.server.server.dto.ReportUserDto; | |
| 8 | +import com.munjaon.server.server.packet.*; | |
| 9 | +import com.munjaon.server.server.queue.ReportUserQueue; | |
| 10 | +import com.munjaon.server.util.LogUtil; | |
| 11 | + | |
| 12 | +import java.io.IOException; | |
| 13 | +import java.nio.ByteBuffer; | |
| 14 | +import java.nio.channels.SelectionKey; | |
| 15 | +import java.nio.channels.Selector; | |
| 16 | +import java.nio.channels.SocketChannel; | |
| 17 | +import java.text.SimpleDateFormat; | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | +import java.time.format.DateTimeFormatter; | |
| 20 | +import java.util.concurrent.Callable; | |
| 21 | + | |
| 22 | +public class ReportReadTask implements Callable<ReportUserDto> { | |
| 23 | + public static final SimpleDateFormat sdf = new SimpleDateFormat("[MM-dd HH:mm:ss]"); | |
| 24 | + public static final String LOG_DATE_FORMAT = "[MM-dd HH:mm:ss]"; | |
| 25 | + | |
| 26 | + private Selector selector; | |
| 27 | + private SelectionKey key; | |
| 28 | + private final ReportUserQueue reportUserQueue = ReportUserQueue.getInstance(); | |
| 29 | + private ReportUserDto reportUserDto; | |
| 30 | + private final LogUtil logger; | |
| 31 | + | |
| 32 | + public ReportReadTask(Selector selector, SelectionKey key, LogUtil logger) { | |
| 33 | + this.selector = selector; | |
| 34 | + this.key = key; | |
| 35 | + this.reportUserDto = (ReportUserDto) key.attachment(); | |
| 36 | + this.logger = logger; | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public ReportUserDto call() throws Exception { | |
| 41 | + int size = -1; | |
| 42 | + try { | |
| 43 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 44 | + /* 1. Head 읽기 */ | |
| 45 | + ByteBuffer headBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH); | |
| 46 | + try { | |
| 47 | + size = channel.read(headBuffer); | |
| 48 | + } catch (IOException e) {} | |
| 49 | + /* 2. Body 읽기 */ | |
| 50 | + if (size > 0) { | |
| 51 | + String command = Header.getCommand(headBuffer); | |
| 52 | + switch (Integer.parseInt(command)) { | |
| 53 | + case 1 : recvBind(channel, headBuffer); break; | |
| 54 | + case 6 : recvReport(channel, headBuffer); break; | |
| 55 | + case 8 : recvLinkCheck(channel, headBuffer); break; | |
| 56 | + default: expireConnectUser(); break; | |
| 57 | + } | |
| 58 | + } else { | |
| 59 | + expireConnectUser(); | |
| 60 | + } | |
| 61 | + } catch (Exception e) { | |
| 62 | + size = -1; | |
| 63 | + e.printStackTrace(); | |
| 64 | + } | |
| 65 | + | |
| 66 | + /* 읽은 데이터가 없는 경우 command -1 */ | |
| 67 | + if (size <= 0) { | |
| 68 | + reportUserDto.setCommand(-1); | |
| 69 | + } | |
| 70 | + | |
| 71 | + return reportUserDto; | |
| 72 | + } | |
| 73 | + | |
| 74 | + private void recvLinkCheck(SocketChannel channel, ByteBuffer headBuffer) { | |
| 75 | + try { | |
| 76 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(LinkCheck.LINK_CHECK_ACK_BODY_LENGTH); | |
| 77 | + int size = channel.read(bodyBuffer); | |
| 78 | + if (size > 0) { | |
| 79 | + saveSystemLog("Recv link check"); | |
| 80 | + reportUserDto.updateLastTrafficTime(); | |
| 81 | + } | |
| 82 | + } catch (Exception e) { | |
| 83 | + e.printStackTrace(); | |
| 84 | + } | |
| 85 | + } | |
| 86 | + | |
| 87 | + private void recvReport(SocketChannel channel, ByteBuffer headBuffer) { | |
| 88 | + try { | |
| 89 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(Report.REPORT_ACK_BODY_LENGTH); | |
| 90 | + saveSystemLog("recv report"); | |
| 91 | + int size = channel.read(bodyBuffer); | |
| 92 | + if (size > 0) { | |
| 93 | + ReportQueue reportQueue = reportUserDto.getReportQueue(); | |
| 94 | + reportUserDto.updateLastTrafficTime(); | |
| 95 | + if (reportQueue != null) { | |
| 96 | + reportQueue.addReadCounter(); | |
| 97 | + } | |
| 98 | + } | |
| 99 | + } catch (Exception e) { | |
| 100 | + e.printStackTrace(); | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + private void recvBind(SocketChannel channel, ByteBuffer headBuffer) { | |
| 105 | + String resultCode = "00"; | |
| 106 | + try { | |
| 107 | + ByteBuffer bindBuffer = ByteBuffer.allocate(Header.HEADER_LENGTH + Bind.BIND_BODY_LENGTH); | |
| 108 | + ByteBuffer bodyBuffer = ByteBuffer.allocate(Bind.BIND_BODY_LENGTH); | |
| 109 | + channel.read(bodyBuffer); | |
| 110 | + Packet.mergeBuffers(bindBuffer, headBuffer, bodyBuffer); | |
| 111 | + | |
| 112 | + String id = Bind.getBindId(bindBuffer); | |
| 113 | + String pwd = Bind.getBindPwd(bindBuffer); | |
| 114 | + saveSystemLog("Bind id : " + id); | |
| 115 | + saveSystemLog("Bind pwd : " + pwd); | |
| 116 | + if (id == null || pwd == null) { | |
| 117 | + resultCode = "50"; | |
| 118 | + } else { | |
| 119 | + if (reportUserQueue.isExist(id)) { | |
| 120 | + resultCode = "60"; | |
| 121 | + } else { | |
| 122 | + MemberService svc = (MemberService) CacheService.LOGIN_SERVICE.getService(); | |
| 123 | + MemberDto memberDto = null; | |
| 124 | + if (svc != null) { | |
| 125 | + memberDto = svc.get(id); | |
| 126 | + } | |
| 127 | + if (memberDto == null || !pwd.equals(memberDto.getAccessKey())) { | |
| 128 | + resultCode = "20"; | |
| 129 | + } else { | |
| 130 | + reportUserDto.setUserId(id); | |
| 131 | + reportUserDto.setLogin(true); | |
| 132 | + reportUserDto.setMemberDto(memberDto); | |
| 133 | + /* 리포트 큐 생성 */ | |
| 134 | + ReportQueue reportQueue = new ReportQueue(reportUserDto.getQueuePath(), reportUserDto.getUserId()); | |
| 135 | + reportUserDto.setReportQueue(reportQueue); | |
| 136 | + /* 사용자 Pool에 저장 */ | |
| 137 | + reportUserQueue.putUser(reportUserDto); | |
| 138 | + /* 세션통신 시간 업데이트 */ | |
| 139 | + reportUserDto.updateLastTrafficTime(); | |
| 140 | + } | |
| 141 | + } | |
| 142 | + } | |
| 143 | + } catch (Exception e) { | |
| 144 | + resultCode = "10"; | |
| 145 | + e.printStackTrace(); | |
| 146 | + } | |
| 147 | + | |
| 148 | + try { | |
| 149 | + saveSystemLog("Bind ResultCode : " + resultCode); | |
| 150 | + channel.write(Bind.makeBindAckBuffer(resultCode)); | |
| 151 | + if ("00".equals(resultCode) == false) { | |
| 152 | + expireConnectUser(); | |
| 153 | + } | |
| 154 | + } catch (IOException e) { | |
| 155 | + e.printStackTrace(); | |
| 156 | + } | |
| 157 | + } | |
| 158 | + | |
| 159 | + private void expireConnectUser() { | |
| 160 | + if (key == null || !key.isValid()) { | |
| 161 | + return; | |
| 162 | + } | |
| 163 | + try { | |
| 164 | + SocketChannel channel = (SocketChannel) key.channel(); | |
| 165 | + if (reportUserDto != null) { | |
| 166 | + if (reportUserDto.getUserId() != null) { | |
| 167 | + reportUserQueue.removeUser(reportUserDto.getUserId()); | |
| 168 | + } | |
| 169 | + key.attach(null); | |
| 170 | + } | |
| 171 | + // 소켓 채널 닫기 | |
| 172 | + channel.close(); | |
| 173 | + // 키 닫기 | |
| 174 | + key.cancel(); | |
| 175 | + } catch (IOException e) { | |
| 176 | + e.printStackTrace(); | |
| 177 | + } | |
| 178 | + } | |
| 179 | + | |
| 180 | + private void saveSystemLog(Object obj) { | |
| 181 | + saveLog(obj, true); | |
| 182 | + } | |
| 183 | + | |
| 184 | + private void saveLog(Object obj) { | |
| 185 | + saveLog(obj, false); | |
| 186 | + } | |
| 187 | + | |
| 188 | + private void saveLog(Object obj, boolean isConsoleOutput) { | |
| 189 | + if (isConsoleOutput) { | |
| 190 | + System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(LOG_DATE_FORMAT)) + " {{REPORT_READ_TASK}} " + obj); | |
| 191 | + } | |
| 192 | + | |
| 193 | + if (logger == null) { | |
| 194 | + return; | |
| 195 | + } | |
| 196 | + | |
| 197 | + logger.log(obj); | |
| 198 | + } | |
| 199 | +} |
--- src/main/java/com/munjaon/server/util/MessageUtil.java
+++ src/main/java/com/munjaon/server/util/MessageUtil.java
... | ... | @@ -1,10 +1,9 @@ |
| 1 | 1 |
package com.munjaon.server.util; |
| 2 | 2 |
|
| 3 |
-import com.munjaon.server.queue.config.BodyCommonConfig; |
|
| 4 |
-import com.munjaon.server.queue.config.MediaBodyConfig; |
|
| 5 |
-import com.munjaon.server.queue.config.QueueHeaderConfig; |
|
| 6 |
-import com.munjaon.server.queue.config.SmsBodyConfig; |
|
| 3 |
+import com.munjaon.server.config.ServiceCode; |
|
| 4 |
+import com.munjaon.server.queue.config.*; |
|
| 7 | 5 |
import com.munjaon.server.queue.dto.BasicMessageDto; |
| 6 |
+import com.munjaon.server.server.dto.ReportDto; |
|
| 8 | 7 |
|
| 9 | 8 |
import java.nio.ByteBuffer; |
| 10 | 9 |
import java.time.LocalDateTime; |
... | ... | @@ -114,11 +113,11 @@ |
| 114 | 113 |
} |
| 115 | 114 |
|
| 116 | 115 |
public static int calcWritePosition(int pushCounter, int dataByteLength) {
|
| 117 |
- return pushCounter < 0 ? QueueHeaderConfig.QUEUE_HEADER_LENGTH : (QueueHeaderConfig.QUEUE_HEADER_LENGTH + pushCounter + dataByteLength); |
|
| 116 |
+ return pushCounter < 0 ? QueueHeaderConfig.QUEUE_HEADER_LENGTH : (QueueHeaderConfig.QUEUE_HEADER_LENGTH + pushCounter * dataByteLength); |
|
| 118 | 117 |
} |
| 119 | 118 |
|
| 120 | 119 |
public static int calcReadPosition(int popCounter, int dataByteLength) {
|
| 121 |
- return popCounter < 0 ? QueueHeaderConfig.QUEUE_HEADER_LENGTH : (QueueHeaderConfig.QUEUE_HEADER_LENGTH + popCounter + dataByteLength); |
|
| 120 |
+ return popCounter < 0 ? QueueHeaderConfig.QUEUE_HEADER_LENGTH : (QueueHeaderConfig.QUEUE_HEADER_LENGTH + popCounter * dataByteLength); |
|
| 122 | 121 |
} |
| 123 | 122 |
|
| 124 | 123 |
public static void setBytesForCommonMessage(ByteBuffer buffer, BasicMessageDto messageDto) {
|
... | ... | @@ -258,6 +257,23 @@ |
| 258 | 257 |
buffer.put(messageDto.getUserMessage().getBytes()); |
| 259 | 258 |
} |
| 260 | 259 |
|
| 260 |
+ public static void getBytesForMediaMessage(ByteBuffer buffer, BasicMessageDto messageDto) {
|
|
| 261 |
+ byte[] destArray = null; |
|
| 262 |
+ if (buffer == null || messageDto == null) {
|
|
| 263 |
+ return; |
|
| 264 |
+ } |
|
| 265 |
+ /* 14. 제목 */ |
|
| 266 |
+ buffer.position(MediaBodyConfig.SUBJECT_BYTE_POSITION); |
|
| 267 |
+ destArray = new byte[MediaBodyConfig.SUBJECT_BYTE_LENGTH]; |
|
| 268 |
+ buffer.get(destArray); |
|
| 269 |
+ messageDto.setUserSubject(new String(destArray)); |
|
| 270 |
+ /* 15. 메시지 */ |
|
| 271 |
+ buffer.position(MediaBodyConfig.MEDIA_MSG_BYTE_POSITION); |
|
| 272 |
+ destArray = new byte[MediaBodyConfig.MEDIA_MSG_BYTE_LENGTH]; |
|
| 273 |
+ buffer.get(destArray); |
|
| 274 |
+ messageDto.setUserMessage(new String(destArray)); |
|
| 275 |
+ } |
|
| 276 |
+ |
|
| 261 | 277 |
public static void setBytesForMmsMessage(ByteBuffer buffer, BasicMessageDto messageDto) {
|
| 262 | 278 |
/* 16. 파일카운트 */ |
| 263 | 279 |
buffer.position(MediaBodyConfig.FILECNT_BYTE_POSITION); |
... | ... | @@ -278,4 +294,137 @@ |
| 278 | 294 |
buffer.put(messageDto.getUserFileName03().getBytes()); |
| 279 | 295 |
} |
| 280 | 296 |
} |
| 297 |
+ |
|
| 298 |
+ public static void getBytesForMmsMessage(ByteBuffer buffer, BasicMessageDto messageDto) {
|
|
| 299 |
+ byte[] destArray = null; |
|
| 300 |
+ if (buffer == null || messageDto == null) {
|
|
| 301 |
+ return; |
|
| 302 |
+ } |
|
| 303 |
+ /* 16. 파일카운트 */ |
|
| 304 |
+ buffer.position(MediaBodyConfig.FILECNT_BYTE_POSITION); |
|
| 305 |
+ destArray = new byte[MediaBodyConfig.FILECNT_BYTE_LENGTH]; |
|
| 306 |
+ buffer.get(destArray); |
|
| 307 |
+ messageDto.setUserFileCnt(Integer.parseInt(new String(destArray))); |
|
| 308 |
+ /* 17. 파일명 #1 */ |
|
| 309 |
+ buffer.position(MediaBodyConfig.FILENAME_ONE_BYTE_POSITION); |
|
| 310 |
+ destArray = new byte[MediaBodyConfig.FILENAME_ONE_BYTE_LENGTH]; |
|
| 311 |
+ buffer.get(destArray); |
|
| 312 |
+ messageDto.setUserFileName01(new String(destArray)); |
|
| 313 |
+ /* 18. 파일명 #2 */ |
|
| 314 |
+ buffer.position(MediaBodyConfig.FILENAME_TWO_BYTE_POSITION); |
|
| 315 |
+ destArray = new byte[MediaBodyConfig.FILENAME_TWO_BYTE_LENGTH]; |
|
| 316 |
+ buffer.get(destArray); |
|
| 317 |
+ messageDto.setUserFileName02(new String(destArray)); |
|
| 318 |
+ /* 19. 파일명 #3 */ |
|
| 319 |
+ buffer.position(MediaBodyConfig.FILENAME_THREE_BYTE_POSITION); |
|
| 320 |
+ destArray = new byte[MediaBodyConfig.FILENAME_THREE_BYTE_LENGTH]; |
|
| 321 |
+ buffer.get(destArray); |
|
| 322 |
+ messageDto.setUserFileName03(new String(destArray)); |
|
| 323 |
+ } |
|
| 324 |
+ |
|
| 325 |
+ public static int isValidateMessageForReport(ReportDto reportDto) {
|
|
| 326 |
+ if (reportDto == null) {
|
|
| 327 |
+ return ServiceCode.MSG_ERROR_REPORT.getCode(); |
|
| 328 |
+ } |
|
| 329 |
+ /* MSG_ID (Length : 20 / Position : 0) */ |
|
| 330 |
+ if (reportDto.getAgentMsgId() == null || reportDto.getAgentMsgId().trim().isEmpty()) {
|
|
| 331 |
+ return ServiceCode.MSG_ERROR_REPORT_MSG_ID.getCode(); |
|
| 332 |
+ } |
|
| 333 |
+ if (reportDto.getAgentMsgId().trim().length() > ReportConfig.MSG_ID_LENGTH) {
|
|
| 334 |
+ return ServiceCode.MSG_ERROR_REPORT_MSG_ID.getCode(); |
|
| 335 |
+ } |
|
| 336 |
+ /* AGENT_CODE (Length : 2 / Position : 20) */ |
|
| 337 |
+ if (reportDto.getAgentCode() == null || reportDto.getAgentCode().trim().isEmpty()) {
|
|
| 338 |
+ return ServiceCode.MSG_ERROR_REPORT_AGENT_CODE.getCode(); |
|
| 339 |
+ } |
|
| 340 |
+ if (reportDto.getAgentCode().trim().length() > ReportConfig.AGENT_CODE_LENGTH) {
|
|
| 341 |
+ return ServiceCode.MSG_ERROR_REPORT_AGENT_CODE.getCode(); |
|
| 342 |
+ } |
|
| 343 |
+ /* SEND_TIME (Length : 14 / Position : 22) */ |
|
| 344 |
+ if (reportDto.getRsltDate() == null || reportDto.getRsltDate().trim().isEmpty()) {
|
|
| 345 |
+ return ServiceCode.MSG_ERROR_REPORT_SEND_TIME.getCode(); |
|
| 346 |
+ } |
|
| 347 |
+ if (reportDto.getRsltDate().trim().length() > ReportConfig.SEND_TIME_LENGTH) {
|
|
| 348 |
+ return ServiceCode.MSG_ERROR_REPORT_SEND_TIME.getCode(); |
|
| 349 |
+ } |
|
| 350 |
+ /* TELECOM (Length : 3 / Position : 36) */ |
|
| 351 |
+ if (reportDto.getRsltNet() == null || reportDto.getRsltNet().trim().isEmpty()) {
|
|
| 352 |
+ return ServiceCode.MSG_ERROR_REPORT_TELECOM.getCode(); |
|
| 353 |
+ } |
|
| 354 |
+ if (reportDto.getRsltNet().trim().length() > ReportConfig.TELECOM_LENGTH) {
|
|
| 355 |
+ return ServiceCode.MSG_ERROR_REPORT_TELECOM.getCode(); |
|
| 356 |
+ } |
|
| 357 |
+ /* RESULT (Length : 5 / Position : 39) */ |
|
| 358 |
+ if (reportDto.getRsltCode() == null || reportDto.getRsltCode().trim().isEmpty()) {
|
|
| 359 |
+ return ServiceCode.MSG_ERROR_REPORT_RESULT.getCode(); |
|
| 360 |
+ } |
|
| 361 |
+ if (reportDto.getRsltCode().trim().length() > ReportConfig.RESULT_LENGTH) {
|
|
| 362 |
+ return ServiceCode.MSG_ERROR_REPORT_RESULT.getCode(); |
|
| 363 |
+ } |
|
| 364 |
+ |
|
| 365 |
+ return ServiceCode.OK.getCode(); |
|
| 366 |
+ } |
|
| 367 |
+ |
|
| 368 |
+ public static int calcWritePositionForReport(int pushCounter, int dataByteLength) {
|
|
| 369 |
+ return pushCounter < 0 ? ReportConfig.HEAD_LENGTH : (ReportConfig.HEAD_LENGTH + pushCounter * dataByteLength); |
|
| 370 |
+ } |
|
| 371 |
+ |
|
| 372 |
+ public static void setBytesForReport(ByteBuffer buffer, ReportDto reportDto) {
|
|
| 373 |
+ if (buffer == null || reportDto == null) {
|
|
| 374 |
+ return; |
|
| 375 |
+ } |
|
| 376 |
+ |
|
| 377 |
+ byte[] destArray = null; |
|
| 378 |
+ /* MSG_ID (Length : 20 / Position : 0) */ |
|
| 379 |
+ buffer.position(ReportConfig.MSG_ID_POSITION); |
|
| 380 |
+ buffer.put(reportDto.getAgentMsgId().getBytes()); |
|
| 381 |
+ /* AGENT_CODE (Length : 2 / Position : 20) */ |
|
| 382 |
+ buffer.position(ReportConfig.AGENT_CODE_POSITION); |
|
| 383 |
+ buffer.put(reportDto.getAgentCode().getBytes()); |
|
| 384 |
+ /* SEND_TIME (Length : 14 / Position : 22) */ |
|
| 385 |
+ buffer.position(ReportConfig.SEND_TIME_POSITION); |
|
| 386 |
+ buffer.put(reportDto.getRsltDate().getBytes()); |
|
| 387 |
+ /* TELECOM (Length : 3 / Position : 36) */ |
|
| 388 |
+ buffer.position(ReportConfig.TELECOM_POSITION); |
|
| 389 |
+ buffer.put(reportDto.getRsltNet().getBytes()); |
|
| 390 |
+ /* RESULT (Length : 5 / Position : 39) */ |
|
| 391 |
+ buffer.position(ReportConfig.RESULT_POSITION); |
|
| 392 |
+ buffer.put(reportDto.getRsltCode().getBytes()); |
|
| 393 |
+ } |
|
| 394 |
+ |
|
| 395 |
+ public static ReportDto getReportFromBuffer(ByteBuffer buffer) {
|
|
| 396 |
+ if (buffer == null) {
|
|
| 397 |
+ return null; |
|
| 398 |
+ } |
|
| 399 |
+ |
|
| 400 |
+ byte[] destArray = null; |
|
| 401 |
+ ReportDto reportDto = new ReportDto(); |
|
| 402 |
+ /* MSG_ID (Length : 20 / Position : 0) */ |
|
| 403 |
+ buffer.position(ReportConfig.MSG_ID_POSITION); |
|
| 404 |
+ destArray = new byte[ReportConfig.MSG_ID_LENGTH]; |
|
| 405 |
+ buffer.get(destArray); |
|
| 406 |
+ reportDto.setMsgId(new String(destArray).trim()); |
|
| 407 |
+ /* AGENT_CODE (Length : 2 / Position : 20) */ |
|
| 408 |
+ buffer.position(ReportConfig.AGENT_CODE_POSITION); |
|
| 409 |
+ destArray = new byte[ReportConfig.AGENT_CODE_LENGTH]; |
|
| 410 |
+ buffer.get(destArray); |
|
| 411 |
+ reportDto.setAgentCode(new String(destArray).trim()); |
|
| 412 |
+ /* SEND_TIME (Length : 14 / Position : 22) */ |
|
| 413 |
+ buffer.position(ReportConfig.SEND_TIME_POSITION); |
|
| 414 |
+ destArray = new byte[ReportConfig.SEND_TIME_LENGTH]; |
|
| 415 |
+ buffer.get(destArray); |
|
| 416 |
+ reportDto.setRsltDate(new String(destArray).trim()); |
|
| 417 |
+ /* TELECOM (Length : 3 / Position : 36) */ |
|
| 418 |
+ buffer.position(ReportConfig.TELECOM_POSITION); |
|
| 419 |
+ destArray = new byte[ReportConfig.TELECOM_LENGTH]; |
|
| 420 |
+ buffer.get(destArray); |
|
| 421 |
+ reportDto.setRsltNet(new String(destArray).trim()); |
|
| 422 |
+ /* RESULT (Length : 5 / Position : 39) */ |
|
| 423 |
+ buffer.position(ReportConfig.RESULT_POSITION); |
|
| 424 |
+ destArray = new byte[ReportConfig.RESULT_LENGTH]; |
|
| 425 |
+ buffer.get(destArray); |
|
| 426 |
+ reportDto.setRsltCode(new String(destArray).trim()); |
|
| 427 |
+ |
|
| 428 |
+ return reportDto; |
|
| 429 |
+ } |
|
| 281 | 430 |
} |
+++ src/main/resources/sqlmap/lms_sql.xml
... | ... | @@ -0,0 +1,21 @@ |
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
| 3 | +<mapper namespace="com.munjaon.server.queue.mapper.LmsMapper"> | |
| 4 | + <insert id="insert"> | |
| 5 | + INSERT INTO MJ_MSG_DATA ( | |
| 6 | + MSG_ID | |
| 7 | + , MSG_GROUP_ID | |
| 8 | + , USER_ID | |
| 9 | + , AGENT_MSG_ID | |
| 10 | + , AGENT_CODE | |
| 11 | + , CUR_STATE | |
| 12 | + , REQ_DATE | |
| 13 | + , CALL_TO | |
| 14 | + , CALL_FROM | |
| 15 | + , SUBJECT | |
| 16 | + , SMS_TXT | |
| 17 | + , MSG_TYPE | |
| 18 | + , CONT_SEQ, FILE_CNT | |
| 19 | + ) VALUES (#{id}, #{msgGroupID}, #{userId}, #{userMsgID}, '04', 0, NOW(), #{userReceiver}, #{userSender}, #{userSubject}, #{userMessage}, '6', null, '0' ) | |
| 20 | + </insert> | |
| 21 | +</mapper>(No newline at end of file) |
--- src/main/resources/sqlmap/report_sql.xml
+++ src/main/resources/sqlmap/report_sql.xml
... | ... | @@ -9,14 +9,44 @@ |
| 9 | 9 |
, AGENT_MSG_ID |
| 10 | 10 |
, AGENT_CODE |
| 11 | 11 |
, MSG_TYPE |
| 12 |
- , RSLT_DATE |
|
| 12 |
+ , CASE WHEN RSLT_DATE IS NULL THEN DATE_FORMAT(NOW(), '%Y%m%d%H%i%S') ELSE DATE_FORMAT(RSLT_DATE, '%Y%m%d%H%i%S') END AS RSLT_DATE |
|
| 13 | 13 |
, RSLT_CODE |
| 14 | 14 |
, RSLT_NET |
| 15 | 15 |
FROM mj_msg_report |
| 16 | 16 |
WHERE USER_ID = #{userId}
|
| 17 | 17 |
LIMIT 1 |
| 18 | 18 |
</select> |
| 19 |
+ |
|
| 20 |
+ <select id="getReportListForUser" resultType="ReportDto"> |
|
| 21 |
+ /* ReportMapper.getReportListForUser */ |
|
| 22 |
+ SELECT |
|
| 23 |
+ MSG_ID |
|
| 24 |
+ , USER_ID |
|
| 25 |
+ , AGENT_MSG_ID |
|
| 26 |
+ , AGENT_CODE |
|
| 27 |
+ , MSG_TYPE |
|
| 28 |
+ , CASE WHEN RSLT_DATE IS NULL THEN DATE_FORMAT(NOW(), '%Y%m%d%H%i%S') ELSE DATE_FORMAT(RSLT_DATE, '%Y%m%d%H%i%S') END AS RSLT_DATE |
|
| 29 |
+ , RSLT_CODE |
|
| 30 |
+ , RSLT_NET |
|
| 31 |
+ FROM mj_msg_report |
|
| 32 |
+ WHERE USER_ID = #{userId}
|
|
| 33 |
+ LIMIT 50 |
|
| 34 |
+ </select> |
|
| 35 |
+ |
|
| 19 | 36 |
<delete id="deleteReport"> |
| 20 | 37 |
DELETE FROM mj_msg_report WHERE MSG_ID = #{msgId}
|
| 21 | 38 |
</delete> |
| 39 |
+ |
|
| 40 |
+ <delete id="deleteBulkReport"> |
|
| 41 |
+ DELETE SRC FROM mj_msg_report SRC |
|
| 42 |
+ INNER JOIN ( |
|
| 43 |
+ with recursive |
|
| 44 |
+ T as ( select #{msgId} as items),
|
|
| 45 |
+ N as ( select 1 as n union select n + 1 from N, T |
|
| 46 |
+ where n <![CDATA[<=]]> length(items) - length(replace(items, ',', ''))) |
|
| 47 |
+ select distinct substring_index(substring_index(items, ',', n), ',', -1) |
|
| 48 |
+ MSG_ID from N, T |
|
| 49 |
+ ) DEST ON SRC.MSG_ID = DEST.MSG_ID |
|
| 50 |
+ WHERE SRC.USER_ID = #{userId}
|
|
| 51 |
+ </delete> |
|
| 22 | 52 |
</mapper>(No newline at end of file) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?