From d7e27d6d6988a0cd98100097c9207ec0f5a9f83c Mon Sep 17 00:00:00 2001 From: yx9o Date: Thu, 4 Dec 2025 19:09:08 +0800 Subject: [PATCH] [ISSUE #9396] Use fastjson2 in all modules (#9397) * Use fastjson2 in all modules * Update test * Update test * Update test * Add serialization compatibility test tool class * Update RemotingSerializableCompatTest.java * Update RemotingSerializableCompatTest.java * Update RemotingSerializableCompatTest.java * Update BitSet problem * Update * Update * Update test * Update test * Update BUILD.bazel * Update BUILD.bazel * Update test * Update BitSet problem * Add test * Add compat test * merge develop * Update test * merge develop --- broker/BUILD.bazel | 2 - broker/pom.xml | 4 - .../config/v1/RocksDBConfigManager.java | 9 +- .../v1/RocksDBConsumerOffsetManager.java | 15 +- .../v1/RocksDBSubscriptionGroupManager.java | 25 +- .../config/v1/RocksDBTopicConfigManager.java | 15 +- .../broker/pop/PopConsumerRecord.java | 8 +- .../broker/pop/PopConsumerService.java | 35 +- .../orderly/QueueLevelConsumerManager.java | 2 +- .../broker/processor/AckMessageProcessor.java | 2 +- .../processor/AdminBrokerProcessor.java | 7 +- .../ChangeInvisibleTimeProcessor.java | 2 +- .../processor/PopBufferMergeService.java | 19 +- .../broker/processor/PopMessageProcessor.java | 29 +- .../broker/processor/PopReviveService.java | 23 +- .../topic/TopicQueueMappingManager.java | 8 +- .../transaction/TransactionMetrics.java | 27 +- .../broker/RocksDBConfigManagerTest.java | 67 ++ .../broker/client/ConsumerManagerTest.java | 4 + .../RocksDBSubscriptionGroupManagerTest.java | 120 +++ .../v1/RocksDBTopicConfigManagerTest.java | 97 ++ .../pagecache/QueryMessageTransferTest.java | 139 +++ .../processor/AdminBrokerProcessorTest.java | 84 +- .../ChangeInvisibleTimeProcessorTest.java | 83 +- .../processor/PopBufferMergeServiceTest.java | 192 +++- .../processor/PopMessageProcessorTest.java | 50 +- .../processor/PopReviveServiceTest.java | 105 ++- .../SubscriptionGroupManagerTest.java | 8 +- .../topic/TopicQueueMappingManagerTest.java | 68 +- .../queue/TransactionMetricsTest.java | 59 +- client/BUILD.bazel | 1 - .../rocketmq/client/impl/MQClientAPIImpl.java | 2 +- .../client/impl/factory/MQClientInstance.java | 39 +- .../rocketmq/client/producer/SendResult.java | 2 +- .../consumer/DefaultLitePullConsumerTest.java | 25 +- .../client/impl/MQClientAPIImplTest.java | 18 + .../impl/admin/MqClientAdminImplTest.java | 4 +- .../impl/consumer/PopProcessQueueTest.java | 56 ++ .../impl/consumer/ProcessQueueTest.java | 12 +- .../client/producer/SendResultTest.java | 59 ++ common/BUILD.bazel | 2 - .../apache/rocketmq/common/TopicConfig.java | 11 +- .../GenericMapSuperclassDeserializer.java | 43 +- .../common/utils/FastJsonSerializer.java | 16 +- .../GenericMapSuperclassDeserializerTest.java | 104 +++ container/BUILD.bazel | 2 - controller/BUILD.bazel | 4 +- .../heartbeat/RaftBrokerHeartBeatManager.java | 4 +- .../impl/manager/RaftReplicasInfoManager.java | 2 +- .../impl/RaftBrokerHeartBeatManagerTest.java | 11 +- .../impl/event/EventSerializerTest.java | 122 +++ .../impl/event/ListEventSerializerTest.java | 93 ++ .../RaftBrokerHeartBeatManagerTest.java | 352 ++++++++ .../manager/RaftReplicasInfoManagerTest.java | 243 ++++++ filter/BUILD.bazel | 1 - namesrv/BUILD.bazel | 4 +- .../processor/ClientRequestProcessor.java | 12 +- .../processor/ClientRequestProcessorTest.java | 195 +++++ proxy/BUILD.bazel | 2 - .../rocketmq/proxy/config/Configuration.java | 2 +- .../proxy/config/ConfigurationManager.java | 6 +- .../channel/RemoteChannelSerializer.java | 9 +- .../activity/GetTopicRouteActivity.java | 17 +- .../remoting/channel/RemotingChannel.java | 15 +- .../AbstractSystemMessageSyncer.java | 11 +- .../service/sysmessage/HeartbeatSyncer.java | 19 +- .../config/ConfigurationManagerTest.java | 10 + .../proxy/config/ConfigurationTest.java | 46 + .../activity/GetTopicRouteActivityTest.java | 171 ++++ remoting/BUILD.bazel | 4 +- .../remoting/netty/NettyRemotingClient.java | 47 +- .../BitSetSerializerDeserializer.java | 53 +- .../remoting/protocol/RemotingCommand.java | 22 +- .../protocol/RemotingSerializable.java | 12 +- .../remoting/protocol/body/BatchAck.java | 2 +- .../protocol/body/RegisterBrokerBody.java | 21 +- .../protocol/heartbeat/HeartbeatData.java | 10 +- .../protocol/heartbeat/SubscriptionData.java | 5 +- .../subscription/GroupRetryPolicy.java | 2 +- .../RemotingSerializableCompatTest.java | 425 +++++++++ .../protocol/RemotingSerializableTest.java | 5 +- .../remoting/protocol/body/BatchAckTest.java | 2 +- .../statictopic/TopicQueueMappingTest.java | 14 +- srvutil/BUILD.bazel | 1 - store/BUILD.bazel | 3 +- .../org/apache/rocketmq/store/pop/AckMsg.java | 2 +- .../rocketmq/store/pop/BatchAckMsg.java | 3 +- .../rocketmq/store/pop/PopCheckPoint.java | 6 +- .../store/queue/CombineConsumeQueueStore.java | 2 +- .../rocketmq/store/timer/TimerMetrics.java | 34 +- .../apache/rocketmq/store/pop/AckMsgTest.java | 2 +- .../rocketmq/store/pop/BatchAckMsgTest.java | 2 +- test/BUILD.bazel | 1 - tieredstore/BUILD.bazel | 4 +- .../tieredstore/file/FlatMessageFile.java | 2 +- .../metadata/DefaultMetadataStore.java | 28 +- .../metadata/entity/FileSegmentMetadata.java | 2 +- .../metadata/entity/QueueMetadata.java | 2 +- .../metadata/entity/TopicMetadata.java | 2 +- tools/BUILD.bazel | 1 - tools/pom.xml | 4 - .../tools/admin/DefaultMQAdminExtImpl.java | 2 +- .../GetColdDataFlowCtrInfoSubCommand.java | 18 +- .../consumer/UpdateSubGroupSubCommand.java | 7 +- .../command/export/ExportConfigsCommand.java | 20 +- .../command/export/ExportMetadataCommand.java | 16 +- .../ExportMetadataInRocksDBCommand.java | 17 +- .../command/export/ExportMetricsCommand.java | 16 +- .../metadata/RocksDBConfigToJsonCommand.java | 38 +- .../queue/QueryConsumeQueueCommand.java | 5 +- .../admin/DefaultMQAdminExtImplTest.java | 825 ++++++++++++++++++ .../ConsumerConnectionSubCommandTest.java | 5 +- .../ProducerConnectionSubCommandTest.java | 5 +- .../ConsumerStatusSubCommandTest.java | 5 +- .../GetConsumerConfigSubCommandTest.java | 9 +- .../producer/ProducerSubCommandTest.java | 36 +- 116 files changed, 4209 insertions(+), 595 deletions(-) create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/RocksDBConfigManagerTest.java create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManagerTest.java create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManagerTest.java create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransferTest.java create mode 100644 client/src/test/java/org/apache/rocketmq/client/impl/consumer/PopProcessQueueTest.java create mode 100644 client/src/test/java/org/apache/rocketmq/client/producer/SendResultTest.java create mode 100644 common/src/test/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializerTest.java create mode 100644 controller/src/test/java/org/apache/rocketmq/controller/impl/event/EventSerializerTest.java create mode 100644 controller/src/test/java/org/apache/rocketmq/controller/impl/event/ListEventSerializerTest.java create mode 100644 controller/src/test/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManagerTest.java create mode 100644 controller/src/test/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManagerTest.java create mode 100644 namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessorTest.java create mode 100644 proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationTest.java create mode 100644 proxy/src/test/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivityTest.java create mode 100644 remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableCompatTest.java create mode 100644 tools/src/test/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImplTest.java diff --git a/broker/BUILD.bazel b/broker/BUILD.bazel index fdf549d3e5..ffd2bea14c 100644 --- a/broker/BUILD.bazel +++ b/broker/BUILD.bazel @@ -31,7 +31,6 @@ java_library( "//tieredstore", "@maven//:org_slf4j_slf4j_api", "@maven//:ch_qos_logback_logback_classic", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_github_luben_zstd_jni", "@maven//:com_google_guava_guava", @@ -83,7 +82,6 @@ java_library( "//remoting", "//store", "//tieredstore", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:org_slf4j_slf4j_api", "@maven//:com_google_guava_guava", diff --git a/broker/pom.xml b/broker/pom.xml index 1751852631..b94e596cc5 100644 --- a/broker/pom.xml +++ b/broker/pom.xml @@ -66,10 +66,6 @@ commons-io commons-io - - com.alibaba - fastjson - org.javassist javassist diff --git a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConfigManager.java b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConfigManager.java index e4827e2baf..e68cd20a9d 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConfigManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConfigManager.java @@ -16,10 +16,7 @@ */ package org.apache.rocketmq.broker.config.v1; -import com.alibaba.fastjson.JSON; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.function.BiConsumer; +import com.alibaba.fastjson2.JSON; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.common.config.ConfigRocksDBStorage; import org.apache.rocketmq.common.constant.LoggerName; @@ -33,6 +30,10 @@ import org.rocksdb.RocksDBException; import org.rocksdb.Statistics; import org.rocksdb.WriteBatch; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.function.BiConsumer; + public class RocksDBConfigManager { protected static final Logger BROKER_LOG = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME); diff --git a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConsumerOffsetManager.java b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConsumerOffsetManager.java index 6536f90a8d..45e1a2ab23 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConsumerOffsetManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBConsumerOffsetManager.java @@ -16,12 +16,8 @@ */ package org.apache.rocketmq.broker.config.v1; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentMap; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.BrokerPathConfigHelper; @@ -35,6 +31,11 @@ import org.apache.rocketmq.remoting.protocol.DataVersion; import org.rocksdb.CompressionType; import org.rocksdb.WriteBatch; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + public class RocksDBConsumerOffsetManager extends ConsumerOffsetManager { protected static final Logger log = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME); @@ -184,7 +185,7 @@ public class RocksDBConsumerOffsetManager extends ConsumerOffsetManager { byte[] keyBytes = topicGroupName.getBytes(DataConverter.CHARSET_UTF8); RocksDBOffsetSerializeWrapper wrapper = new RocksDBOffsetSerializeWrapper(); wrapper.setOffsetTable(offsetMap); - byte[] valueBytes = JSON.toJSONBytes(wrapper, SerializerFeature.BrowserCompatible); + byte[] valueBytes = JSON.toJSONBytes(wrapper, JSONWriter.Feature.BrowserCompatible); rocksDBConfigManager.writeBatchPutOperation(writeBatch, keyBytes, valueBytes); } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManager.java b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManager.java index f6ae3a3e59..b4392212a6 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManager.java @@ -16,16 +16,9 @@ */ package org.apache.rocketmq.broker.config.v1; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.serializer.SerializerFeature; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiConsumer; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.BrokerPathConfigHelper; @@ -35,6 +28,14 @@ import org.apache.rocketmq.remoting.protocol.DataVersion; import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; import org.rocksdb.CompressionType; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; + public class RocksDBSubscriptionGroupManager extends SubscriptionGroupManager { protected transient RocksDBConfigManager rocksDBConfigManager; @@ -162,7 +163,7 @@ public class RocksDBSubscriptionGroupManager extends SubscriptionGroupManager { try { byte[] keyBytes = groupName.getBytes(RocksDBConfigManager.CHARSET); - byte[] valueBytes = JSON.toJSONBytes(subscriptionGroupConfig, SerializerFeature.BrowserCompatible); + byte[] valueBytes = JSON.toJSONBytes(subscriptionGroupConfig, JSONWriter.Feature.BrowserCompatible); this.rocksDBConfigManager.put(keyBytes, valueBytes); } catch (Exception e) { log.error("kv put sub Failed, {}", subscriptionGroupConfig.toString()); @@ -177,7 +178,7 @@ public class RocksDBSubscriptionGroupManager extends SubscriptionGroupManager { if (oldConfig == null) { try { byte[] keyBytes = groupName.getBytes(RocksDBConfigManager.CHARSET); - byte[] valueBytes = JSON.toJSONBytes(subscriptionGroupConfig, SerializerFeature.BrowserCompatible); + byte[] valueBytes = JSON.toJSONBytes(subscriptionGroupConfig, JSONWriter.Feature.BrowserCompatible); this.rocksDBConfigManager.put(keyBytes, valueBytes); } catch (Exception e) { log.error("kv put sub Failed, {}", subscriptionGroupConfig.toString()); diff --git a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManager.java b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManager.java index 4a8d124e9b..96f12e6681 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManager.java @@ -16,12 +16,8 @@ */ package org.apache.rocketmq.broker.config.v1; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; -import java.util.concurrent.ConcurrentMap; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.BrokerPathConfigHelper; @@ -32,6 +28,11 @@ import org.apache.rocketmq.common.utils.DataConverter; import org.apache.rocketmq.remoting.protocol.DataVersion; import org.rocksdb.CompressionType; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + public class RocksDBTopicConfigManager extends TopicConfigManager { private static final String VERSION_COLUMN_FAMILY = "topicVersion"; private static final String TOPIC_COLUMN_FAMILY = "topic"; @@ -142,7 +143,7 @@ public class RocksDBTopicConfigManager extends TopicConfigManager { TopicConfig oldTopicConfig = this.topicConfigTable.put(topicName, topicConfig); try { byte[] keyBytes = topicName.getBytes(DataConverter.CHARSET_UTF8); - byte[] valueBytes = JSON.toJSONBytes(topicConfig, SerializerFeature.BrowserCompatible); + byte[] valueBytes = JSON.toJSONBytes(topicConfig, JSONWriter.Feature.BrowserCompatible); this.rocksDBConfigManager.put(keyBytes, valueBytes); } catch (Exception e) { log.error("kv put topic Failed, {}", topicConfig.toString(), e); diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerRecord.java b/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerRecord.java index 1ee01fea1c..661ace9bcb 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerRecord.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerRecord.java @@ -16,9 +16,9 @@ */ package org.apache.rocketmq.broker.pop; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.annotation.JSONField; + import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -119,7 +119,7 @@ public class PopConsumerRecord { } public static PopConsumerRecord decode(byte[] body) { - return JSONObject.parseObject(body, PopConsumerRecord.class); + return JSON.parseObject(body, PopConsumerRecord.class); } public long getPopTime() { diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerService.java b/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerService.java index 7678daa1d3..839c96e390 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerService.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/pop/PopConsumerService.java @@ -16,25 +16,9 @@ */ package org.apache.rocketmq.broker.pop; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; -import java.nio.ByteBuffer; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.Queue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Triple; import org.apache.rocketmq.broker.BrokerController; @@ -65,6 +49,23 @@ import org.apache.rocketmq.store.pop.PopCheckPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.ByteBuffer; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + public class PopConsumerService extends ServiceThread { private static final Logger log = LoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME); diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pop/orderly/QueueLevelConsumerManager.java b/broker/src/main/java/org/apache/rocketmq/broker/pop/orderly/QueueLevelConsumerManager.java index 79bd59fb78..8a5ed6b150 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/pop/orderly/QueueLevelConsumerManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/pop/orderly/QueueLevelConsumerManager.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.broker.pop.orderly; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import java.util.ArrayList; diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java index 864f40d296..1334693352 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import java.nio.charset.StandardCharsets; diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java index 298e239086..e7333ab91a 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java @@ -16,8 +16,9 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import com.google.common.collect.Sets; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -2785,7 +2786,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor { } else { ConsumerFilterData filterData = this.brokerController.getConsumerFilterManager() .get(requestHeader.getTopic(), requestHeader.getConsumerGroup()); - body.setFilterData(JSON.toJSONString(filterData, true)); + body.setFilterData(JSON.toJSONString(filterData, JSONWriter.Feature.PrettyFormat)); messageFilter = new ExpressionMessageFilter(subscriptionData, filterData, this.brokerController.getConsumerFilterManager()); diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java index f104e76a52..332930cd78 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import java.nio.charset.StandardCharsets; diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java index 657adaa34d..06d89e047d 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java @@ -16,15 +16,7 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.atomic.AtomicInteger; +import com.alibaba.fastjson2.JSON; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.common.KeyBuilder; import org.apache.rocketmq.common.PopAckConstants; @@ -43,6 +35,15 @@ import org.apache.rocketmq.store.pop.AckMsg; import org.apache.rocketmq.store.pop.BatchAckMsg; import org.apache.rocketmq.store.pop.PopCheckPoint; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.atomic.AtomicInteger; + public class PopBufferMergeService extends ServiceThread { private static final Logger POP_LOGGER = LoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME); ConcurrentHashMap diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java index 9967955656..3144eb973a 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java @@ -16,26 +16,13 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import com.github.benmanes.caffeine.cache.Cache; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.FileRegion; import io.opentelemetry.api.common.Attributes; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.filter.ConsumerFilterData; import org.apache.rocketmq.broker.filter.ConsumerFilterManager; @@ -89,6 +76,20 @@ import org.apache.rocketmq.store.pop.AckMsg; import org.apache.rocketmq.store.pop.BatchAckMsg; import org.apache.rocketmq.store.pop.PopCheckPoint; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + import static org.apache.rocketmq.broker.metrics.BrokerMetricsConstant.LABEL_CONSUMER_GROUP; import static org.apache.rocketmq.broker.metrics.BrokerMetricsConstant.LABEL_IS_RETRY; import static org.apache.rocketmq.broker.metrics.BrokerMetricsConstant.LABEL_IS_SYSTEM; diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java index aa7d87505e..434812883e 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java @@ -16,18 +16,8 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import io.opentelemetry.api.common.Attributes; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.TreeMap; -import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Triple; import org.apache.rocketmq.broker.BrokerController; @@ -59,6 +49,17 @@ import org.apache.rocketmq.store.pop.AckMsg; import org.apache.rocketmq.store.pop.BatchAckMsg; import org.apache.rocketmq.store.pop.PopCheckPoint; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; + import static org.apache.rocketmq.broker.metrics.BrokerMetricsConstant.LABEL_CONSUMER_GROUP; import static org.apache.rocketmq.broker.metrics.BrokerMetricsConstant.LABEL_IS_SYSTEM; import static org.apache.rocketmq.broker.metrics.BrokerMetricsConstant.LABEL_TOPIC; diff --git a/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManager.java b/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManager.java index 4b0714decb..9e20ecd9b6 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManager.java @@ -16,7 +16,8 @@ */ package org.apache.rocketmq.broker.topic; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import com.google.common.collect.Maps; import java.util.List; import java.util.Map; @@ -156,7 +157,10 @@ public class TopicQueueMappingManager extends ConfigManager { TopicQueueMappingSerializeWrapper wrapper = new TopicQueueMappingSerializeWrapper(); wrapper.setTopicQueueMappingInfoMap(topicQueueMappingTable); wrapper.setDataVersion(this.dataVersion); - return JSON.toJSONString(wrapper, pretty); + if (pretty) { + return JSON.toJSONString(wrapper, JSONWriter.Feature.PrettyFormat); + } + return JSON.toJSONString(wrapper); } @Override diff --git a/broker/src/main/java/org/apache/rocketmq/broker/transaction/TransactionMetrics.java b/broker/src/main/java/org/apache/rocketmq/broker/transaction/TransactionMetrics.java index 8a18218f53..17b0ac6774 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/transaction/TransactionMetrics.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/transaction/TransactionMetrics.java @@ -16,9 +16,19 @@ */ package org.apache.rocketmq.broker.transaction; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import org.apache.rocketmq.common.ConfigManager; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.constant.LoggerName; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.logging.org.slf4j.Logger; +import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; +import org.apache.rocketmq.remoting.protocol.DataVersion; +import org.apache.rocketmq.remoting.protocol.RemotingSerializable; + import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.io.StringWriter; import java.io.Writer; @@ -32,14 +42,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; -import org.apache.rocketmq.common.ConfigManager; -import org.apache.rocketmq.common.MixAll; -import org.apache.rocketmq.common.constant.LoggerName; -import org.apache.rocketmq.common.topic.TopicValidator; -import org.apache.rocketmq.logging.org.slf4j.Logger; -import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; -import org.apache.rocketmq.remoting.protocol.DataVersion; -import org.apache.rocketmq.remoting.protocol.RemotingSerializable; + public class TransactionMetrics extends ConfigManager { private static final Logger log = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME); @@ -90,11 +93,11 @@ public class TransactionMetrics extends ConfigManager { this.transactionCounts = transactionCounts; } - protected void write0(Writer writer) { + protected void write0(Writer writer) throws IOException { TransactionMetricsSerializeWrapper wrapper = new TransactionMetricsSerializeWrapper(); wrapper.setTransactionCount(transactionCounts); wrapper.setDataVersion(dataVersion); - JSON.writeJSONString(writer, wrapper, SerializerFeature.BrowserCompatible); + writer.write(JSON.toJSONString(wrapper, JSONWriter.Feature.BrowserCompatible)); } @Override diff --git a/broker/src/test/java/org/apache/rocketmq/broker/RocksDBConfigManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/RocksDBConfigManagerTest.java new file mode 100644 index 0000000000..1832902a76 --- /dev/null +++ b/broker/src/test/java/org/apache/rocketmq/broker/RocksDBConfigManagerTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.broker; + +import org.apache.rocketmq.broker.config.v1.RocksDBConfigManager; +import org.apache.rocketmq.common.config.ConfigRocksDBStorage; +import org.apache.rocketmq.remoting.protocol.DataVersion; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mock; + +public class RocksDBConfigManagerTest { + + private ConfigRocksDBStorage configRocksDBStorage; + + private RocksDBConfigManager rocksDBConfigManager; + + @Before + public void setUp() throws IllegalAccessException { + configRocksDBStorage = mock(ConfigRocksDBStorage.class); + rocksDBConfigManager = spy(new RocksDBConfigManager("testPath", 1000L, null)); + rocksDBConfigManager.configRocksDBStorage = configRocksDBStorage; + } + + @Test + public void testLoadDataVersion() throws Exception { + DataVersion expected = new DataVersion(); + expected.nextVersion(); + + when(rocksDBConfigManager.getKvDataVersion()).thenReturn(expected); + + boolean result = rocksDBConfigManager.loadDataVersion(); + + assertTrue(result); + assertEquals(expected.getCounter().get(), rocksDBConfigManager.getKvDataVersion().getCounter().get()); + assertEquals(expected.getTimestamp(), rocksDBConfigManager.getKvDataVersion().getTimestamp()); + } + + @Test + public void testUpdateKvDataVersion() throws Exception { + rocksDBConfigManager.updateKvDataVersion(); + + verify(rocksDBConfigManager, times(1)).updateKvDataVersion(); + } +} diff --git a/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java index 1b8293159d..5f2e96015d 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/client/ConsumerManagerTest.java @@ -20,6 +20,7 @@ package org.apache.rocketmq.broker.client; import com.google.common.collect.ImmutableSet; import io.netty.channel.Channel; import org.apache.rocketmq.broker.BrokerController; +import org.apache.rocketmq.broker.client.net.Broker2Client; import org.apache.rocketmq.broker.filter.ConsumerFilterManager; import org.apache.rocketmq.common.BrokerConfig; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; @@ -214,6 +215,9 @@ public class ConsumerManagerTest { @Test public void testRegisterConsumerWithoutSub() { + when(brokerController.getBrokerConfig()).thenReturn(brokerConfig); + Broker2Client broker2Client = mock(Broker2Client.class); + when(brokerController.getBroker2Client()).thenReturn(broker2Client); ConsumerGroupInfo groupInfo = new ConsumerGroupInfo(GROUP, CONSUME_PASSIVELY, MessageModel.CLUSTERING, ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); SubscriptionData subscriptionData = new SubscriptionData(TOPIC, "*"); diff --git a/broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManagerTest.java new file mode 100644 index 0000000000..98d0e34219 --- /dev/null +++ b/broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManagerTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.broker.config.v1; + +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.broker.BrokerController; +import org.apache.rocketmq.common.BrokerConfig; +import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; +import org.apache.rocketmq.store.config.MessageStoreConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ConcurrentMap; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RocksDBSubscriptionGroupManagerTest { + + @Mock + private BrokerController brokerController; + + @Mock + private RocksDBConfigManager rocksDBConfigManager; + + private RocksDBSubscriptionGroupManager rocksDBSubscriptionGroupManager; + + @Mock + private MessageStoreConfig messageStoreConfig; + + @Before + public void init() throws IllegalAccessException { + when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig); + when(messageStoreConfig.getMemTableFlushIntervalMs()).thenReturn(1000L); + when(messageStoreConfig.getRocksdbCompressionType()).thenReturn("LZ4_COMPRESSION"); + when(messageStoreConfig.getStorePathRootDir()).thenReturn("/"); + BrokerConfig brokerConfig = mock(BrokerConfig.class); + when(brokerConfig.isUseSingleRocksDBForAllConfigs()).thenReturn(true); + when(brokerController.getBrokerConfig()).thenReturn(brokerConfig); + rocksDBSubscriptionGroupManager = new RocksDBSubscriptionGroupManager(brokerController); + FieldUtils.writeDeclaredField(rocksDBSubscriptionGroupManager, "rocksDBConfigManager", rocksDBConfigManager, true); + } + + @Test + public void testPutSubscriptionGroupConfig() { + SubscriptionGroupConfig newConfig = new SubscriptionGroupConfig(); + newConfig.setGroupName("group"); + SubscriptionGroupConfig oldConfig = new SubscriptionGroupConfig(); + oldConfig.setGroupName("group"); + rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().put("group", oldConfig); + + assertEquals(oldConfig, rocksDBSubscriptionGroupManager.putSubscriptionGroupConfig(newConfig)); + assertEquals(newConfig, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().get("group")); + } + + @Test + public void testPutSubscriptionGroupConfigIfAbsent() { + SubscriptionGroupConfig newConfig = new SubscriptionGroupConfig(); + newConfig.setGroupName("group"); + SubscriptionGroupConfig oldConfig = new SubscriptionGroupConfig(); + oldConfig.setGroupName("group"); + + assertNull(rocksDBSubscriptionGroupManager.putSubscriptionGroupConfigIfAbsent(newConfig)); + assertEquals(newConfig, rocksDBSubscriptionGroupManager.getSubscriptionGroupTable().get("group")); + } + + @Test + public void testDecodeForbidden() { + String forbiddenGroupName = "group"; + String bodyJson = "{\"topic1\":1,\"topic2\":2}"; + byte[] key = forbiddenGroupName.getBytes(StandardCharsets.UTF_8); + byte[] body = bodyJson.getBytes(StandardCharsets.UTF_8); + + rocksDBSubscriptionGroupManager.decodeForbidden(key, body); + ConcurrentMap> forbiddenTable = rocksDBSubscriptionGroupManager.getForbiddenTable(); + assertTrue(forbiddenTable.containsKey(forbiddenGroupName)); + + ConcurrentMap forbiddenGroup = forbiddenTable.get(forbiddenGroupName); + assertEquals(2, forbiddenGroup.size()); + assertEquals(Integer.valueOf(1), forbiddenGroup.get("topic1")); + assertEquals(Integer.valueOf(2), forbiddenGroup.get("topic2")); + } + + @Test + public void testDecodeSubscriptionGroup() { + String groupName = "group"; + String bodyJson = "{\"groupName\":\"group\",\"consumeEnable\":true}"; + byte[] key = groupName.getBytes(StandardCharsets.UTF_8); + byte[] body = bodyJson.getBytes(StandardCharsets.UTF_8); + + rocksDBSubscriptionGroupManager.decodeSubscriptionGroup(key, body); + ConcurrentMap subscriptionGroupTable = rocksDBSubscriptionGroupManager.getSubscriptionGroupTable(); + assertEquals(1, subscriptionGroupTable.size()); + SubscriptionGroupConfig config = subscriptionGroupTable.get(groupName); + assertEquals(groupName, config.getGroupName()); + assertTrue(config.isConsumeEnable()); + } +} diff --git a/broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManagerTest.java new file mode 100644 index 0000000000..b2b742fb2f --- /dev/null +++ b/broker/src/test/java/org/apache/rocketmq/broker/config/v1/RocksDBTopicConfigManagerTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.broker.config.v1; + +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.broker.BrokerController; +import org.apache.rocketmq.common.BrokerConfig; +import org.apache.rocketmq.common.TopicConfig; +import org.apache.rocketmq.store.config.MessageStoreConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ConcurrentMap; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RocksDBTopicConfigManagerTest { + + @Mock + private BrokerController brokerController; + + @Mock + private MessageStoreConfig messageStoreConfig; + + @Mock + private RocksDBConfigManager rocksDBConfigManager; + + private RocksDBTopicConfigManager rocksDBTopicConfigManager; + + @Before + public void init() throws Exception { + when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig); + when(messageStoreConfig.getMemTableFlushIntervalMs()).thenReturn(1000L); + when(messageStoreConfig.getRocksdbCompressionType()).thenReturn("LZ4_COMPRESSION"); + when(messageStoreConfig.getStorePathRootDir()).thenReturn("/"); + BrokerConfig brokerConfig = mock(BrokerConfig.class); + when(brokerConfig.isUseSingleRocksDBForAllConfigs()).thenReturn(true); + when(brokerController.getBrokerConfig()).thenReturn(brokerConfig); + rocksDBTopicConfigManager = new RocksDBTopicConfigManager(brokerController); + FieldUtils.writeDeclaredField(rocksDBTopicConfigManager, "rocksDBConfigManager", rocksDBConfigManager, true); + } + + @Test + public void testDecodeTopicConfig() { + String topicName = "testTopic"; + String topicConfigJson = "{\"topicName\":\"testTopic\",\"readQueueNums\":10,\"writeQueueNums\":10}"; + byte[] key = topicName.getBytes(StandardCharsets.UTF_8); + byte[] body = topicConfigJson.getBytes(StandardCharsets.UTF_8); + + rocksDBTopicConfigManager.decodeTopicConfig(key, body); + + ConcurrentMap topicConfigTable = rocksDBTopicConfigManager.getTopicConfigTable(); + assertNotNull(topicConfigTable); + assertEquals(1, topicConfigTable.size()); + TopicConfig topicConfig = topicConfigTable.get(topicName); + assertNotNull(topicConfig); + assertEquals(topicName, topicConfig.getTopicName()); + assertEquals(10, topicConfig.getReadQueueNums()); + assertEquals(10, topicConfig.getWriteQueueNums()); + } + + @Test + public void testPutTopicConfig() throws Exception { + TopicConfig newTopicConfig = new TopicConfig("newTopic"); + newTopicConfig.setReadQueueNums(10); + newTopicConfig.setWriteQueueNums(10); + + assertNull(rocksDBTopicConfigManager.putTopicConfig(newTopicConfig)); + verify(rocksDBConfigManager, times(1)).put(any(byte[].class), any(byte[].class)); + } +} diff --git a/broker/src/test/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransferTest.java b/broker/src/test/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransferTest.java new file mode 100644 index 0000000000..a10dd4efca --- /dev/null +++ b/broker/src/test/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransferTest.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.broker.pagecache; + +import org.apache.rocketmq.store.QueryMessageResult; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; +import java.util.ArrayList; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class QueryMessageTransferTest { + + @Mock + private WritableByteChannel writableByteChannel; + + @Mock + private QueryMessageResult queryMessageResult; + + private QueryMessageTransfer queryMessageTransfer; + + private ByteBuffer byteBufferHeader; + + private ByteBuffer bb1; + + private ByteBuffer bb2; + + @Before + public void init() { + byteBufferHeader = ByteBuffer.allocate(4); + byteBufferHeader.putInt(1); + byteBufferHeader.flip(); + + bb1 = ByteBuffer.allocate(4); + bb1.putInt(2); + bb1.flip(); + + bb2 = ByteBuffer.allocate(4); + bb2.putInt(3); + bb2.flip(); + + when(queryMessageResult.getMessageBufferList()).thenReturn(Arrays.asList(bb1, bb2)); + + queryMessageTransfer = new QueryMessageTransfer(byteBufferHeader, queryMessageResult); + } + + @Test + public void testPosition_WithHeaderAndMessageBuffers() { + byteBufferHeader.position(2); + bb1.position(1); + bb2.position(3); + + long actual = queryMessageTransfer.position(); + + long expected = byteBufferHeader.position() + bb1.position() + bb2.position(); + assertEquals(expected, actual); + } + + @Test + public void testPosition_WithHeaderOnly() { + byteBufferHeader.position(2); + + when(queryMessageResult.getMessageBufferList()).thenReturn(new ArrayList<>()); + + long actual = queryMessageTransfer.position(); + + long expected = byteBufferHeader.position(); + assertEquals(expected, actual); + } + + @Test + public void testPosition_WithMessageBuffersOnly() { + byteBufferHeader.clear(); + byteBufferHeader.flip(); + + bb1.position(1); + bb2.position(3); + + long actual = queryMessageTransfer.position(); + + long expected = bb1.position() + bb2.position(); + assertEquals(expected, actual); + } + + @Test + public void testTransferTo_OnlyHeaderData() throws Exception { + bb1.clear(); + bb2.clear(); + + when(writableByteChannel.write(byteBufferHeader)).thenReturn(4); + + long actual = queryMessageTransfer.transferTo(writableByteChannel, 0); + + assertEquals(4, actual); + verify(writableByteChannel, times(1)).write(byteBufferHeader); + verify(writableByteChannel, never()).write(bb1); + verify(writableByteChannel, never()).write(bb2); + } + + @Test + public void testTransferTo_OnlyMessageBuffersData() throws Exception { + byteBufferHeader.clear(); + byteBufferHeader.flip(); + + when(writableByteChannel.write(bb1)).thenReturn(4); + + long actual = queryMessageTransfer.transferTo(writableByteChannel, 0); + + assertEquals(4, actual); + verify(writableByteChannel, never()).write(byteBufferHeader); + verify(writableByteChannel, times(1)).write(bb1); + } +} diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java index 1bf99eadfb..572be63e3f 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessorTest.java @@ -16,7 +16,8 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -37,11 +38,11 @@ import org.apache.rocketmq.broker.client.ClientChannelInfo; import org.apache.rocketmq.broker.client.ConsumerGroupInfo; import org.apache.rocketmq.broker.client.ConsumerManager; import org.apache.rocketmq.broker.client.net.Broker2Client; +import org.apache.rocketmq.broker.config.v1.RocksDBSubscriptionGroupManager; +import org.apache.rocketmq.broker.config.v1.RocksDBTopicConfigManager; import org.apache.rocketmq.broker.metrics.BrokerMetricsManager; import org.apache.rocketmq.broker.offset.ConsumerOffsetManager; import org.apache.rocketmq.broker.schedule.ScheduleMessageService; -import org.apache.rocketmq.broker.config.v1.RocksDBSubscriptionGroupManager; -import org.apache.rocketmq.broker.config.v1.RocksDBTopicConfigManager; import org.apache.rocketmq.broker.topic.TopicConfigManager; import org.apache.rocketmq.common.BoundaryType; import org.apache.rocketmq.common.BrokerConfig; @@ -80,6 +81,7 @@ import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper; import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper; import org.apache.rocketmq.remoting.protocol.body.UnlockBatchRequestBody; import org.apache.rocketmq.remoting.protocol.body.UserInfo; +import org.apache.rocketmq.remoting.protocol.header.CheckRocksdbCqWriteProgressRequestHeader; import org.apache.rocketmq.remoting.protocol.header.CreateAclRequestHeader; import org.apache.rocketmq.remoting.protocol.header.CreateTopicRequestHeader; import org.apache.rocketmq.remoting.protocol.header.CreateUserRequestHeader; @@ -97,11 +99,13 @@ import org.apache.rocketmq.remoting.protocol.header.GetConsumerStatusRequestHead import org.apache.rocketmq.remoting.protocol.header.GetEarliestMsgStoretimeRequestHeader; import org.apache.rocketmq.remoting.protocol.header.GetMaxOffsetRequestHeader; import org.apache.rocketmq.remoting.protocol.header.GetMinOffsetRequestHeader; +import org.apache.rocketmq.remoting.protocol.header.GetSubscriptionGroupConfigRequestHeader; import org.apache.rocketmq.remoting.protocol.header.GetTopicConfigRequestHeader; import org.apache.rocketmq.remoting.protocol.header.GetUserRequestHeader; import org.apache.rocketmq.remoting.protocol.header.ListAclsRequestHeader; import org.apache.rocketmq.remoting.protocol.header.ListUsersRequestHeader; import org.apache.rocketmq.remoting.protocol.header.NotifyMinBrokerIdChangeRequestHeader; +import org.apache.rocketmq.remoting.protocol.header.QueryConsumeQueueRequestHeader; import org.apache.rocketmq.remoting.protocol.header.QueryCorrectionOffsetHeader; import org.apache.rocketmq.remoting.protocol.header.QuerySubscriptionByConsumerRequestHeader; import org.apache.rocketmq.remoting.protocol.header.QueryTopicConsumeByWhoRequestHeader; @@ -114,6 +118,8 @@ import org.apache.rocketmq.remoting.protocol.header.UpdateAclRequestHeader; import org.apache.rocketmq.remoting.protocol.header.UpdateUserRequestHeader; import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType; import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel; +import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData; +import org.apache.rocketmq.remoting.protocol.statictopic.TopicConfigAndQueueMapping; import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; import org.apache.rocketmq.store.CommitLog; import org.apache.rocketmq.store.DefaultMessageStore; @@ -121,6 +127,7 @@ import org.apache.rocketmq.store.MessageStore; import org.apache.rocketmq.store.SelectMappedBufferResult; import org.apache.rocketmq.store.config.MessageStoreConfig; import org.apache.rocketmq.store.logfile.DefaultMappedFile; +import org.apache.rocketmq.store.queue.ConsumeQueueInterface; import org.apache.rocketmq.store.stats.BrokerStats; import org.apache.rocketmq.store.timer.TimerCheckpoint; import org.apache.rocketmq.store.timer.TimerMessageStore; @@ -159,6 +166,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.LongAdder; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -889,7 +898,7 @@ public class AdminBrokerProcessorTest { consumerOffsetManager = mock(ConsumerOffsetManager.class); when(brokerController.getConsumerOffsetManager()).thenReturn(consumerOffsetManager); ConsumerOffsetManager consumerOffset = new ConsumerOffsetManager(); - when(consumerOffsetManager.encode()).thenReturn(JSON.toJSONString(consumerOffset, false)); + when(consumerOffsetManager.encode()).thenReturn(JSON.toJSONString(consumerOffset)); RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_CONSUMER_OFFSET, null); RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request); assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); @@ -1500,6 +1509,73 @@ public class AdminBrokerProcessorTest { assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS); } + @Test + public void testGetSubscriptionGroup() throws RemotingCommandException { + brokerController.getSubscriptionGroupManager().getSubscriptionGroupTable().put("group", new SubscriptionGroupConfig()); + GetSubscriptionGroupConfigRequestHeader requestHeader = new GetSubscriptionGroupConfigRequestHeader(); + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_SUBSCRIPTIONGROUP_CONFIG, requestHeader); + requestHeader.setGroup("group"); + request.makeCustomHeaderToNet(); + RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + } + + @Test + public void testCheckRocksdbCqWriteProgress() throws RemotingCommandException { + CheckRocksdbCqWriteProgressRequestHeader requestHeader = new CheckRocksdbCqWriteProgressRequestHeader(); + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.CHECK_ROCKSDB_CQ_WRITE_PROGRESS, requestHeader); + requestHeader.setTopic("topic"); + request.makeCustomHeaderToNet(); + RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + } + + @Test + public void testQueryConsumeQueue() throws RemotingCommandException { + messageStore = mock(MessageStore.class); + ConsumeQueueInterface consumeQueue = mock(ConsumeQueueInterface.class); + when(consumeQueue.getMinOffsetInQueue()).thenReturn(0L); + when(consumeQueue.getMaxOffsetInQueue()).thenReturn(1L); + when(messageStore.getConsumeQueue(anyString(), anyInt())).thenReturn(consumeQueue); + when(brokerController.getMessageStore()).thenReturn(messageStore); + QueryConsumeQueueRequestHeader requestHeader = new QueryConsumeQueueRequestHeader(); + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_CONSUME_QUEUE, requestHeader); + requestHeader.setTopic("topic"); + requestHeader.setQueueId(0); + requestHeader.setConsumerGroup("testGroup"); + request.makeCustomHeaderToNet(); + SubscriptionData subscriptionData = mock(SubscriptionData.class); + when(brokerController.getConsumerManager()).thenReturn(consumerManager); + when(consumerManager.findSubscriptionData(any(), any())).thenReturn(subscriptionData); + RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + } + + @Test + public void testProcessRequest_GetTopicConfig() throws Exception { + GetTopicConfigRequestHeader requestHeader = new GetTopicConfigRequestHeader(); + requestHeader.setTopic("testTopic"); + + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_TOPIC_CONFIG, requestHeader); + request.makeCustomHeaderToNet(); + + TopicConfig topicConfig = new TopicConfig(); + topicConfig.setTopicName("testTopic"); + TopicConfigManager topicConfigManager = mock(TopicConfigManager.class); + when(brokerController.getTopicConfigManager()).thenReturn(topicConfigManager); + when(topicConfigManager.selectTopicConfig("testTopic")) + .thenReturn(topicConfig); + + RemotingCommand response = adminBrokerProcessor.processRequest(handlerContext, request); + + assertNotNull(response); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + + String responseBody = new String(response.getBody(), StandardCharsets.UTF_8); + TopicConfigAndQueueMapping result = JSONObject.parseObject(responseBody, TopicConfigAndQueueMapping.class); + assertEquals("testTopic", result.getTopicName()); + } + private ResetOffsetRequestHeader createRequestHeader(String topic,String group,long timestamp,boolean force,long offset,int queueId) { ResetOffsetRequestHeader requestHeader = new ResetOffsetRequestHeader(); requestHeader.setTopic(topic); diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessorTest.java index ca2529e319..7afd338dca 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessorTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessorTest.java @@ -18,18 +18,15 @@ package org.apache.rocketmq.broker.processor; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import java.lang.reflect.Field; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.client.ClientChannelInfo; import org.apache.rocketmq.broker.client.net.Broker2Client; import org.apache.rocketmq.broker.failover.EscapeBridge; import org.apache.rocketmq.broker.metrics.BrokerMetricsManager; -import org.apache.rocketmq.store.stats.BrokerStatsManager; +import org.apache.rocketmq.broker.metrics.PopMetricsManager; +import org.apache.rocketmq.broker.topic.TopicConfigManager; import org.apache.rocketmq.common.BrokerConfig; import org.apache.rocketmq.common.TopicConfig; -import org.apache.rocketmq.broker.topic.TopicConfigManager; import org.apache.rocketmq.common.message.MessageConst; import org.apache.rocketmq.common.message.MessageExtBrokerInner; import org.apache.rocketmq.remoting.exception.RemotingCommandException; @@ -41,13 +38,16 @@ import org.apache.rocketmq.remoting.protocol.RequestCode; import org.apache.rocketmq.remoting.protocol.ResponseCode; import org.apache.rocketmq.remoting.protocol.header.ChangeInvisibleTimeRequestHeader; import org.apache.rocketmq.remoting.protocol.header.ExtraInfoUtil; +import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumerData; import org.apache.rocketmq.store.AppendMessageResult; import org.apache.rocketmq.store.AppendMessageStatus; import org.apache.rocketmq.store.DefaultMessageStore; +import org.apache.rocketmq.store.MessageStore; import org.apache.rocketmq.store.PutMessageResult; import org.apache.rocketmq.store.PutMessageStatus; import org.apache.rocketmq.store.config.MessageStoreConfig; import org.apache.rocketmq.store.exception.ConsumeQueueException; +import org.apache.rocketmq.store.stats.BrokerStatsManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,10 +55,18 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import java.lang.reflect.Field; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import static org.apache.rocketmq.broker.processor.PullMessageProcessorTest.createConsumerData; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -118,6 +126,17 @@ public class ChangeInvisibleTimeProcessorTest { PopBufferMergeService popBufferMergeService = mock(PopBufferMergeService.class); when(brokerController.getPopMessageProcessor()).thenReturn(popMessageProcessor); when(popMessageProcessor.getPopBufferMergeService()).thenReturn(popBufferMergeService); + + ConsumerData consumerData = createConsumerData(group, topic); + clientInfo = new ClientChannelInfo(channel, "127.0.0.1", LanguageCode.JAVA, 0); + brokerController.getConsumerManager().registerConsumer( + consumerData.getGroupName(), + clientInfo, + consumerData.getConsumeType(), + consumerData.getMessageModel(), + consumerData.getConsumeFromWhere(), + consumerData.getSubscriptionDataSet(), + false); clientInfo = new ClientChannelInfo(channel, "127.0.0.1", LanguageCode.JAVA, 0); changeInvisibleTimeProcessor = new ChangeInvisibleTimeProcessor(brokerController); @@ -177,4 +196,58 @@ public class ChangeInvisibleTimeProcessorTest { assertThat(responseToReturn.getCode()).isEqualTo(ResponseCode.NO_MESSAGE); assertThat(responseToReturn.getOpaque()).isEqualTo(request.getOpaque()); } + + @Test + public void testProcessRequestAsync_JsonParsing() throws Exception { + Channel mockChannel = mock(Channel.class); + RemotingCommand mockRequest = mock(RemotingCommand.class); + BrokerController mockBrokerController = mock(BrokerController.class); + TopicConfigManager mockTopicConfigManager = mock(TopicConfigManager.class); + MessageStore mockMessageStore = mock(MessageStore.class); + BrokerConfig mockBrokerConfig = mock(BrokerConfig.class); + BrokerStatsManager mockBrokerStatsManager = mock(BrokerStatsManager.class); + PopMessageProcessor mockPopMessageProcessor = mock(PopMessageProcessor.class); + PopBufferMergeService mockPopBufferMergeService = mock(PopBufferMergeService.class); + BrokerMetricsManager brokerMetricsManager = mock(BrokerMetricsManager.class); + PopMetricsManager popMetricsManager = mock(PopMetricsManager.class); + + when(brokerMetricsManager.getPopMetricsManager()).thenReturn(popMetricsManager); + when(mockBrokerController.getBrokerMetricsManager()).thenReturn(brokerMetricsManager); + doNothing().when(popMetricsManager).incPopReviveCkPutCount(any(), any()); + when(brokerMetricsManager.getPopMetricsManager()).thenReturn(popMetricsManager); + when(mockBrokerController.getTopicConfigManager()).thenReturn(mockTopicConfigManager); + when(mockBrokerController.getMessageStore()).thenReturn(mockMessageStore); + when(mockBrokerController.getBrokerConfig()).thenReturn(mockBrokerConfig); + when(mockBrokerController.getBrokerStatsManager()).thenReturn(mockBrokerStatsManager); + when(mockBrokerController.getPopMessageProcessor()).thenReturn(mockPopMessageProcessor); + when(mockPopMessageProcessor.getPopBufferMergeService()).thenReturn(mockPopBufferMergeService); + when(mockPopBufferMergeService.addAk(anyInt(), any())).thenReturn(false); + when(mockBrokerController.getEscapeBridge()).thenReturn(escapeBridge); + PutMessageResult mockPutMessageResult = new PutMessageResult(PutMessageStatus.PUT_OK, null, true); + when(mockBrokerController.getEscapeBridge().asyncPutMessageToSpecificQueue(any())) + .thenReturn(CompletableFuture.completedFuture(mockPutMessageResult)); + + TopicConfig topicConfig = new TopicConfig(); + topicConfig.setReadQueueNums(4); + when(mockTopicConfigManager.selectTopicConfig(anyString())).thenReturn(topicConfig); + when(mockMessageStore.getMinOffsetInQueue(anyString(), anyInt())).thenReturn(0L); + when(mockMessageStore.getMaxOffsetInQueue(anyString(), anyInt())).thenReturn(10L); + when(mockBrokerConfig.isPopConsumerKVServiceEnable()).thenReturn(false); + + ChangeInvisibleTimeRequestHeader requestHeader = new ChangeInvisibleTimeRequestHeader(); + requestHeader.setTopic("TestTopic"); + requestHeader.setQueueId(1); + requestHeader.setOffset(5L); + requestHeader.setConsumerGroup("TestGroup"); + requestHeader.setExtraInfo("0 10000 10000 0 TestBroker 1"); + requestHeader.setInvisibleTime(60000L); + when(mockRequest.decodeCommandCustomHeader(ChangeInvisibleTimeRequestHeader.class)).thenReturn(requestHeader); + + ChangeInvisibleTimeProcessor processor = new ChangeInvisibleTimeProcessor(mockBrokerController); + CompletableFuture futureResponse = processor.processRequestAsync(mockChannel, mockRequest, true); + + RemotingCommand response = futureResponse.get(); + assertNotNull(response); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + } } diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopBufferMergeServiceTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopBufferMergeServiceTest.java index acc7a3da74..6cbbd9cfd9 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopBufferMergeServiceTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopBufferMergeServiceTest.java @@ -16,19 +16,22 @@ */ package org.apache.rocketmq.broker.processor; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; +import com.alibaba.fastjson2.JSON; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.broker.BrokerController; -import org.apache.rocketmq.broker.client.ClientChannelInfo; +import org.apache.rocketmq.broker.client.ConsumerManager; +import org.apache.rocketmq.broker.failover.EscapeBridge; +import org.apache.rocketmq.broker.metrics.BrokerMetricsManager; +import org.apache.rocketmq.broker.metrics.PopMetricsManager; import org.apache.rocketmq.broker.schedule.ScheduleMessageService; +import org.apache.rocketmq.broker.topic.TopicConfigManager; import org.apache.rocketmq.common.BrokerConfig; import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.TopicConfig; -import org.apache.rocketmq.remoting.netty.NettyClientConfig; -import org.apache.rocketmq.remoting.netty.NettyServerConfig; -import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumerData; +import org.apache.rocketmq.common.message.MessageExtBrokerInner; import org.apache.rocketmq.store.DefaultMessageStore; +import org.apache.rocketmq.store.PutMessageResult; +import org.apache.rocketmq.store.PutMessageStatus; import org.apache.rocketmq.store.config.MessageStoreConfig; import org.apache.rocketmq.store.pop.AckMsg; import org.apache.rocketmq.store.pop.PopCheckPoint; @@ -37,56 +40,83 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; -import static org.apache.rocketmq.broker.processor.PullMessageProcessorTest.createConsumerData; +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class PopBufferMergeServiceTest { - @Spy - private BrokerController brokerController = new BrokerController(new BrokerConfig(), new NettyServerConfig(), new NettyClientConfig(), new MessageStoreConfig()); + @Mock + private BrokerController brokerController; + private PopMessageProcessor popMessageProcessor; + @Mock - private ChannelHandlerContext handlerContext; + private ScheduleMessageService scheduleMessageService; + + @Mock + private TopicConfigManager topicConfigManager; + + @Mock + private ConsumerManager consumerManager; + @Mock private DefaultMessageStore messageStore; - private ScheduleMessageService scheduleMessageService; - private ClientChannelInfo clientChannelInfo; - private String group = "FooBarGroup"; - private String topic = "FooBar"; + + @Mock + private MessageStoreConfig messageStoreConfig; + + private String defaultGroup = "defaultGroup"; + + private String defaultTopic = "defaultTopic"; + + private PopBufferMergeService popBufferMergeService; + + @Mock + private BrokerConfig brokerConfig; + + @Mock + private EscapeBridge escapeBridge; @Before public void init() throws Exception { - FieldUtils.writeField(brokerController.getBrokerConfig(), "enablePopBufferMerge", true, true); - brokerController.setMessageStore(messageStore); + when(brokerConfig.getBrokerIP1()).thenReturn("127.0.0.1"); + when(brokerConfig.isEnablePopBufferMerge()).thenReturn(true); + when(brokerConfig.getPopCkStayBufferTime()).thenReturn(10 * 1000); + when(brokerController.getBrokerConfig()).thenReturn(brokerConfig); + when(brokerController.getEscapeBridge()).thenReturn(escapeBridge); + when(brokerController.getMessageStore()).thenReturn(messageStore); + when(brokerController.getTopicConfigManager()).thenReturn(topicConfigManager); + when(brokerController.getScheduleMessageService()).thenReturn(scheduleMessageService); + when(brokerController.getConsumerManager()).thenReturn(consumerManager); + when(brokerController.getMessageStoreConfig()).thenReturn(messageStoreConfig); popMessageProcessor = new PopMessageProcessor(brokerController); - scheduleMessageService = new ScheduleMessageService(brokerController); - scheduleMessageService.parseDelayLevel(); - Channel mockChannel = mock(Channel.class); - brokerController.getTopicConfigManager().getTopicConfigTable().put(topic, new TopicConfig()); - clientChannelInfo = new ClientChannelInfo(mockChannel); - ConsumerData consumerData = createConsumerData(group, topic); - brokerController.getConsumerManager().registerConsumer( - consumerData.getGroupName(), - clientChannelInfo, - consumerData.getConsumeType(), - consumerData.getMessageModel(), - consumerData.getConsumeFromWhere(), - consumerData.getSubscriptionDataSet(), - false); + popBufferMergeService = new PopBufferMergeService(brokerController, popMessageProcessor); + FieldUtils.writeDeclaredField(popBufferMergeService, "brokerController", brokerController, true); + ConcurrentMap topicConfigTable = new ConcurrentHashMap<>(); + topicConfigTable.put(defaultTopic, new TopicConfig()); + when(topicConfigManager.getTopicConfigTable()).thenReturn(topicConfigTable); } - @Test(timeout = 10_000) + @Test(timeout = 15_000) public void testBasic() throws Exception { // This test case fails on Windows in CI pipeline // Disable it for later fix Assume.assumeFalse(MixAll.isWindows()); - PopBufferMergeService popBufferMergeService = new PopBufferMergeService(brokerController, popMessageProcessor); - popBufferMergeService.start(); PopCheckPoint ck = new PopCheckPoint(); ck.setBitMap(0); int msgCnt = 1; @@ -97,8 +127,8 @@ public class PopBufferMergeServiceTest { ck.setInvisibleTime(invisibleTime); int offset = 100; ck.setStartOffset(offset); - ck.setCId(group); - ck.setTopic(topic); + ck.setCId(defaultGroup); + ck.setTopic(defaultTopic); int queueId = 0; ck.setQueueId(queueId); @@ -108,18 +138,100 @@ public class PopBufferMergeServiceTest { AckMsg ackMsg = new AckMsg(); ackMsg.setAckOffset(ackOffset); ackMsg.setStartOffset(offset); - ackMsg.setConsumerGroup(group); - ackMsg.setTopic(topic); + ackMsg.setConsumerGroup(defaultGroup); + ackMsg.setTopic(defaultTopic); ackMsg.setQueueId(queueId); ackMsg.setPopTime(popTime); try { assertThat(popBufferMergeService.addCk(ck, reviveQid, ackOffset, nextBeginOffset)).isTrue(); - assertThat(popBufferMergeService.getLatestOffset(topic, group, queueId)).isEqualTo(nextBeginOffset); + assertThat(popBufferMergeService.getLatestOffset(defaultTopic, defaultGroup, queueId)).isEqualTo(nextBeginOffset); Thread.sleep(1000); // wait background threads of PopBufferMergeService run for some time assertThat(popBufferMergeService.addAk(reviveQid, ackMsg)).isTrue(); - assertThat(popBufferMergeService.getLatestOffset(topic, group, queueId)).isEqualTo(nextBeginOffset); + assertThat(popBufferMergeService.getLatestOffset(defaultTopic, defaultGroup, queueId)).isEqualTo(nextBeginOffset); } finally { popBufferMergeService.shutdown(true); } } + + @Test + public void testAddCkJustOffset_MergeKeyConflict() { + PopCheckPoint point = mock(PopCheckPoint.class); + String mergeKey = "testMergeKey"; + when(point.getTopic()).thenReturn(mergeKey); + when(point.getCId()).thenReturn(""); + when(point.getQueueId()).thenReturn(0); + when(point.getStartOffset()).thenReturn(0L); + when(point.getPopTime()).thenReturn(0L); + when(point.getBrokerName()).thenReturn(""); + popBufferMergeService.buffer.put(mergeKey + "000", mock(PopBufferMergeService.PopCheckPointWrapper.class)); + + assertFalse(popBufferMergeService.addCkJustOffset(point, 0, 0, 0)); + } + + @Test + public void testAddCkMock() { + int queueId = 0; + long startOffset = 100L; + long invisibleTime = 30_000L; + long popTime = System.currentTimeMillis(); + int reviveQueueId = 0; + long nextBeginOffset = 101L; + String brokerName = "brokerName"; + popBufferMergeService.addCkMock(defaultGroup, defaultTopic, queueId, startOffset, invisibleTime, popTime, reviveQueueId, nextBeginOffset, brokerName); + verify(brokerConfig, times(1)).isEnablePopLog(); + } + + @Test + public void testPutAckToStore() throws Exception { + PopCheckPoint point = new PopCheckPoint(); + point.setStartOffset(100L); + point.setCId("testGroup"); + point.setTopic("testTopic"); + point.setQueueId(1); + point.setPopTime(System.currentTimeMillis()); + point.setBrokerName("testBroker"); + + PopBufferMergeService.PopCheckPointWrapper pointWrapper = mock(PopBufferMergeService.PopCheckPointWrapper.class); + when(pointWrapper.getCk()).thenReturn(point); + when(pointWrapper.getReviveQueueId()).thenReturn(0); + + AtomicInteger toStoreBits = new AtomicInteger(0); + when(pointWrapper.getToStoreBits()).thenReturn(toStoreBits); + + byte msgIndex = 0; + AtomicInteger count = new AtomicInteger(0); + + EscapeBridge escapeBridge = mock(EscapeBridge.class); + when(brokerController.getEscapeBridge()).thenReturn(escapeBridge); + when(brokerController.getBrokerConfig().isAppendAckAsync()).thenReturn(false); + BrokerMetricsManager brokerMetricsManager = mock(BrokerMetricsManager.class); + PopMetricsManager popMetricsManager = mock(PopMetricsManager.class); + + when(brokerMetricsManager.getPopMetricsManager()).thenReturn(popMetricsManager); + when(brokerController.getBrokerMetricsManager()).thenReturn(brokerMetricsManager); + doNothing().when(popMetricsManager).incPopReviveCkPutCount(any(), any()); + when(brokerMetricsManager.getPopMetricsManager()).thenReturn(popMetricsManager); + + when(escapeBridge.putMessageToSpecificQueue(any())).thenAnswer(invocation -> { + MessageExtBrokerInner capturedMessage = invocation.getArgument(0); + AckMsg ackMsg = JSON.parseObject(capturedMessage.getBody(), AckMsg.class); + + assertEquals(point.ackOffsetByIndex(msgIndex), ackMsg.getAckOffset()); + assertEquals(point.getStartOffset(), ackMsg.getStartOffset()); + assertEquals(point.getCId(), ackMsg.getConsumerGroup()); + assertEquals(point.getTopic(), ackMsg.getTopic()); + assertEquals(point.getQueueId(), ackMsg.getQueueId()); + assertEquals(point.getPopTime(), ackMsg.getPopTime()); + assertEquals(point.getBrokerName(), ackMsg.getBrokerName()); + + PutMessageResult result = mock(PutMessageResult.class); + when(result.getPutMessageStatus()).thenReturn(PutMessageStatus.PUT_OK); + return result; + }); + + Method method = PopBufferMergeService.class.getDeclaredMethod("putAckToStore", PopBufferMergeService.PopCheckPointWrapper.class, byte.class, AtomicInteger.class); + method.setAccessible(true); + method.invoke(popBufferMergeService, pointWrapper, msgIndex, count); + verify(escapeBridge, times(1)).putMessageToSpecificQueue(any(MessageExtBrokerInner.class)); + } } diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java index 59559d3cfd..5d7b97f229 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopMessageProcessorTest.java @@ -16,10 +16,9 @@ */ package org.apache.rocketmq.broker.processor; +import com.alibaba.fastjson2.JSON; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.embedded.EmbeddedChannel; -import java.nio.ByteBuffer; -import java.util.concurrent.CompletableFuture; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.client.ClientChannelInfo; import org.apache.rocketmq.broker.metrics.BrokerMetricsManager; @@ -28,6 +27,7 @@ import org.apache.rocketmq.common.KeyBuilder; import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.common.constant.ConsumeInitMode; import org.apache.rocketmq.common.message.MessageDecoder; +import org.apache.rocketmq.common.message.MessageExtBrokerInner; import org.apache.rocketmq.remoting.exception.RemotingCommandException; import org.apache.rocketmq.remoting.netty.NettyClientConfig; import org.apache.rocketmq.remoting.netty.NettyServerConfig; @@ -43,7 +43,7 @@ import org.apache.rocketmq.store.SelectMappedBufferResult; import org.apache.rocketmq.store.config.MessageStoreConfig; import org.apache.rocketmq.store.exception.ConsumeQueueException; import org.apache.rocketmq.store.logfile.DefaultMappedFile; -import org.junit.Assert; +import org.apache.rocketmq.store.pop.PopCheckPoint; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,8 +51,13 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; + import static org.apache.rocketmq.broker.processor.PullMessageProcessorTest.createConsumerData; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -171,17 +176,17 @@ public class PopMessageProcessorTest { .thenReturn(CompletableFuture.completedFuture(getMessageResult)); long offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, retryTopic, 0); - Assert.assertEquals(-1, offset); + assertEquals(-1, offset); RemotingCommand request = createPopMsgCommand(newGroup, topic, 0, ConsumeInitMode.MAX); popMessageProcessor.processRequest(handlerContext, request); offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, retryTopic, 0); - Assert.assertEquals(minOffset, offset); + assertEquals(minOffset, offset); when(messageStore.getMinOffsetInQueue(retryTopic, 0)).thenReturn(minOffset * 2); popMessageProcessor.processRequest(handlerContext, request); offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, retryTopic, 0); - Assert.assertEquals(minOffset, offset); // will not entry getInitOffset() again + assertEquals(minOffset, offset); // will not entry getInitOffset() again messageStore.getMinOffsetInQueue(retryTopic, 0); // prevent UnnecessaryStubbingException } @@ -196,20 +201,47 @@ public class PopMessageProcessorTest { .thenReturn(CompletableFuture.completedFuture(getMessageResult)); long offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, topic, 0); - Assert.assertEquals(-1, offset); + assertEquals(-1, offset); RemotingCommand request = createPopMsgCommand(newGroup, topic, 0, ConsumeInitMode.MAX); popMessageProcessor.processRequest(handlerContext, request); offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, topic, 0); - Assert.assertEquals(maxOffset - 1, offset); // checkInMem return false + assertEquals(maxOffset - 1, offset); // checkInMem return false when(messageStore.getMaxOffsetInQueue(topic, 0)).thenReturn(maxOffset * 2); popMessageProcessor.processRequest(handlerContext, request); offset = brokerController.getConsumerOffsetManager().queryOffset(newGroup, topic, 0); - Assert.assertEquals(maxOffset - 1, offset); // will not entry getInitOffset() again + assertEquals(maxOffset - 1, offset); // will not entry getInitOffset() again messageStore.getMaxOffsetInQueue(topic, 0); // prevent UnnecessaryStubbingException } + @Test + public void testBuildCkMsgJsonParsing() { + PopCheckPoint ck = new PopCheckPoint(); + ck.setTopic("TestTopic"); + ck.setQueueId(1); + ck.setStartOffset(100L); + ck.setCId("TestConsumer"); + ck.setPopTime(System.currentTimeMillis()); + ck.setBrokerName("TestBroker"); + + int reviveQid = 0; + PopMessageProcessor processor = new PopMessageProcessor(brokerController); + + MessageExtBrokerInner result = processor.buildCkMsg(ck, reviveQid); + + String jsonBody = new String(result.getBody(), StandardCharsets.UTF_8); + PopCheckPoint actual = JSON.parseObject(jsonBody, PopCheckPoint.class); + + assertEquals(ck.getTopic(), actual.getTopic()); + assertEquals(ck.getQueueId(), actual.getQueueId()); + assertEquals(ck.getStartOffset(), actual.getStartOffset()); + assertEquals(ck.getCId(), actual.getCId()); + assertEquals(ck.getPopTime(), actual.getPopTime()); + assertEquals(ck.getBrokerName(), actual.getBrokerName()); + assertEquals(ck.getReviveTime(), actual.getReviveTime()); + } + private RemotingCommand createPopMsgCommand() { return createPopMsgCommand(group, topic, -1, ConsumeInitMode.MAX); } diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java index 6e77e7c557..fa7e9982e1 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PopReviveServiceTest.java @@ -16,13 +16,7 @@ */ package org.apache.rocketmq.broker.processor; -import com.alibaba.fastjson.JSON; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; +import com.alibaba.fastjson2.JSON; import org.apache.commons.lang3.tuple.Triple; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.broker.failover.EscapeBridge; @@ -42,12 +36,13 @@ import org.apache.rocketmq.common.message.MessageExtBrokerInner; import org.apache.rocketmq.common.utils.DataConverter; import org.apache.rocketmq.common.utils.NetworkUtil; import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; +import org.apache.rocketmq.store.AppendMessageResult; +import org.apache.rocketmq.store.AppendMessageStatus; import org.apache.rocketmq.store.MessageStore; import org.apache.rocketmq.store.PutMessageResult; import org.apache.rocketmq.store.PutMessageStatus; -import org.apache.rocketmq.store.AppendMessageResult; -import org.apache.rocketmq.store.AppendMessageStatus; import org.apache.rocketmq.store.pop.AckMsg; +import org.apache.rocketmq.store.pop.BatchAckMsg; import org.apache.rocketmq.store.pop.PopCheckPoint; import org.apache.rocketmq.store.timer.TimerMessageStore; import org.junit.Assert; @@ -58,18 +53,27 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class PopReviveServiceTest { @@ -139,7 +143,7 @@ public class PopReviveServiceTest { long maxReviveOffset = 4; when(consumerOffsetManager.queryOffset(PopAckConstants.REVIVE_GROUP, REVIVE_TOPIC, REVIVE_QUEUE_ID)) - .thenReturn(0L); + .thenReturn(0L); List reviveMessageExtList = new ArrayList<>(); long basePopTime = System.currentTimeMillis(); { @@ -176,7 +180,7 @@ public class PopReviveServiceTest { long maxReviveOffset = 4; when(consumerOffsetManager.queryOffset(PopAckConstants.REVIVE_GROUP, REVIVE_TOPIC, REVIVE_QUEUE_ID)) - .thenReturn(0L); + .thenReturn(0L); List reviveMessageExtList = new ArrayList<>(); long basePopTime = System.currentTimeMillis() - brokerConfig.getReviveAckWaitMs() * 2; { @@ -213,7 +217,7 @@ public class PopReviveServiceTest { long maxReviveOffset = 4; when(consumerOffsetManager.queryOffset(PopAckConstants.REVIVE_GROUP, REVIVE_TOPIC, REVIVE_QUEUE_ID)) - .thenReturn(0L); + .thenReturn(0L); List reviveMessageExtList = new ArrayList<>(); long basePopTime = System.currentTimeMillis() - brokerConfig.getReviveAckWaitMs() * 2; { @@ -419,6 +423,59 @@ public class PopReviveServiceTest { verify(messageStore, times(1)).putMessage(any(MessageExtBrokerInner.class)); // rewrite CK } + @Test + public void testReviveMsgFromBatchAck() throws Throwable { + brokerConfig.setEnableSkipLongAwaitingAck(true); + when(consumerOffsetManager.queryOffset(PopAckConstants.REVIVE_GROUP, REVIVE_TOPIC, REVIVE_QUEUE_ID)).thenReturn(0L); + List reviveMessageExtList = new ArrayList<>(); + long basePopTime = System.currentTimeMillis(); + reviveMessageExtList.add(buildBatchAckMsg(buildBatchAckMsg(Arrays.asList(1L, 2L, 3L), basePopTime), 1, 1, basePopTime)); + doReturn(reviveMessageExtList, new ArrayList<>()).when(popReviveService).getReviveMessage(anyLong(), anyInt()); + + PopReviveService.ConsumeReviveObj consumeReviveObj = new PopReviveService.ConsumeReviveObj(); + popReviveService.consumeReviveMessage(consumeReviveObj); + assertEquals(1, consumeReviveObj.map.size()); + + ArgumentCaptor commitOffsetCaptor = ArgumentCaptor.forClass(Long.class); + doNothing().when(consumerOffsetManager).commitOffset(anyString(), anyString(), anyString(), anyInt(), commitOffsetCaptor.capture()); + popReviveService.mergeAndRevive(consumeReviveObj); + assertEquals(1, commitOffsetCaptor.getValue().longValue()); + } + + public static MessageExtBrokerInner buildBatchAckMsg(BatchAckMsg batchAckMsg, long deliverMs, long reviveOffset, long deliverTime) { + MessageExtBrokerInner result = buildBatchAckInnerMessage(REVIVE_TOPIC, batchAckMsg, REVIVE_QUEUE_ID, STORE_HOST, deliverMs, PopMessageProcessor.genAckUniqueId(batchAckMsg)); + result.setQueueOffset(reviveOffset); + result.setDeliverTimeMs(deliverMs); + result.setStoreTimestamp(deliverTime); + return result; + } + + public static BatchAckMsg buildBatchAckMsg(Collection offsets, long popTime) { + BatchAckMsg result = new BatchAckMsg(); + result.setConsumerGroup(GROUP); + result.setTopic(TOPIC); + result.setQueueId(0); + result.setPopTime(popTime); + result.setBrokerName("broker-a"); + result.getAckOffsetList().addAll(offsets); + return result; + } + + public static MessageExtBrokerInner buildBatchAckInnerMessage(String reviveTopic, AckMsg ackMsg, int reviveQid, SocketAddress host, long deliverMs, String ackUniqueId) { + MessageExtBrokerInner result = new MessageExtBrokerInner(); + result.setTopic(reviveTopic); + result.setBody(JSON.toJSONString(ackMsg).getBytes(DataConverter.CHARSET_UTF8)); + result.setQueueId(reviveQid); + result.setTags(PopAckConstants.BATCH_ACK_TAG); + result.setBornTimestamp(System.currentTimeMillis()); + result.setBornHost(host); + result.setStoreHost(host); + result.setDeliverTimeMs(deliverMs); + result.getProperties().put(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, ackUniqueId); + result.setPropertiesString(MessageDecoder.messageProperties2String(result.getProperties())); + return result; + } + public static PopCheckPoint buildPopCheckPoint(long startOffset, long popTime, long reviveOffset) { PopCheckPoint ck = new PopCheckPoint(); ck.setStartOffset(startOffset); @@ -467,14 +524,14 @@ public class PopReviveServiceTest { } public static MessageExtBrokerInner buildAckMsg(AckMsg ackMsg, long deliverMs, long reviveOffset, - long deliverTime) { + long deliverTime) { MessageExtBrokerInner messageExtBrokerInner = buildAckInnerMessage( - REVIVE_TOPIC, - ackMsg, - REVIVE_QUEUE_ID, - STORE_HOST, - deliverMs, - PopMessageProcessor.genAckUniqueId(ackMsg) + REVIVE_TOPIC, + ackMsg, + REVIVE_QUEUE_ID, + STORE_HOST, + deliverMs, + PopMessageProcessor.genAckUniqueId(ackMsg) ); messageExtBrokerInner.setQueueOffset(reviveOffset); messageExtBrokerInner.setDeliverTimeMs(deliverMs); @@ -483,7 +540,7 @@ public class PopReviveServiceTest { } public static MessageExtBrokerInner buildAckInnerMessage(String reviveTopic, AckMsg ackMsg, int reviveQid, - SocketAddress host, long deliverMs, String ackUniqueId) { + SocketAddress host, long deliverMs, String ackUniqueId) { MessageExtBrokerInner msgInner = new MessageExtBrokerInner(); msgInner.setTopic(reviveTopic); msgInner.setBody(JSON.toJSONString(ackMsg).getBytes(DataConverter.CHARSET_UTF8)); diff --git a/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java index 3c975a599b..bc34e26bf5 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/subscription/SubscriptionGroupManagerTest.java @@ -47,7 +47,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) @@ -101,13 +100,13 @@ public class SubscriptionGroupManagerTest { subscriptionGroupConfig.setGroupName(group); Map attr = ImmutableMap.of("+test", "true"); subscriptionGroupConfig.setAttributes(attr); + SubscriptionGroupManager subscriptionGroupManager = new SubscriptionGroupManager(brokerControllerMock); subscriptionGroupManager.updateSubscriptionGroupConfig(subscriptionGroupConfig); SubscriptionGroupConfig result = subscriptionGroupManager.getSubscriptionGroupTable().get(group); assertThat(result).isNotNull(); assertThat(result.getGroupName()).isEqualTo(group); assertThat(result.getAttributes().get("test")).isEqualTo("true"); - SubscriptionGroupConfig subscriptionGroupConfig1 = new SubscriptionGroupConfig(); subscriptionGroupConfig1.setGroupName(group); Map attrRemove = ImmutableMap.of("-test", ""); @@ -157,14 +156,11 @@ public class SubscriptionGroupManagerTest { groupNames.add(groupName); } + SubscriptionGroupManager subscriptionGroupManager = new SubscriptionGroupManager(brokerControllerMock); subscriptionGroupManager.updateSubscriptionGroupConfigList(configList); - // Verifying that persist() is called once - verify(subscriptionGroupManager, times(1)).persist(); - groupNames.forEach(groupName -> assertThat(subscriptionGroupManager.getSubscriptionGroupTable().get(groupName)).isNotNull()); - } @Test diff --git a/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManagerTest.java index b74e57ab93..9b25e0134c 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManagerTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManagerTest.java @@ -17,15 +17,11 @@ package org.apache.rocketmq.broker.topic; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.rocketmq.broker.BrokerController; import org.apache.rocketmq.common.BrokerConfig; +import org.apache.rocketmq.remoting.protocol.body.TopicQueueMappingSerializeWrapper; import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingDetail; import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingUtils; import org.apache.rocketmq.remoting.protocol.statictopic.TopicRemappingDetailWrapper; @@ -37,6 +33,16 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -79,9 +85,9 @@ public class TopicQueueMappingManagerTest { String topic = UUID.randomUUID().toString(); int queueNum = 10; TopicRemappingDetailWrapper topicRemappingDetailWrapper = TopicQueueMappingUtils.createTopicConfigMapping(topic, queueNum, brokers, new HashMap<>()); - Assert.assertEquals(1, topicRemappingDetailWrapper.getBrokerConfigMap().size()); + assertEquals(1, topicRemappingDetailWrapper.getBrokerConfigMap().size()); TopicQueueMappingDetail topicQueueMappingDetail = topicRemappingDetailWrapper.getBrokerConfigMap().values().iterator().next().getMappingDetail(); - Assert.assertEquals(queueNum, topicQueueMappingDetail.getHostedQueues().size()); + assertEquals(queueNum, topicQueueMappingDetail.getHostedQueues().size()); mappingDetailMap.put(topic, topicQueueMappingDetail); } } @@ -89,7 +95,7 @@ public class TopicQueueMappingManagerTest { { topicQueueMappingManager = new TopicQueueMappingManager(brokerController); Assert.assertTrue(topicQueueMappingManager.load()); - Assert.assertEquals(0, topicQueueMappingManager.getTopicQueueMappingTable().size()); + assertEquals(0, topicQueueMappingManager.getTopicQueueMappingTable().size()); for (TopicQueueMappingDetail mappingDetail : mappingDetailMap.values()) { for (int i = 0; i < 10; i++) { topicQueueMappingManager.updateTopicQueueMapping(mappingDetail, false, false, true); @@ -101,11 +107,49 @@ public class TopicQueueMappingManagerTest { { topicQueueMappingManager = new TopicQueueMappingManager(brokerController); Assert.assertTrue(topicQueueMappingManager.load()); - Assert.assertEquals(mappingDetailMap.size(), topicQueueMappingManager.getTopicQueueMappingTable().size()); + assertEquals(mappingDetailMap.size(), topicQueueMappingManager.getTopicQueueMappingTable().size()); for (TopicQueueMappingDetail topicQueueMappingDetail: topicQueueMappingManager.getTopicQueueMappingTable().values()) { - Assert.assertEquals(topicQueueMappingDetail, mappingDetailMap.get(topicQueueMappingDetail.getTopic())); + assertEquals(topicQueueMappingDetail, mappingDetailMap.get(topicQueueMappingDetail.getTopic())); } } delete(topicQueueMappingManager); } + + @Test + public void testEncodePretty() { + TopicQueueMappingManager topicQueueMappingManager = new TopicQueueMappingManager(null); + TopicQueueMappingDetail detail = new TopicQueueMappingDetail(); + detail.setTopic("testTopic"); + detail.setBname("testBroker"); + + topicQueueMappingManager.getTopicQueueMappingTable().put("testTopic", detail); + topicQueueMappingManager.getDataVersion().nextVersion(); + + String actual = topicQueueMappingManager.encode(true); + TopicQueueMappingSerializeWrapper expectedWrapper = new TopicQueueMappingSerializeWrapper(); + expectedWrapper.setTopicQueueMappingInfoMap(new ConcurrentHashMap<>(topicQueueMappingManager.getTopicQueueMappingTable())); + expectedWrapper.setDataVersion(topicQueueMappingManager.getDataVersion()); + String expected = JSON.toJSONString(expectedWrapper, JSONWriter.Feature.PrettyFormat); + + assertEquals(expected, actual); + } + + @Test + public void testEncodeNonPretty() { + TopicQueueMappingManager topicQueueMappingManager = new TopicQueueMappingManager(null); + TopicQueueMappingDetail detail = new TopicQueueMappingDetail(); + detail.setTopic("testTopic"); + detail.setBname("testBroker"); + + topicQueueMappingManager.getTopicQueueMappingTable().put("testTopic", detail); + topicQueueMappingManager.getDataVersion().nextVersion(); + + String actual = topicQueueMappingManager.encode(false); + TopicQueueMappingSerializeWrapper expectedWrapper = new TopicQueueMappingSerializeWrapper(); + expectedWrapper.setTopicQueueMappingInfoMap(new ConcurrentHashMap<>(topicQueueMappingManager.getTopicQueueMappingTable())); + expectedWrapper.setDataVersion(topicQueueMappingManager.getDataVersion()); + String expected = JSON.toJSONString(expectedWrapper); + + assertEquals(expected, actual); + } } diff --git a/broker/src/test/java/org/apache/rocketmq/broker/transaction/queue/TransactionMetricsTest.java b/broker/src/test/java/org/apache/rocketmq/broker/transaction/queue/TransactionMetricsTest.java index 690b4eabb5..62a6ad8b5b 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/transaction/queue/TransactionMetricsTest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/transaction/queue/TransactionMetricsTest.java @@ -19,23 +19,40 @@ package org.apache.rocketmq.broker.transaction.queue; import org.apache.rocketmq.broker.transaction.TransactionMetrics; import org.apache.rocketmq.broker.transaction.TransactionMetrics.Metric; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; +import java.util.UUID; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class TransactionMetricsTest { private TransactionMetrics transactionMetrics; private String configPath; + private Path path; @Before - public void setUp() throws Exception { - configPath = "configPath"; - transactionMetrics = new TransactionMetrics(configPath); + public void before() throws Exception { + configPath = createBaseDir(); + path = Paths.get(configPath); + transactionMetrics = spy(new TransactionMetrics(configPath)); + } + + @After + public void after() throws Exception { + deleteFile(configPath); + assertFalse(path.toFile().exists()); } /** @@ -80,4 +97,40 @@ public class TransactionMetricsTest { transactionMetrics.cleanMetrics(Collections.singleton(topic)); assert transactionMetrics.getTransactionCount(topic) == 0; } + + @Test + public void testPersist() { + assertFalse(path.toFile().exists()); + transactionMetrics.persist(); + assertTrue(path.toFile().exists()); + verify(transactionMetrics).persist(); + } + + private String createBaseDir() { + String baseDir = System.getProperty("java.io.tmpdir") + File.separator + "unitteststore-" + UUID.randomUUID(); + final File file = new File(baseDir); + if (file.exists()) { + System.exit(1); + } + return baseDir; + } + + private void deleteFile(String fileName) { + deleteFile(new File(fileName)); + } + + private void deleteFile(File file) { + if (!file.exists()) { + return; + } + if (file.isFile()) { + file.delete(); + } else if (file.isDirectory()) { + File[] files = file.listFiles(); + for (File file1 : files) { + deleteFile(file1); + } + file.delete(); + } + } } diff --git a/client/BUILD.bazel b/client/BUILD.bazel index 31681f1029..3bd84606a2 100644 --- a/client/BUILD.bazel +++ b/client/BUILD.bazel @@ -27,7 +27,6 @@ java_library( "@maven//:commons_validator_commons_validator", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:io_netty_netty_all", "@maven//:io_opentracing_opentracing_api", diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java index 95bb0e8a96..11af6ecd4b 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.client.impl; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.client.ClientConfig; diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java b/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java index 3055f2cdee..bb838a6265 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java @@ -16,26 +16,8 @@ */ package org.apache.rocketmq.client.impl.factory; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import io.netty.channel.Channel; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.client.ClientConfig; import org.apache.rocketmq.client.admin.MQAdminExtInner; @@ -86,6 +68,25 @@ import org.apache.rocketmq.remoting.protocol.route.BrokerData; import org.apache.rocketmq.remoting.protocol.route.QueueData; import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import static org.apache.rocketmq.remoting.rpc.ClientMetadata.topicRouteData2EndpointsForStaticTopic; public class MQClientInstance { diff --git a/client/src/main/java/org/apache/rocketmq/client/producer/SendResult.java b/client/src/main/java/org/apache/rocketmq/client/producer/SendResult.java index d160eb4eae..5405275398 100644 --- a/client/src/main/java/org/apache/rocketmq/client/producer/SendResult.java +++ b/client/src/main/java/org/apache/rocketmq/client/producer/SendResult.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.client.producer; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.apache.rocketmq.common.message.MessageQueue; public class SendResult { diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java index 592c247057..f57b3f80fe 100644 --- a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java @@ -17,17 +17,6 @@ package org.apache.rocketmq.client.consumer; -import java.io.ByteArrayOutputStream; -import java.lang.reflect.Field; -import java.net.InetSocketAddress; -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.client.ClientConfig; import org.apache.rocketmq.client.consumer.store.OffsetStore; @@ -65,6 +54,18 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; import static org.awaitility.Awaitility.await; @@ -75,6 +76,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -898,5 +900,6 @@ public class DefaultLitePullConsumerTest { ConcurrentMap factoryTable = (ConcurrentMap) FieldUtils.readDeclaredField(MQClientManager.getInstance(), "factoryTable", true); factoryTable.put(litePullConsumer.buildMQClientId(), mQClientFactory); doReturn(false).when(mQClientFactory).updateTopicRouteInfoFromNameServer(anyString()); + doNothing().when(mQClientFactory).updateTopicRouteInfoFromNameServer(); } } diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java index c12b23cb0d..c8d23274bd 100644 --- a/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/impl/MQClientAPIImplTest.java @@ -16,6 +16,7 @@ */ package org.apache.rocketmq.client.impl; +import com.alibaba.fastjson2.JSON; import org.apache.rocketmq.client.ClientConfig; import org.apache.rocketmq.client.consumer.AckCallback; import org.apache.rocketmq.client.consumer.AckResult; @@ -34,6 +35,7 @@ import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.common.CheckRocksdbCqWriteResult; import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.ObjectCreator; import org.apache.rocketmq.common.Pair; @@ -2031,6 +2033,22 @@ public class MQClientAPIImplTest { } } + @Test + public void testCheckRocksdbCqWriteProgress() throws Exception { + RemotingCommand response = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success"); + CheckRocksdbCqWriteResult expectedResult = new CheckRocksdbCqWriteResult(); + expectedResult.setCheckStatus(CheckRocksdbCqWriteResult.CheckStatus.CHECK_OK.getValue()); + response.setBody(JSON.toJSONString(expectedResult).getBytes()); + + when(remotingClient.invokeSync(any(String.class), any(RemotingCommand.class), any(Long.class))) + .thenReturn(response); + + CheckRocksdbCqWriteResult result = mqClientAPI.checkRocksdbCqWriteProgress( + "brokerAddr", "testTopic", 12345L, 3000L); + + assertEquals(CheckRocksdbCqWriteResult.CheckStatus.CHECK_OK.getValue(), result.getCheckStatus()); + } + private Properties createProperties() { Properties result = new Properties(); result.put("key", "value"); diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java index 71682fb52c..520f4da5f2 100644 --- a/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/impl/admin/MqClientAdminImplTest.java @@ -206,7 +206,7 @@ public class MqClientAdminImplTest { @Test public void assertUpdateOrCreateSubscriptionGroupWithSuccess() throws Exception { setResponseSuccess(null); - SubscriptionGroupConfig config = mock(SubscriptionGroupConfig.class); + SubscriptionGroupConfig config = new SubscriptionGroupConfig(); CompletableFuture actual = mqClientAdminImpl.updateOrCreateSubscriptionGroup(defaultBrokerAddr, config, defaultTimeout); assertNull(actual.get()); } @@ -214,7 +214,7 @@ public class MqClientAdminImplTest { @Test public void assertUpdateOrCreateSubscriptionGroupWithError() { setResponseError(); - SubscriptionGroupConfig config = mock(SubscriptionGroupConfig.class); + SubscriptionGroupConfig config = new SubscriptionGroupConfig(); CompletableFuture actual = mqClientAdminImpl.updateOrCreateSubscriptionGroup(defaultBrokerAddr, config, defaultTimeout); Throwable thrown = assertThrows(ExecutionException.class, actual::get); assertTrue(thrown.getCause() instanceof MQClientException); diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/consumer/PopProcessQueueTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/consumer/PopProcessQueueTest.java new file mode 100644 index 0000000000..0633342c59 --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/impl/consumer/PopProcessQueueTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.impl.consumer; + +import org.apache.rocketmq.remoting.protocol.body.PopProcessQueueInfo; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.Assert.assertEquals; + +@RunWith(MockitoJUnitRunner.class) +public class PopProcessQueueTest { + + private final PopProcessQueueInfo popProcessQueueInfo = new PopProcessQueueInfo(); + + @Test + public void testPopProcessQueue() { + long currentTime = System.currentTimeMillis(); + PopProcessQueue popRequest1 = createPopProcessQueue(currentTime); + PopProcessQueue popRequest2 = createPopProcessQueue(currentTime); + assertEquals(popRequest1.getLastPopTimestamp(), popRequest2.getLastPopTimestamp()); + assertEquals(popRequest1.toString(), popRequest2.toString()); + assertEquals(popRequest1.getWaiAckMsgCount(), popRequest2.getWaiAckMsgCount()); + assertEquals(popRequest1.ack(), popRequest2.ack()); + assertEquals(popRequest1.isPullExpired(), popRequest2.isPullExpired()); + assertEquals(popProcessQueueInfo.getLastPopTimestamp(), popRequest1.getLastPopTimestamp()); + assertEquals(popProcessQueueInfo.isDroped(), popRequest1.isDropped()); + assertEquals(popProcessQueueInfo.getWaitAckCount(), popRequest1.getWaiAckMsgCount() + popRequest2.getWaiAckMsgCount()); + } + + private PopProcessQueue createPopProcessQueue(final long currentTime) { + PopProcessQueue result = new PopProcessQueue(); + long curTime = System.currentTimeMillis(); + result.setLastPopTimestamp(curTime); + result.incFoundMsg(1); + result.decFoundMsg(1); + result.setLastPopTimestamp(currentTime); + result.fillPopProcessQueueInfo(popProcessQueueInfo); + return result; + } +} diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ProcessQueueTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ProcessQueueTest.java index dd7ffa757f..a12633be1b 100644 --- a/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ProcessQueueTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ProcessQueueTest.java @@ -16,11 +16,6 @@ */ package org.apache.rocketmq.client.impl.consumer; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.TreeMap; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.exception.MQBrokerException; @@ -34,6 +29,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.TreeMap; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -164,6 +165,7 @@ public class ProcessQueueTest { ProcessQueue result = new ProcessQueue(); result.setMsgAccCnt(1); result.incTryUnlockTimes(); + result.setLastPullTimestamp(10000L); return result; } diff --git a/client/src/test/java/org/apache/rocketmq/client/producer/SendResultTest.java b/client/src/test/java/org/apache/rocketmq/client/producer/SendResultTest.java new file mode 100644 index 0000000000..1ae8371a96 --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/producer/SendResultTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.client.producer; + +import com.alibaba.fastjson2.JSON; +import org.apache.rocketmq.common.message.MessageQueue; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SendResultTest { + + @Test + public void testEncoderSendResultToJson() { + SendResult sendResult = new SendResult(); + sendResult.setSendStatus(SendStatus.SEND_OK); + sendResult.setMsgId("12345"); + sendResult.setQueueOffset(100L); + MessageQueue messageQueue = new MessageQueue("TestTopic", "BrokerA", 1); + sendResult.setMessageQueue(messageQueue); + + String json = SendResult.encoderSendResultToJson(sendResult); + + SendResult decodedResult = JSON.parseObject(json, SendResult.class); + assertEquals(sendResult.getSendStatus(), decodedResult.getSendStatus()); + assertEquals(sendResult.getMsgId(), decodedResult.getMsgId()); + assertEquals(sendResult.getQueueOffset(), decodedResult.getQueueOffset()); + assertEquals(sendResult.getMessageQueue(), decodedResult.getMessageQueue()); + } + + @Test + public void testDecoderSendResultFromJson() { + String json = "{\"sendStatus\":\"SEND_OK\",\"msgId\":\"12345\",\"queueOffset\":100,\"messageQueue\":{\"topic\":\"TestTopic\",\"brokerName\":\"BrokerA\",\"queueId\":1}}"; + + SendResult sendResult = SendResult.decoderSendResultFromJson(json); + + assertEquals(SendStatus.SEND_OK, sendResult.getSendStatus()); + assertEquals("12345", sendResult.getMsgId()); + assertEquals(100L, sendResult.getQueueOffset()); + assertEquals("TestTopic", sendResult.getMessageQueue().getTopic()); + assertEquals("BrokerA", sendResult.getMessageQueue().getBrokerName()); + assertEquals(1, sendResult.getMessageQueue().getQueueId()); + } +} diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 10c5d19fbe..8aeeb2f24f 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -21,7 +21,6 @@ java_library( srcs = glob(["src/main/java/**/*.java"]), visibility = ["//visibility:public"], deps = [ - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_github_luben_zstd_jni", "@maven//:com_google_guava_guava", @@ -55,7 +54,6 @@ java_library( ":common", "//:test_deps", "@maven//:com_google_guava_guava", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:commons_codec_commons_codec", "@maven//:io_netty_netty_all", diff --git a/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java b/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java index 0bf64905a0..ebb009c567 100644 --- a/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java +++ b/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java @@ -16,14 +16,15 @@ */ package org.apache.rocketmq.common; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.alibaba.fastjson2.annotation.JSONField; +import org.apache.rocketmq.common.attribute.TopicMessageType; +import org.apache.rocketmq.common.constant.PermName; + import java.util.HashMap; import java.util.Map; import java.util.Objects; -import org.apache.rocketmq.common.attribute.TopicMessageType; -import org.apache.rocketmq.common.constant.PermName; import static org.apache.rocketmq.common.TopicAttributes.TOPIC_MESSAGE_TYPE_ATTRIBUTE; diff --git a/common/src/main/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializer.java b/common/src/main/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializer.java index 80a1554d12..f2639b51c1 100644 --- a/common/src/main/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializer.java +++ b/common/src/main/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializer.java @@ -17,11 +17,10 @@ package org.apache.rocketmq.common.fastjson; -import com.alibaba.fastjson.JSONException; -import com.alibaba.fastjson.parser.DefaultJSONParser; -import com.alibaba.fastjson.parser.JSONToken; -import com.alibaba.fastjson.parser.deserializer.MapDeserializer; -import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; +import com.alibaba.fastjson2.JSONException; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.reader.ObjectReader; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Map; @@ -29,32 +28,40 @@ import java.util.Map; /** * workaround https://github.com/alibaba/fastjson/issues/3730 */ -public class GenericMapSuperclassDeserializer implements ObjectDeserializer { +public class GenericMapSuperclassDeserializer implements ObjectReader { public static final GenericMapSuperclassDeserializer INSTANCE = new GenericMapSuperclassDeserializer(); - @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + public Object readObject(JSONReader reader, Type type, Object fieldName, long features) { Class clz = (Class) type; Type genericSuperclass = clz.getGenericSuperclass(); Map map; try { - map = (Map) clz.newInstance(); + map = (Map) clz.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new JSONException("unsupport type " + type, e); } ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type keyType = parameterizedType.getActualTypeArguments()[0]; Type valueType = parameterizedType.getActualTypeArguments()[1]; - if (String.class == keyType) { - return (T) MapDeserializer.parseMap(parser, (Map) map, valueType, fieldName); - } else { - return (T) MapDeserializer.parseMap(parser, map, keyType, valueType, fieldName); - } - } - @Override - public int getFastMatchToken() { - return JSONToken.LBRACE; + if (!reader.nextIfObjectStart()) { + throw new JSONException(reader.info("expect '{', but " + reader.current())); + } + + while (!reader.nextIfObjectEnd()) { + Object key; + if (keyType == String.class) { + key = reader.readFieldName(); + } else { + key = reader.getContext().getProvider().getObjectReader(keyType).readObject(reader, keyType, fieldName, features); + reader.nextIfMatch(':'); + } + + Object value = reader.getContext().getProvider().getObjectReader(valueType).readObject(reader, valueType, fieldName, features); + map.put(key, value); + reader.nextIfComma(); + } + return map; } } diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/FastJsonSerializer.java b/common/src/main/java/org/apache/rocketmq/common/utils/FastJsonSerializer.java index 600054b40b..943e823566 100644 --- a/common/src/main/java/org/apache/rocketmq/common/utils/FastJsonSerializer.java +++ b/common/src/main/java/org/apache/rocketmq/common/utils/FastJsonSerializer.java @@ -16,23 +16,13 @@ */ package org.apache.rocketmq.common.utils; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson2.JSON; import org.apache.commons.lang3.SerializationException; /** * The object serializer based on fastJson */ public class FastJsonSerializer implements Serializer { - private FastJsonConfig fastJsonConfig = new FastJsonConfig(); - - public FastJsonConfig getFastJsonConfig() { - return this.fastJsonConfig; - } - - public void setFastJsonConfig(FastJsonConfig fastJsonConfig) { - this.fastJsonConfig = fastJsonConfig; - } @Override public byte[] serialize(T t) throws SerializationException { @@ -40,7 +30,7 @@ public class FastJsonSerializer implements Serializer { return new byte[0]; } else { try { - return JSON.toJSONBytes(this.fastJsonConfig.getCharset(), t, this.fastJsonConfig.getSerializeConfig(), this.fastJsonConfig.getSerializeFilters(), this.fastJsonConfig.getDateFormat(), JSON.DEFAULT_GENERATE_FEATURE, this.fastJsonConfig.getSerializerFeatures()); + return JSON.toJSONBytes(t); } catch (Exception var3) { throw new SerializationException("Could not serialize: " + var3.getMessage(), var3); } @@ -51,7 +41,7 @@ public class FastJsonSerializer implements Serializer { public T deserialize(byte[] bytes, Class type) throws SerializationException { if (bytes != null && bytes.length != 0) { try { - return JSON.parseObject(bytes, this.fastJsonConfig.getCharset(), type, this.fastJsonConfig.getParserConfig(), this.fastJsonConfig.getParseProcess(), JSON.DEFAULT_PARSER_FEATURE, this.fastJsonConfig.getFeatures()); + return JSON.parseObject(bytes, type); } catch (Exception var3) { throw new SerializationException("Could not deserialize: " + var3.getMessage(), var3); } diff --git a/common/src/test/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializerTest.java b/common/src/test/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializerTest.java new file mode 100644 index 0000000000..36c633f699 --- /dev/null +++ b/common/src/test/java/org/apache/rocketmq/common/fastjson/GenericMapSuperclassDeserializerTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.common.fastjson; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONException; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class GenericMapSuperclassDeserializerTest { + + public static class CustomMap extends HashMap { + private static final long serialVersionUID = 1L; + } + + public static class IntKeyMap extends HashMap { + private static final long serialVersionUID = 1L; + } + + @Test + public void testBasicDeserialization() { + JSON.registerIfAbsent(CustomMap.class, GenericMapSuperclassDeserializer.INSTANCE); + String json = "{\"key1\":\"value1\",\"key2\":42,\"key3\":true}"; + CustomMap map = JSON.parseObject(json, CustomMap.class); + + assertNotNull(map); + assertEquals(3, map.size()); + assertEquals("value1", map.get("key1")); + assertEquals(42, map.get("key2")); + assertEquals(true, map.get("key3")); + } + + @Test + public void testNestedObjects() { + JSON.registerIfAbsent(CustomMap.class, GenericMapSuperclassDeserializer.INSTANCE); + String json = "{\"simple\":\"value\",\"nested\":{\"inner\":123},\"array\":[1,2,3]}"; + CustomMap map = JSON.parseObject(json, CustomMap.class); + + assertNotNull(map); + assertEquals(3, map.size()); + assertEquals("value", map.get("simple")); + + assertTrue(map.get("nested") instanceof Map); + Map nestedMap = (Map) map.get("nested"); + assertEquals(123, nestedMap.get("inner")); + + assertTrue(map.get("array") instanceof java.util.List); + java.util.List array = (java.util.List) map.get("array"); + assertEquals(3, array.size()); + assertEquals(1, array.get(0)); + assertEquals(2, array.get(1)); + assertEquals(3, array.get(2)); + } + + @Test + public void testEmptyObject() { + JSON.registerIfAbsent(CustomMap.class, GenericMapSuperclassDeserializer.INSTANCE); + String json = "{}"; + CustomMap map = JSON.parseObject(json, CustomMap.class); + + assertNotNull(map); + assertEquals(0, map.size()); + } + + @Test + public void testNonStringKey() { + JSON.registerIfAbsent(IntKeyMap.class, GenericMapSuperclassDeserializer.INSTANCE); + String json = "{1:\"one\",2:\"two\",3:\"three\"}"; + IntKeyMap map = JSON.parseObject(json, IntKeyMap.class); + + assertNotNull(map); + assertEquals(3, map.size()); + assertEquals("one", map.get(1)); + assertEquals("two", map.get(2)); + assertEquals("three", map.get(3)); + } + + @Test(expected = JSONException.class) + public void testMalformedJson() { + JSON.registerIfAbsent(CustomMap.class, GenericMapSuperclassDeserializer.INSTANCE); + String json = "{\"key\":\"missing closing brace\""; + JSON.parseObject(json, CustomMap.class); + } +} diff --git a/container/BUILD.bazel b/container/BUILD.bazel index 4888de2228..b828c4c431 100644 --- a/container/BUILD.bazel +++ b/container/BUILD.bazel @@ -35,7 +35,6 @@ java_library( "@maven//:commons_codec_commons_codec", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", "@maven//:io_netty_netty_all", "@maven//:com_google_guava_guava", "@maven//:org_slf4j_slf4j_api", @@ -65,7 +64,6 @@ java_library( "@maven//:org_apache_commons_commons_lang3", "@maven//:io_netty_netty_all", "@maven//:com_google_guava_guava", - "@maven//:com_alibaba_fastjson", ], resources = glob(["src/test/resources/certs/*.pem"]) + glob(["src/test/resources/certs/*.key"]) ) diff --git a/controller/BUILD.bazel b/controller/BUILD.bazel index e1dc61c500..652dbd4d0c 100644 --- a/controller/BUILD.bazel +++ b/controller/BUILD.bazel @@ -31,7 +31,7 @@ java_library( "@maven//:commons_codec_commons_codec", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:io_netty_netty_all", "@maven//:com_google_guava_guava", "@maven//:org_slf4j_slf4j_api", @@ -70,7 +70,7 @@ java_library( "@maven//:org_apache_commons_commons_lang3", "@maven//:io_netty_netty_all", "@maven//:com_google_guava_guava", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", ], resources = glob(["src/test/resources/certs/*.pem"]) + glob(["src/test/resources/certs/*.key"]) ) diff --git a/controller/src/main/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManager.java b/controller/src/main/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManager.java index c7d5d26fd0..e7a3443001 100644 --- a/controller/src/main/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManager.java +++ b/controller/src/main/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManager.java @@ -16,8 +16,8 @@ */ package org.apache.rocketmq.controller.impl.heartbeat; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; import io.netty.channel.Channel; import org.apache.rocketmq.common.ControllerConfig; import org.apache.rocketmq.common.ThreadFactoryImpl; diff --git a/controller/src/main/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManager.java b/controller/src/main/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManager.java index 492043235b..046ced90c0 100644 --- a/controller/src/main/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManager.java +++ b/controller/src/main/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManager.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.controller.impl.manager; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.apache.rocketmq.common.ControllerConfig; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.controller.helper.BrokerValidPredicate; diff --git a/controller/src/test/java/org/apache/rocketmq/controller/impl/RaftBrokerHeartBeatManagerTest.java b/controller/src/test/java/org/apache/rocketmq/controller/impl/RaftBrokerHeartBeatManagerTest.java index 3c28158982..ee742cc55b 100644 --- a/controller/src/test/java/org/apache/rocketmq/controller/impl/RaftBrokerHeartBeatManagerTest.java +++ b/controller/src/test/java/org/apache/rocketmq/controller/impl/RaftBrokerHeartBeatManagerTest.java @@ -16,13 +16,9 @@ */ package org.apache.rocketmq.controller.impl; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import io.netty.channel.Channel; import io.netty.channel.DefaultChannelPromise; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import org.apache.rocketmq.common.ControllerConfig; import org.apache.rocketmq.controller.impl.heartbeat.BrokerIdentityInfo; import org.apache.rocketmq.controller.impl.heartbeat.RaftBrokerHeartBeatManager; @@ -34,6 +30,11 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; diff --git a/controller/src/test/java/org/apache/rocketmq/controller/impl/event/EventSerializerTest.java b/controller/src/test/java/org/apache/rocketmq/controller/impl/event/EventSerializerTest.java new file mode 100644 index 0000000000..4eb482da64 --- /dev/null +++ b/controller/src/test/java/org/apache/rocketmq/controller/impl/event/EventSerializerTest.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.controller.impl.event; + +import org.apache.commons.lang3.SerializationException; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.common.utils.FastJsonSerializer; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class EventSerializerTest { + + @Mock + private FastJsonSerializer serializer; + + private final EventSerializer eventSerializer = new EventSerializer(); + + @Before + public void init() throws IllegalAccessException { + FieldUtils.writeDeclaredField(eventSerializer, "serializer", serializer, true); + } + + @Test + public void testSerializeValidEventMessageShouldReturnSerializedData() { + EventMessage eventMessage = mock(EventMessage.class); + EventType eventType = EventType.APPLY_BROKER_ID_EVENT; + when(eventMessage.getEventType()).thenReturn(eventType); + when(serializer.serialize(eventMessage)).thenReturn("{\"event\":\"APPLY_BROKER_ID_EVENT\"}".getBytes()); + byte[] result = eventSerializer.serialize(eventMessage); + assertNotNull(result); + } + + @Test + public void testSerializeEventMessageWithNoEventType() { + EventMessage eventMessage = mock(EventMessage.class); + when(eventMessage.getEventType()).thenReturn(null); + assertThrows(NullPointerException.class, () -> eventSerializer.serialize(eventMessage)); + } + + @Test + public void testSerializeSerializerReturnsNullShouldReturnNull() { + EventMessage eventMessage = mock(EventMessage.class); + EventType eventType = EventType.READ_EVENT; + when(eventMessage.getEventType()).thenReturn(eventType); + when(serializer.serialize(eventMessage)).thenReturn(null); + byte[] result = eventSerializer.serialize(eventMessage); + assertNull(result); + } + + @Test + public void testSerializeSerializerThrowsException() { + EventMessage eventMessage = mock(EventMessage.class); + EventType eventType = EventType.ELECT_MASTER_EVENT; + when(eventMessage.getEventType()).thenReturn(eventType); + when(serializer.serialize(eventMessage)).thenThrow(new RuntimeException("Serialization error")); + assertThrows(RuntimeException.class, () -> eventSerializer.serialize(eventMessage)); + } + + @Test + public void testDeserializeBytesLessThanTwoReturnsNull() { + byte[] bytes = new byte[1]; + assertNull(eventSerializer.deserialize(bytes)); + } + + @Test + public void testDeserializeInvalidEventIdReturnsNull() { + assertNull(eventSerializer.deserialize(new byte[]{0, 0xF})); + } + + @Test + public void testDeserializeValidEventTypeReturnsEventMessage() throws SerializationException { + byte[] data = new byte[]{0, 0xF}; + byte[] bytes = new byte[]{0, (byte) EventType.ALTER_SYNC_STATE_SET_EVENT.getId(), data[0], data[1]}; + AlterSyncStateSetEvent alterSyncStateSetEvent = mock(AlterSyncStateSetEvent.class); + when(serializer.deserialize(any(byte[].class), eq(AlterSyncStateSetEvent.class))).thenReturn(alterSyncStateSetEvent); + EventMessage result = eventSerializer.deserialize(bytes); + assertNotNull(result); + assertTrue(result instanceof AlterSyncStateSetEvent); + } + + @Test + public void testDeserializeSerializerThrowsException() throws SerializationException { + byte[] data = new byte[]{0, 0xF}; + byte[] bytes = new byte[]{0, (byte) EventType.ALTER_SYNC_STATE_SET_EVENT.getId(), data[0], data[1]}; + when(serializer.deserialize(any(byte[].class), eq(AlterSyncStateSetEvent.class))).thenThrow(new SerializationException("Deserialization failed")); + assertThrows(SerializationException.class, () -> eventSerializer.deserialize(bytes)); + } + + @Test + public void testDeserializeValidEventTypeUnknownEventReturnsNull() throws SerializationException { + byte[] data = new byte[]{0, 0xF}; + byte[] bytes = new byte[]{0, (short) 99, data[0], data[1]}; + assertNull(eventSerializer.deserialize(bytes)); + } +} \ No newline at end of file diff --git a/controller/src/test/java/org/apache/rocketmq/controller/impl/event/ListEventSerializerTest.java b/controller/src/test/java/org/apache/rocketmq/controller/impl/event/ListEventSerializerTest.java new file mode 100644 index 0000000000..1c5e7f3a04 --- /dev/null +++ b/controller/src/test/java/org/apache/rocketmq/controller/impl/event/ListEventSerializerTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.controller.impl.event; + +import org.apache.commons.lang3.SerializationException; +import org.apache.rocketmq.logging.org.slf4j.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ListEventSerializerTest { + + @Mock + private Logger logger; + + @Test + public void testSerializeEmptyList() { + List events = Collections.emptyList(); + byte[] result = ListEventSerializer.serialize(events, null); + assertNotNull(result); + assertEquals(0, result.length); + } + + @Test + public void testSerializeValidEventMessage() { + EventMessage eventMessage = new ElectMasterEvent("brokerA", 0L); + List events = Collections.singletonList(eventMessage); + byte[] result = ListEventSerializer.serialize(events, null); + assertNotNull(result); + assertTrue(result.length > 0); + } + + @Test + public void testSerializeEventMessageWithNullEventType() { + EventMessage eventMessage = mock(EventMessage.class); + when(eventMessage.getEventType()).thenReturn(null); + List events = Collections.singletonList(eventMessage); + assertThrows(NullPointerException.class, () -> ListEventSerializer.serialize(events, logger)); + } + + @Test + public void testDeserializeBytesIsNull() throws SerializationException { + List result = ListEventSerializer.deserialize(null, logger); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testDeserializeBytesLengthLessThanSix() throws SerializationException { + byte[] bytes = new byte[5]; + List result = ListEventSerializer.deserialize(bytes, logger); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testDeserializeValidBytesWithKnownEventType() throws SerializationException { + byte[] bytes = new byte[]{0x01, 0x00, 0x06, 0x00, 0x00, 0x00}; + assertNotNull(ListEventSerializer.deserialize(bytes, logger)); + } + + @Test + public void testDeserializeException() throws SerializationException { + byte[] bytes = new byte[]{0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00}; + assertThrows(ArrayIndexOutOfBoundsException.class, () -> ListEventSerializer.deserialize(bytes, logger)); + } +} \ No newline at end of file diff --git a/controller/src/test/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManagerTest.java b/controller/src/test/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManagerTest.java new file mode 100644 index 0000000000..28c370abbe --- /dev/null +++ b/controller/src/test/java/org/apache/rocketmq/controller/impl/heartbeat/RaftBrokerHeartBeatManagerTest.java @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.controller.impl.heartbeat; + +import com.alibaba.fastjson2.JSON; +import io.netty.channel.Channel; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.common.ControllerConfig; +import org.apache.rocketmq.common.JraftConfig; +import org.apache.rocketmq.controller.impl.JRaftController; +import org.apache.rocketmq.controller.impl.task.BrokerCloseChannelRequest; +import org.apache.rocketmq.controller.impl.task.CheckNotActiveBrokerRequest; +import org.apache.rocketmq.controller.impl.task.GetBrokerLiveInfoResponse; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RaftBrokerHeartBeatManagerTest { + + @Mock + private JRaftController controller; + + private RaftBrokerHeartBeatManager raftBrokerHeartBeatManager; + + @Before + public void init() throws IllegalAccessException { + ControllerConfig controllerConfig = new ControllerConfig(); + raftBrokerHeartBeatManager = new RaftBrokerHeartBeatManager(controllerConfig); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "controller", controller, true); + } + + @Test + public void testOnBrokerHeartbeatSuccess() { + Channel channel = mock(Channel.class); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + when(controller.onBrokerHeartBeat(any())).thenReturn(future); + raftBrokerHeartBeatManager.onBrokerHeartbeat("cluster1", "broker1", "127.0.0.1:10911", 1L, 3000L, channel, 1, 1000L, 500L, 1); + verify(channel, never()).close(); + } + + @Test + public void testOnBrokerHeartbeatLeaderNotAvailable() { + Channel channel = mock(Channel.class); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.CONTROLLER_NOT_LEADER, "Not Leader")); + when(controller.onBrokerHeartBeat(any())).thenReturn(future); + raftBrokerHeartBeatManager.onBrokerHeartbeat("cluster1", "broker1", "127.0.0.1:10911", 1L, 3000L, channel, 1, 1000L, 500L, 1); + verify(channel, never()).close(); + } + + @Test + public void testOnBrokerHeartbeatException() throws Exception { + Channel channel = mock(Channel.class); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + when(controller.onBrokerHeartBeat(any())).thenReturn(future); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "brokerChannelIdentityInfoMap", null, true); + assertThrows(NullPointerException.class, () -> raftBrokerHeartBeatManager.onBrokerHeartbeat("cluster1", "broker1", "127.0.0.1:10911", 1L, 3000L, channel, 1, 1000L, 500L, 1)); + } + + @Test + public void testOnBrokerChannelCloseBrokerIdentityInfoNotNullSuccess() throws Exception { + Channel channel = mock(Channel.class); + BrokerIdentityInfo brokerIdentityInfo = new BrokerIdentityInfo("cluster1", "broker1", 1L); + Map brokerChannelIdentityInfoMap = new HashMap<>(); + brokerChannelIdentityInfoMap.put(channel, brokerIdentityInfo); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "brokerChannelIdentityInfoMap", brokerChannelIdentityInfoMap, true); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + when(controller.onBrokerCloseChannel(any(BrokerCloseChannelRequest.class))).thenReturn(future); + raftBrokerHeartBeatManager.onBrokerChannelClose(channel); + verify(controller).onBrokerCloseChannel(any(BrokerCloseChannelRequest.class)); + } + + @Test + public void testOnBrokerChannelCloseBrokerIdentityInfoNotNullException() throws Exception { + Channel channel = mock(Channel.class); + BrokerIdentityInfo brokerIdentityInfo = new BrokerIdentityInfo("cluster1", "broker1", 1L); + Map brokerChannelIdentityInfoMap = new HashMap<>(); + brokerChannelIdentityInfoMap.put(channel, brokerIdentityInfo); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "brokerChannelIdentityInfoMap", brokerChannelIdentityInfoMap, true); + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new ExecutionException(new RuntimeException("Test Exception"))); + when(controller.onBrokerCloseChannel(any(BrokerCloseChannelRequest.class))).thenReturn(future); + raftBrokerHeartBeatManager.onBrokerChannelClose(channel); + verify(controller).onBrokerCloseChannel(any(BrokerCloseChannelRequest.class)); + } + + @Test + public void testOnBrokerChannelCloseBrokerIdentityInfoNull() { + Channel channel = mock(Channel.class); + raftBrokerHeartBeatManager.onBrokerChannelClose(channel); + verify(controller, never()).onBrokerCloseChannel(any(BrokerCloseChannelRequest.class)); + } + + @Test + public void testOnBrokerChannelCloseBrokerIdentityInfoNotNullTimeoutException() throws Exception { + Channel channel = mock(Channel.class); + BrokerIdentityInfo brokerIdentityInfo = new BrokerIdentityInfo("cluster1", "broker1", 1L); + Map brokerChannelIdentityInfoMap = new HashMap<>(); + brokerChannelIdentityInfoMap.put(channel, brokerIdentityInfo); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "brokerChannelIdentityInfoMap", brokerChannelIdentityInfoMap, true); + CompletableFuture future = new CompletableFuture<>(); + when(controller.onBrokerCloseChannel(any(BrokerCloseChannelRequest.class))).thenReturn(future); + raftBrokerHeartBeatManager.onBrokerChannelClose(channel); + verify(controller).onBrokerCloseChannel(any(BrokerCloseChannelRequest.class)); + } + + @Test + public void testScanNotActiveBrokerSuccess() throws Exception { + ControllerConfig controllerConfig = new ControllerConfig(); + JraftConfig jraftConfig = new JraftConfig(); + jraftConfig.setjRaftScanWaitTimeoutMs(10000); + controllerConfig.setJraftConfig(jraftConfig); + raftBrokerHeartBeatManager = new RaftBrokerHeartBeatManager(controllerConfig); + controller = mock(JRaftController.class); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "controller", controller, true); + when(controller.isLeaderState()).thenReturn(true); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "firstReceivedHeartbeatTime", 1000, true); + + List inactiveBrokers = new ArrayList<>(); + BrokerIdentityInfo brokerInfo = new BrokerIdentityInfo("testCluster", "testBroker", 1L); + inactiveBrokers.add(brokerInfo); + + RemotingCommand response = RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success"); + response.setBody(JSON.toJSONString(inactiveBrokers).getBytes()); + CompletableFuture future = CompletableFuture.completedFuture(response); + when(controller.checkNotActiveBroker(any())).thenReturn(future); + + Channel channel = mock(Channel.class); + Map brokerChannelMap = new HashMap<>(); + brokerChannelMap.put(channel, brokerInfo); + FieldUtils.writeDeclaredField(raftBrokerHeartBeatManager, "brokerChannelIdentityInfoMap", brokerChannelMap, true); + + Method method = RaftBrokerHeartBeatManager.class.getDeclaredMethod("scanNotActiveBroker"); + method.setAccessible(true); + method.invoke(raftBrokerHeartBeatManager); + + verify(controller).checkNotActiveBroker(any(CheckNotActiveBrokerRequest.class)); + } + + @Test + public void testGetBrokerLiveInfoSuccess() throws Exception { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + BrokerIdentityInfo brokerIdentityInfo = new BrokerIdentityInfo(clusterName, brokerName, brokerId); + BrokerLiveInfo expectedBrokerLiveInfo = new BrokerLiveInfo(brokerName, "127.0.0.1:10911", brokerId, System.currentTimeMillis(), 3000L, null, 1, 1000L, 500); + Map expectedResponse = new HashMap<>(); + expectedResponse.put(brokerIdentityInfo, expectedBrokerLiveInfo); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(expectedResponse).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + BrokerLiveInfo brokerLiveInfo = raftBrokerHeartBeatManager.getBrokerLiveInfo(clusterName, brokerName, brokerId); + assertEquals(expectedBrokerLiveInfo.getBrokerName(), brokerLiveInfo.getBrokerName()); + assertEquals(expectedBrokerLiveInfo.getBrokerAddr(), brokerLiveInfo.getBrokerAddr()); + assertEquals(expectedBrokerLiveInfo.getBrokerId(), brokerLiveInfo.getBrokerId()); + } + + @Test + public void testGetBrokerLiveInfoAllBrokers() throws Exception { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + BrokerIdentityInfo brokerIdentityInfo = new BrokerIdentityInfo(clusterName, brokerName, brokerId); + BrokerLiveInfo expectedBrokerLiveInfo = new BrokerLiveInfo(brokerName, "127.0.0.1:10911", brokerId, System.currentTimeMillis(), 3000L, null, 1, 1000L, 500); + Map expectedResponse = new HashMap<>(); + expectedResponse.put(brokerIdentityInfo, expectedBrokerLiveInfo); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(expectedResponse).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + BrokerLiveInfo brokerLiveInfo = raftBrokerHeartBeatManager.getBrokerLiveInfo(null, null, null); + assertNull(brokerLiveInfo); + } + + @Test + public void testIsBrokerActiveBrokerActive() throws Exception { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + BrokerLiveInfo brokerLiveInfo = new BrokerLiveInfo(brokerName, "127.0.0.1:10911", brokerId, System.currentTimeMillis(), 3000L, null, 1, 1000L, 500); + Map responseMap = new HashMap<>(); + responseMap.put(new BrokerIdentityInfo(clusterName, brokerName, brokerId), brokerLiveInfo); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(responseMap).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + assertTrue(raftBrokerHeartBeatManager.isBrokerActive(clusterName, brokerName, brokerId)); + } + + @Test + public void testIsBrokerActiveBrokerNotActive() throws Exception { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + BrokerLiveInfo brokerLiveInfo = new BrokerLiveInfo(brokerName, "127.0.0.1:10911", brokerId, System.currentTimeMillis() - 4000L, 3000L, null, 1, 1000L, 500); + Map responseMap = new HashMap<>(); + responseMap.put(new BrokerIdentityInfo(clusterName, brokerName, brokerId), brokerLiveInfo); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(responseMap).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + assertFalse(raftBrokerHeartBeatManager.isBrokerActive(clusterName, brokerName, brokerId)); + } + + @Test + public void testIsBrokerActiveException() { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new ExecutionException(new RuntimeException("Test Exception"))); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + assertFalse(raftBrokerHeartBeatManager.isBrokerActive(clusterName, brokerName, brokerId)); + } + + @Test + public void testIsBrokerActiveNoInfo() throws Exception { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(new HashMap()).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + assertFalse(raftBrokerHeartBeatManager.isBrokerActive(clusterName, brokerName, brokerId)); + } + + @Test + public void testIsBrokerActiveInvalidResponseCode() { + String clusterName = "cluster1"; + String brokerName = "broker1"; + Long brokerId = 1L; + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.RPC_TIME_OUT, "Timeout")); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + assertFalse(raftBrokerHeartBeatManager.isBrokerActive(clusterName, brokerName, brokerId)); + } + + @Test + public void testGetActiveBrokersNumAllBrokers() throws Exception { + String clusterName1 = "cluster1"; + String brokerName1 = "broker1"; + Long brokerId1 = 1L; + String clusterName2 = "cluster2"; + String brokerName2 = "broker2"; + Long brokerId2 = 2L; + BrokerIdentityInfo brokerIdentityInfo1 = new BrokerIdentityInfo(clusterName1, brokerName1, brokerId1); + BrokerIdentityInfo brokerIdentityInfo2 = new BrokerIdentityInfo(clusterName2, brokerName2, brokerId2); + BrokerLiveInfo brokerLiveInfo1 = new BrokerLiveInfo(brokerName1, "127.0.0.1:10911", brokerId1, System.currentTimeMillis(), 3000L, null, 1, 1000L, 500); + BrokerLiveInfo brokerLiveInfo2 = new BrokerLiveInfo(brokerName2, "127.0.0.1:10912", brokerId2, System.currentTimeMillis(), 3000L, null, 1, 1000L, 500); + Map responseMap = new HashMap<>(); + responseMap.put(brokerIdentityInfo1, brokerLiveInfo1); + responseMap.put(brokerIdentityInfo2, brokerLiveInfo2); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(responseMap).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + Map> activeBrokersNum = raftBrokerHeartBeatManager.getActiveBrokersNum(); + assertEquals(2, activeBrokersNum.size()); + assertEquals(1, activeBrokersNum.get(clusterName1).size()); + assertEquals(1, activeBrokersNum.get(clusterName2).size()); + assertEquals(1, (int) activeBrokersNum.get(clusterName1).get(brokerName1)); + assertEquals(1, (int) activeBrokersNum.get(clusterName2).get(brokerName2)); + } + + @Test + public void testGetActiveBrokersNum() throws Exception { + String clusterName1 = "cluster1"; + String brokerName1 = "broker1"; + Long brokerId1 = 1L; + String clusterName2 = "cluster2"; + String brokerName2 = "broker2"; + Long brokerId2 = 2L; + BrokerIdentityInfo brokerIdentityInfo1 = new BrokerIdentityInfo(clusterName1, brokerName1, brokerId1); + BrokerIdentityInfo brokerIdentityInfo2 = new BrokerIdentityInfo(clusterName2, brokerName2, brokerId2); + BrokerLiveInfo brokerLiveInfo1 = new BrokerLiveInfo(brokerName1, "127.0.0.1:10911", brokerId1, System.currentTimeMillis(), 3000L, null, 1, 1000L, 500); + BrokerLiveInfo brokerLiveInfo2 = new BrokerLiveInfo(brokerName2, "127.0.0.1:10912", brokerId2, System.currentTimeMillis() - 4000L, 3000L, null, 1, 1000L, 500); + Map responseMap = new HashMap<>(); + responseMap.put(brokerIdentityInfo1, brokerLiveInfo1); + responseMap.put(brokerIdentityInfo2, brokerLiveInfo2); + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(responseMap).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + Map> activeBrokersNum = raftBrokerHeartBeatManager.getActiveBrokersNum(); + assertEquals(1, activeBrokersNum.size()); + } + + @Test + public void testGetActiveBrokersNumException() { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new ExecutionException(new RuntimeException("Test Exception"))); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + Map> activeBrokersNum = raftBrokerHeartBeatManager.getActiveBrokersNum(); + assertTrue(activeBrokersNum.isEmpty()); + } + + @Test + public void testGetActiveBrokersNumNoBrokers() throws Exception { + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.SUCCESS, "Success")); + future.get().writeCustomHeader(new GetBrokerLiveInfoResponse()); + future.get().setBody(JSON.toJSONString(new HashMap()).getBytes()); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + Map> activeBrokersNum = raftBrokerHeartBeatManager.getActiveBrokersNum(); + assertTrue(activeBrokersNum.isEmpty()); + } + + @Test + public void testGetActiveBrokersNumInvalidResponseCode() { + CompletableFuture future = CompletableFuture.completedFuture(RemotingCommand.createResponseCommand(ResponseCode.RPC_TIME_OUT, "Timeout")); + when(controller.getBrokerLiveInfo(any())).thenReturn(future); + Map> activeBrokersNum = raftBrokerHeartBeatManager.getActiveBrokersNum(); + assertTrue(activeBrokersNum.isEmpty()); + } +} diff --git a/controller/src/test/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManagerTest.java b/controller/src/test/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManagerTest.java new file mode 100644 index 0000000000..b47f072c2c --- /dev/null +++ b/controller/src/test/java/org/apache/rocketmq/controller/impl/manager/RaftReplicasInfoManagerTest.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.controller.impl.manager; + +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.common.ControllerConfig; +import org.apache.rocketmq.controller.impl.event.ControllerResult; +import org.apache.rocketmq.controller.impl.heartbeat.BrokerIdentityInfo; +import org.apache.rocketmq.controller.impl.heartbeat.BrokerLiveInfo; +import org.apache.rocketmq.controller.impl.task.BrokerCloseChannelRequest; +import org.apache.rocketmq.controller.impl.task.CheckNotActiveBrokerRequest; +import org.apache.rocketmq.controller.impl.task.CheckNotActiveBrokerResponse; +import org.apache.rocketmq.controller.impl.task.GetBrokerLiveInfoRequest; +import org.apache.rocketmq.controller.impl.task.GetBrokerLiveInfoResponse; +import org.apache.rocketmq.controller.impl.task.RaftBrokerHeartBeatEventRequest; +import org.apache.rocketmq.controller.impl.task.RaftBrokerHeartBeatEventResponse; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@RunWith(MockitoJUnitRunner.class) +public class RaftReplicasInfoManagerTest { + + @Mock + private ControllerConfig controllerConfig; + + private RaftReplicasInfoManager raftReplicasInfoManager; + + @Before + public void init() { + raftReplicasInfoManager = new RaftReplicasInfoManager(controllerConfig); + } + + @Test + public void testGetBrokerLiveInfoBrokerIdentityInfoIsNullReturnsAllBrokersInfo() throws IllegalAccessException { + List brokerIdentityInfos = createBrokerIdentityInfos(2); + List brokerLiveInfos = createBrokerLiveInfos(2); + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(brokerIdentityInfos.get(0), brokerLiveInfos.get(0)); + brokerLiveTable.put(brokerIdentityInfos.get(1), brokerLiveInfos.get(1)); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + GetBrokerLiveInfoRequest request = new GetBrokerLiveInfoRequest(); + ControllerResult result = raftReplicasInfoManager.getBrokerLiveInfo(request); + assertNotNull(result); + assertEquals(ResponseCode.SUCCESS, result.getResponseCode()); + } + + @Test + public void testGetBrokerLiveInfoBrokerIdentityInfoExistsReturnsBrokerInfo() throws IllegalAccessException { + BrokerIdentityInfo brokerIdentityInfo = createBrokerIdentityInfo(); + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(brokerIdentityInfo, createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + GetBrokerLiveInfoRequest request = new GetBrokerLiveInfoRequest(brokerIdentityInfo); + ControllerResult result = raftReplicasInfoManager.getBrokerLiveInfo(request); + assertNotNull(result); + assertEquals(ResponseCode.SUCCESS, result.getResponseCode()); + } + + @Test + public void testGetBrokerLiveInfoBrokerIdentityInfoNotExistsReturnsError() { + GetBrokerLiveInfoRequest request = new GetBrokerLiveInfoRequest(createBrokerIdentityInfo()); + ControllerResult result = raftReplicasInfoManager.getBrokerLiveInfo(request); + assertNotNull(result); + assertEquals(ResponseCode.CONTROLLER_BROKER_LIVE_INFO_NOT_EXISTS, result.getResponseCode()); + } + + @Test + public void testOnBrokerHeartBeatNewBrokerRegistered() { + RaftBrokerHeartBeatEventRequest request = new RaftBrokerHeartBeatEventRequest(createBrokerIdentityInfo(), createBrokerLiveInfo()); + ControllerResult result = raftReplicasInfoManager.onBrokerHeartBeat(request); + assertNotNull(result); + assertEquals(ResponseCode.SUCCESS, result.getResponseCode()); + } + + @Test + public void testOnBrokerHeartBeatExistingBrokerUpdate() throws IllegalAccessException { + BrokerIdentityInfo brokerIdentityInfo = createBrokerIdentityInfo(); + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(brokerIdentityInfo, createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + BrokerLiveInfo updatedInfo = new BrokerLiveInfo("brokerName1", "brokerAddr1", 1L, System.currentTimeMillis(), 2000L, null, 2, 200L, 2); + RaftBrokerHeartBeatEventRequest request = new RaftBrokerHeartBeatEventRequest(brokerIdentityInfo, updatedInfo); + ControllerResult result = raftReplicasInfoManager.onBrokerHeartBeat(request); + assertNotNull(result); + assertEquals(ResponseCode.SUCCESS, result.getResponseCode()); + } + + @Test + public void testOnBrokerCloseChannelBrokerIdentityInfoIsNullLogsWarningAndReturnsResult() { + assertNotNull(raftReplicasInfoManager.onBrokerCloseChannel(new BrokerCloseChannelRequest())); + } + + @Test + public void testCheckNotActiveBrokerNoBrokersInTableReturnsEmptyList() { + CheckNotActiveBrokerRequest request = new CheckNotActiveBrokerRequest(); + ControllerResult result = raftReplicasInfoManager.checkNotActiveBroker(request); + assertNotNull(result); + assertEquals(ResponseCode.SUCCESS, result.getResponseCode()); + } + + @Test + public void testCheckNotActiveBrokerBrokerLiveTableNotEmptyIdentifiesNotActiveBrokers() throws IllegalAccessException { + List brokerIdentityInfos = createBrokerIdentityInfos(2); + List brokerLiveInfos = createBrokerLiveInfos(2); + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(brokerIdentityInfos.get(0), brokerLiveInfos.get(0)); + brokerLiveTable.put(brokerIdentityInfos.get(1), brokerLiveInfos.get(1)); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + CheckNotActiveBrokerRequest request = new CheckNotActiveBrokerRequest(); + ControllerResult result = raftReplicasInfoManager.checkNotActiveBroker(request); + assertNotNull(result); + assertEquals(ResponseCode.SUCCESS, result.getResponseCode()); + assertNotNull(result.getBody()); + } + + @Test + public void testCheckNotActiveBrokerSerializeErrorSetsErrorRemark() throws IllegalAccessException { + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(createBrokerIdentityInfo(), createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + CheckNotActiveBrokerRequest request = new CheckNotActiveBrokerRequest(); + ControllerResult result = raftReplicasInfoManager.checkNotActiveBroker(request); + assertNotNull(result); + } + + @Test + public void testIsBrokerActiveBrokerLiveInfoNotNullAndActiveReturnsTrue() throws IllegalAccessException { + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(createBrokerIdentityInfo(), createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + long invokeTime = System.currentTimeMillis() + 500; + boolean brokerActive = raftReplicasInfoManager.isBrokerActive("cluster0", "broker0", 0L, invokeTime); + assertTrue(brokerActive); + } + + @Test + public void testIsBrokerActiveBrokerLiveInfoNotNullAndNotActiveReturnsFalse() throws IllegalAccessException { + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(createBrokerIdentityInfo(), createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + long invokeTime = System.currentTimeMillis(); + assertFalse(raftReplicasInfoManager.isBrokerActive("cluster1", "broker1", 1L, invokeTime)); + } + + @Test + public void testIsBrokerActiveBrokerLiveInfoNullReturnsFalse() { + assertFalse(raftReplicasInfoManager.isBrokerActive("cluster1", "broker1", 1L, System.currentTimeMillis())); + } + + @Test + public void testSerializeWithPopulatedTablesReturnsByteArray() throws Throwable { + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(createBrokerIdentityInfo(), createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + byte[] result = raftReplicasInfoManager.serialize(); + assertNotNull(result); + assertTrue(result.length > 0); + } + + @Test + public void testDeserializeFromValidDataSuccess() throws Throwable { + BrokerIdentityInfo brokerIdentityInfo = createBrokerIdentityInfo(); + Map brokerLiveTable = new HashMap<>(); + brokerLiveTable.put(brokerIdentityInfo, createBrokerLiveInfo()); + FieldUtils.writeDeclaredField(raftReplicasInfoManager, "brokerLiveTable", brokerLiveTable, true); + raftReplicasInfoManager.deserializeFrom(raftReplicasInfoManager.serialize()); + assertNotNull(brokerLiveTable); + assertEquals(1, brokerLiveTable.size()); + assertTrue(brokerLiveTable.containsKey(brokerIdentityInfo)); + } + + @Test + public void testDeserializeFromInvalidDataExceptionThrown() { + byte[] invalidData = new byte[]{0x00, 0x01, 0x02, 0x03}; + try { + raftReplicasInfoManager.deserializeFrom(invalidData); + fail("Expected an exception to be thrown"); + } catch (Throwable e) { + assertTrue(e instanceof ArrayIndexOutOfBoundsException); + } + } + + private BrokerIdentityInfo createBrokerIdentityInfo() { + return createBrokerIdentityInfos(1).get(0); + } + + private List createBrokerIdentityInfos(final int count) { + List result = new ArrayList<>(); + for (int i = 0; i < count; i++) { + result.add(new BrokerIdentityInfo("cluster" + i, "broker" + i, (long) i)); + } + return result; + } + + private BrokerLiveInfo createBrokerLiveInfo() { + return createBrokerLiveInfos(1).get(0); + } + + private List createBrokerLiveInfos(final int count) { + List result = new ArrayList<>(); + for (int i = 0; i < count; i++) { + result.add(new BrokerLiveInfo("brokerName" + i, + "brokerAddr" + i, + i, + System.currentTimeMillis(), + 1000L, + null, + 1, + 100L, + 1)); + } + return result; + } +} diff --git a/filter/BUILD.bazel b/filter/BUILD.bazel index c0d59ba6f4..76e3ef43de 100644 --- a/filter/BUILD.bazel +++ b/filter/BUILD.bazel @@ -26,7 +26,6 @@ java_library( "@maven//:commons_validator_commons_validator", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", "@maven//:io_netty_netty_all", "@maven//:com_google_guava_guava", ], diff --git a/namesrv/BUILD.bazel b/namesrv/BUILD.bazel index fec42eaa3e..435fc29a7f 100644 --- a/namesrv/BUILD.bazel +++ b/namesrv/BUILD.bazel @@ -31,7 +31,7 @@ java_library( "@maven//:commons_validator_commons_validator", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:io_netty_netty_all", "@maven//:ch_qos_logback_logback_classic", "@maven//:ch_qos_logback_logback_core", @@ -60,7 +60,7 @@ java_library( "@maven//:commons_cli_commons_cli", "@maven//:io_netty_netty_all", "@maven//:com_google_guava_guava", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:io_github_aliyunmq_rocketmq_slf4j_api", ], resources = glob(["src/test/resources/certs/*.pem"]) + glob(["src/test/resources/certs/*.key"]) diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java index 1ef6beadd3..ef653129a7 100644 --- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java +++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java @@ -17,10 +17,8 @@ package org.apache.rocketmq.namesrv.processor; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSONWriter; import io.netty.channel.ChannelHandlerContext; -import java.util.Optional; -import java.util.concurrent.TimeUnit; import org.apache.rocketmq.common.MQVersion; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.common.help.FAQUrl; @@ -35,6 +33,9 @@ import org.apache.rocketmq.remoting.protocol.ResponseCode; import org.apache.rocketmq.remoting.protocol.header.namesrv.GetRouteInfoRequestHeader; import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + public class ClientRequestProcessor implements NettyRequestProcessor { private static Logger log = LoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME); @@ -81,9 +82,8 @@ public class ClientRequestProcessor implements NettyRequestProcessor { byte[] content; Boolean standardJsonOnly = Optional.ofNullable(requestHeader.getAcceptStandardJsonOnly()).orElse(false); if (request.getVersion() >= MQVersion.Version.V4_9_4.ordinal() || standardJsonOnly) { - content = topicRouteData.encode(SerializerFeature.BrowserCompatible, - SerializerFeature.QuoteFieldNames, SerializerFeature.SkipTransientField, - SerializerFeature.MapSortField); + content = topicRouteData.encode(JSONWriter.Feature.BrowserCompatible, + JSONWriter.Feature.MapSortField); } else { content = topicRouteData.encode(); } diff --git a/namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessorTest.java b/namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessorTest.java new file mode 100644 index 0000000000..6fa88ad6ea --- /dev/null +++ b/namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessorTest.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.namesrv.processor; + +import io.netty.channel.ChannelHandlerContext; +import org.apache.rocketmq.common.MQVersion; +import org.apache.rocketmq.common.namesrv.NamesrvConfig; +import org.apache.rocketmq.namesrv.NamesrvController; +import org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager; +import org.apache.rocketmq.remoting.exception.RemotingCommandException; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.remoting.protocol.RequestCode; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.apache.rocketmq.remoting.protocol.header.namesrv.GetRouteInfoRequestHeader; +import org.apache.rocketmq.remoting.protocol.route.BrokerData; +import org.apache.rocketmq.remoting.protocol.route.QueueData; +import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ClientRequestProcessorTest { + + @Mock + private NamesrvController namesrvController; + + @Mock + private RouteInfoManager routeInfoManager; + + @Mock + private NamesrvConfig namesrvConfig; + + @Mock + private ChannelHandlerContext ctx; + + private ClientRequestProcessor clientRequestProcessor; + + @Before + public void setup() throws NoSuchFieldException, IllegalAccessException { + when(namesrvController.getRouteInfoManager()).thenReturn(routeInfoManager); + when(namesrvController.getNamesrvConfig()).thenReturn(namesrvConfig); + + when(namesrvConfig.getWaitSecondsForService()).thenReturn(0); + when(namesrvConfig.isNeedWaitForService()).thenReturn(true); + + clientRequestProcessor = new ClientRequestProcessor(namesrvController); + + Field startupTimeMillisField = ClientRequestProcessor.class.getDeclaredField("startupTimeMillis"); + startupTimeMillisField.setAccessible(true); + startupTimeMillisField.set(clientRequestProcessor, System.currentTimeMillis() - 60000); + } + + @Test + public void testGetRouteInfoByTopicWithHighVersionClient() throws RemotingCommandException { + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, null); + request.setVersion(MQVersion.Version.V4_9_4.ordinal()); + + GetRouteInfoRequestHeader requestHeader = new GetRouteInfoRequestHeader(); + requestHeader.setTopic("TestTopic"); + + RemotingCommand spyRequest = spy(request); + doReturn(requestHeader).when(spyRequest).decodeCommandCustomHeader(GetRouteInfoRequestHeader.class); + + TopicRouteData topicRouteData = createMockTopicRouteData(); + + when(routeInfoManager.pickupTopicRouteData("TestTopic")).thenReturn(topicRouteData); + + RemotingCommand response = clientRequestProcessor.getRouteInfoByTopic(ctx, spyRequest); + + assertEquals(ResponseCode.SUCCESS, response.getCode()); + assertNotNull(response.getBody()); + } + + @Test + public void testGetRouteInfoByTopicWithLowVersionClientAndNoStandardJsonFlag() throws RemotingCommandException { + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, null); + request.setVersion(MQVersion.Version.V4_9_3.ordinal()); + + GetRouteInfoRequestHeader requestHeader = new GetRouteInfoRequestHeader(); + requestHeader.setTopic("TestTopic"); + requestHeader.setAcceptStandardJsonOnly(false); + + RemotingCommand spyRequest = spy(request); + doReturn(requestHeader).when(spyRequest).decodeCommandCustomHeader(GetRouteInfoRequestHeader.class); + + TopicRouteData topicRouteData = createMockTopicRouteData(); + + when(routeInfoManager.pickupTopicRouteData("TestTopic")).thenReturn(topicRouteData); + + RemotingCommand response = clientRequestProcessor.getRouteInfoByTopic(ctx, spyRequest); + + assertEquals(ResponseCode.SUCCESS, response.getCode()); + assertNotNull(response.getBody()); + } + + @Test + public void testGetRouteInfoByTopicWithNameServerNotReady() throws RemotingCommandException { + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, null); + + GetRouteInfoRequestHeader requestHeader = new GetRouteInfoRequestHeader(); + requestHeader.setTopic("TestTopic"); + + RemotingCommand spyRequest = spy(request); + doReturn(requestHeader).when(spyRequest).decodeCommandCustomHeader(GetRouteInfoRequestHeader.class); + + when(namesrvConfig.getWaitSecondsForService()).thenReturn(60); + when(namesrvConfig.isNeedWaitForService()).thenReturn(true); + + try { + Field startupTimeMillisField = ClientRequestProcessor.class.getDeclaredField("startupTimeMillis"); + startupTimeMillisField.setAccessible(true); + startupTimeMillisField.set(clientRequestProcessor, System.currentTimeMillis()); + } catch (Exception e) { + e.printStackTrace(); + } + + RemotingCommand response = clientRequestProcessor.getRouteInfoByTopic(ctx, spyRequest); + + assertEquals(ResponseCode.SYSTEM_ERROR, response.getCode()); + assertEquals("name server not ready", response.getRemark()); + } + + @Test + public void testGetRouteInfoByTopicWithTopicNotExist() throws RemotingCommandException { + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, null); + + GetRouteInfoRequestHeader requestHeader = new GetRouteInfoRequestHeader(); + requestHeader.setTopic("NonExistentTopic"); + + RemotingCommand spyRequest = spy(request); + doReturn(requestHeader).when(spyRequest).decodeCommandCustomHeader(GetRouteInfoRequestHeader.class); + + when(routeInfoManager.pickupTopicRouteData("NonExistentTopic")).thenReturn(null); + + RemotingCommand response = clientRequestProcessor.getRouteInfoByTopic(ctx, spyRequest); + + assertEquals(ResponseCode.TOPIC_NOT_EXIST, response.getCode()); + assertNotNull(response.getRemark()); + } + + private TopicRouteData createMockTopicRouteData() { + TopicRouteData result = new TopicRouteData(); + + List queueDataList = new ArrayList<>(); + QueueData queueData = new QueueData(); + queueData.setBrokerName("broker-a"); + queueData.setReadQueueNums(4); + queueData.setWriteQueueNums(4); + queueData.setPerm(6); + queueData.setTopicSysFlag(0); + queueDataList.add(queueData); + result.setQueueDatas(queueDataList); + + List brokerDataList = new ArrayList<>(); + BrokerData brokerData = new BrokerData(); + brokerData.setBrokerName("broker-a"); + brokerData.setCluster("default-cluster"); + HashMap brokerAddrs = new HashMap<>(); + brokerAddrs.put(0L, "127.0.0.1:10911"); + brokerData.setBrokerAddrs(brokerAddrs); + brokerDataList.add(brokerData); + result.setBrokerDatas(brokerDataList); + + return result; + } +} \ No newline at end of file diff --git a/proxy/BUILD.bazel b/proxy/BUILD.bazel index 8b7915ba74..5d7fe24435 100644 --- a/proxy/BUILD.bazel +++ b/proxy/BUILD.bazel @@ -29,7 +29,6 @@ java_library( "//srvutil", "@maven//:ch_qos_logback_logback_classic", "@maven//:ch_qos_logback_logback_core", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_github_ben_manes_caffeine_caffeine", "@maven//:com_github_luben_zstd_jni", @@ -88,7 +87,6 @@ java_library( "//srvutil", "//remoting", "@maven//:ch_qos_logback_logback_core", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_github_ben_manes_caffeine_caffeine", "@maven//:com_google_guava_guava", diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/config/Configuration.java b/proxy/src/main/java/org/apache/rocketmq/proxy/config/Configuration.java index 5b7c6c3007..175ff438f8 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/config/Configuration.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/config/Configuration.java @@ -17,7 +17,7 @@ package org.apache.rocketmq.proxy.config; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import com.google.common.base.Charsets; import com.google.common.io.CharStreams; import java.io.File; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/config/ConfigurationManager.java b/proxy/src/main/java/org/apache/rocketmq/proxy/config/ConfigurationManager.java index 0d8c60931d..4660610ecd 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/config/ConfigurationManager.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/config/ConfigurationManager.java @@ -17,8 +17,8 @@ package org.apache.rocketmq.proxy.config; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter.Feature; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.auth.config.AuthConfig; import org.apache.rocketmq.common.MixAll; @@ -59,6 +59,6 @@ public class ConfigurationManager { public static String formatProxyConfig() { return JSON.toJSONString(ConfigurationManager.getProxyConfig(), - SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.WriteNullListAsEmpty); + Feature.PrettyFormat, Feature.WriteMapNullValue, Feature.WriteNullListAsEmpty); } } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/channel/RemoteChannelSerializer.java b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/channel/RemoteChannelSerializer.java index a22401a5f3..dba0655c7e 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/processor/channel/RemoteChannelSerializer.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/processor/channel/RemoteChannelSerializer.java @@ -17,15 +17,16 @@ package org.apache.rocketmq.proxy.processor.channel; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import java.util.HashMap; -import java.util.Map; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.logging.org.slf4j.Logger; import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.Map; + public class RemoteChannelSerializer { private static final Logger log = LoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); private static final String REMOTE_PROXY_IP_KEY = "remoteProxyIp"; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivity.java b/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivity.java index 56ec34fae6..759b74fe5d 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivity.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivity.java @@ -17,15 +17,10 @@ package org.apache.rocketmq.proxy.remoting.activity; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSONWriter; import com.google.common.net.HostAndPort; import io.netty.channel.ChannelHandlerContext; -import java.util.ArrayList; -import java.util.List; import org.apache.rocketmq.common.MQVersion; -import org.apache.rocketmq.remoting.protocol.ResponseCode; -import org.apache.rocketmq.remoting.protocol.header.namesrv.GetRouteInfoRequestHeader; -import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; import org.apache.rocketmq.proxy.common.Address; import org.apache.rocketmq.proxy.common.ProxyContext; import org.apache.rocketmq.proxy.config.ConfigurationManager; @@ -34,6 +29,12 @@ import org.apache.rocketmq.proxy.processor.MessagingProcessor; import org.apache.rocketmq.proxy.remoting.pipeline.RequestPipeline; import org.apache.rocketmq.proxy.service.route.ProxyTopicRouteData; import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.apache.rocketmq.remoting.protocol.header.namesrv.GetRouteInfoRequestHeader; +import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; + +import java.util.ArrayList; +import java.util.List; public class GetTopicRouteActivity extends AbstractRemotingActivity { public GetTopicRouteActivity(RequestPipeline requestPipeline, @@ -57,9 +58,7 @@ public class GetTopicRouteActivity extends AbstractRemotingActivity { byte[] content; Boolean standardJsonOnly = requestHeader.getAcceptStandardJsonOnly(); if (request.getVersion() >= MQVersion.Version.V4_9_4.ordinal() || null != standardJsonOnly && standardJsonOnly) { - content = topicRouteData.encode(SerializerFeature.BrowserCompatible, - SerializerFeature.QuoteFieldNames, SerializerFeature.SkipTransientField, - SerializerFeature.MapSortField); + content = topicRouteData.encode(JSONWriter.Feature.BrowserCompatible, JSONWriter.Feature.MapSortField); } else { content = topicRouteData.encode(); } diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/channel/RemotingChannel.java b/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/channel/RemotingChannel.java index 5dbdea1b2e..ea8a519d5b 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/channel/RemotingChannel.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/remoting/channel/RemotingChannel.java @@ -17,25 +17,22 @@ package org.apache.rocketmq.proxy.remoting.channel; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; import com.google.common.base.MoreObjects; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelMetadata; -import java.time.Duration; -import java.util.Set; -import java.util.concurrent.CompletableFuture; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.utils.ExceptionUtils; +import org.apache.rocketmq.common.utils.FutureUtils; import org.apache.rocketmq.common.utils.NetworkUtil; import org.apache.rocketmq.logging.org.slf4j.Logger; import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; import org.apache.rocketmq.proxy.common.channel.ChannelHelper; -import org.apache.rocketmq.common.utils.ExceptionUtils; -import org.apache.rocketmq.common.utils.FutureUtils; import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.processor.channel.ChannelExtendAttributeGetter; import org.apache.rocketmq.proxy.processor.channel.ChannelProtocolType; @@ -58,6 +55,10 @@ import org.apache.rocketmq.remoting.protocol.header.ConsumeMessageDirectlyResult import org.apache.rocketmq.remoting.protocol.header.GetConsumerRunningInfoRequestHeader; import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData; +import java.time.Duration; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + public class RemotingChannel extends ProxyChannel implements RemoteChannelConverter, ChannelExtendAttributeGetter { private static final Logger log = LoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); private static final long DEFAULT_MQ_CLIENT_TIMEOUT = Duration.ofSeconds(3).toMillis(); diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/AbstractSystemMessageSyncer.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/AbstractSystemMessageSyncer.java index 6c19edf2f8..05eb672618 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/AbstractSystemMessageSyncer.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/AbstractSystemMessageSyncer.java @@ -17,35 +17,36 @@ package org.apache.rocketmq.proxy.service.sysmessage; -import com.alibaba.fastjson.JSON; -import java.nio.charset.StandardCharsets; -import java.time.Duration; +import com.alibaba.fastjson2.JSON; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.impl.mqclient.MQClientAPIFactory; import org.apache.rocketmq.client.producer.SendStatus; import org.apache.rocketmq.common.constant.LoggerName; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.common.utils.StartAndShutdown; import org.apache.rocketmq.logging.org.slf4j.Logger; import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; import org.apache.rocketmq.proxy.common.ProxyContext; import org.apache.rocketmq.proxy.common.ProxyException; import org.apache.rocketmq.proxy.common.ProxyExceptionCode; -import org.apache.rocketmq.common.utils.StartAndShutdown; import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.config.ProxyConfig; import org.apache.rocketmq.proxy.service.admin.AdminService; -import org.apache.rocketmq.client.impl.mqclient.MQClientAPIFactory; import org.apache.rocketmq.proxy.service.route.AddressableMessageQueue; import org.apache.rocketmq.proxy.service.route.TopicRouteService; import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.protocol.header.SendMessageRequestHeader; import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel; +import java.nio.charset.StandardCharsets; +import java.time.Duration; + public abstract class AbstractSystemMessageSyncer implements StartAndShutdown, MessageListenerConcurrently { protected static final Logger log = LoggerFactory.getLogger(LoggerName.PROXY_LOGGER_NAME); protected final TopicRouteService topicRouteService; diff --git a/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/HeartbeatSyncer.java b/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/HeartbeatSyncer.java index fee3ea87d2..e063d79707 100644 --- a/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/HeartbeatSyncer.java +++ b/proxy/src/main/java/org/apache/rocketmq/proxy/service/sysmessage/HeartbeatSyncer.java @@ -17,21 +17,15 @@ package org.apache.rocketmq.proxy.service.sysmessage; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import io.netty.channel.Channel; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import org.apache.rocketmq.broker.client.ClientChannelInfo; import org.apache.rocketmq.broker.client.ConsumerGroupEvent; import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener; import org.apache.rocketmq.broker.client.ConsumerManager; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.client.impl.mqclient.MQClientAPIFactory; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.thread.ThreadPoolMonitor; @@ -40,13 +34,20 @@ import org.apache.rocketmq.proxy.config.ConfigurationManager; import org.apache.rocketmq.proxy.config.ProxyConfig; import org.apache.rocketmq.proxy.processor.channel.RemoteChannel; import org.apache.rocketmq.proxy.service.admin.AdminService; -import org.apache.rocketmq.client.impl.mqclient.MQClientAPIFactory; import org.apache.rocketmq.proxy.service.route.TopicRouteService; import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType; import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel; import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + public class HeartbeatSyncer extends AbstractSystemMessageSyncer { protected ThreadPoolExecutor threadPoolExecutor; diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java index 74803609ba..2caa4e37e9 100644 --- a/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationManagerTest.java @@ -21,6 +21,8 @@ import org.apache.rocketmq.proxy.ProxyMode; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class ConfigurationManagerTest extends InitConfigTest { @@ -44,4 +46,12 @@ public class ConfigurationManagerTest extends InitConfigTest { assertThat(ConfigurationManager.getProxyConfig()).isNotNull(); } + @Test + public void testFormatProxyConfig() { + String actual = ConfigurationManager.formatProxyConfig(); + assertNotNull(actual); + ProxyConfig expected = ConfigurationManager.getProxyConfig(); + assertTrue(actual.contains(expected.getProxyMode())); + assertTrue(actual.contains(expected.getProxyName())); + } } diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationTest.java new file mode 100644 index 0000000000..72b7eae6fa --- /dev/null +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/config/ConfigurationTest.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.proxy.config; + +import org.apache.rocketmq.auth.config.AuthConfig; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.spy; + +public class ConfigurationTest { + + private Configuration configuration; + + @Before + public void init() { + configuration = spy(new Configuration()); + } + + @Test + public void testInit() throws Exception { + configuration.init(); + + ProxyConfig loadedProxyConfig = configuration.getProxyConfig(); + assertNotNull(loadedProxyConfig); + + AuthConfig loadedAuthConfig = configuration.getAuthConfig(); + assertNotNull(loadedAuthConfig); + } +} diff --git a/proxy/src/test/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivityTest.java b/proxy/src/test/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivityTest.java new file mode 100644 index 0000000000..d956da52a1 --- /dev/null +++ b/proxy/src/test/java/org/apache/rocketmq/proxy/remoting/activity/GetTopicRouteActivityTest.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.proxy.remoting.activity; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.apache.rocketmq.common.MQVersion; +import org.apache.rocketmq.proxy.common.ProxyContext; +import org.apache.rocketmq.proxy.config.ConfigurationManager; +import org.apache.rocketmq.proxy.processor.MessagingProcessor; +import org.apache.rocketmq.proxy.remoting.pipeline.RequestPipeline; +import org.apache.rocketmq.proxy.service.channel.SimpleChannel; +import org.apache.rocketmq.proxy.service.channel.SimpleChannelHandlerContext; +import org.apache.rocketmq.proxy.service.route.ProxyTopicRouteData; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.remoting.protocol.RequestCode; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.apache.rocketmq.remoting.protocol.header.namesrv.GetRouteInfoRequestHeader; +import org.apache.rocketmq.remoting.protocol.route.BrokerData; +import org.apache.rocketmq.remoting.protocol.route.QueueData; +import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class GetTopicRouteActivityTest { + + @Mock + private RequestPipeline requestPipeline; + + @Mock + private MessagingProcessor messagingProcessor; + + private GetTopicRouteActivity getTopicRouteActivity; + + private ChannelHandlerContext ctx; + + private ProxyContext context; + + @Before + public void setup() throws Exception { + getTopicRouteActivity = new GetTopicRouteActivity(requestPipeline, messagingProcessor); + + ConfigurationManager.initEnv(); + ConfigurationManager.intConfig(); + + Channel channel = new SimpleChannel(null, "0.0.0.0:0", "1.1.1.1:1"); + ctx = new SimpleChannelHandlerContext(channel); + + context = ProxyContext.create(); + } + + @Test + public void testProcessRequest0_HighVersion_SerializeWithFeatures() throws Exception { + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, null); + request.setVersion(MQVersion.Version.V4_9_4.ordinal()); + + GetRouteInfoRequestHeader header = new GetRouteInfoRequestHeader(); + header.setTopic("TestTopic"); + header.setAcceptStandardJsonOnly(false); + request.writeCustomHeader(header); + + TopicRouteData topicRouteData = prepareTopicRouteData(); + + TopicRouteData spyTopicRouteData = Mockito.spy(topicRouteData); + + ProxyTopicRouteData proxyTopicRouteData = mock(ProxyTopicRouteData.class); + when(proxyTopicRouteData.buildTopicRouteData()).thenReturn(spyTopicRouteData); + when(messagingProcessor.getTopicRouteDataForProxy(any(ProxyContext.class), anyList(), any())) + .thenReturn(proxyTopicRouteData); + + RemotingCommand response = getTopicRouteActivity.processRequest0(ctx, request, context); + + assertNotNull(response); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + + verify(spyTopicRouteData).encode( + JSONWriter.Feature.BrowserCompatible, + JSONWriter.Feature.MapSortField + ); + + TopicRouteData deserializedData = JSON.parseObject(response.getBody(), TopicRouteData.class); + assertEquals(topicRouteData.getOrderTopicConf(), deserializedData.getOrderTopicConf()); + assertEquals(topicRouteData.getQueueDatas().size(), deserializedData.getQueueDatas().size()); + } + + @Test + public void testProcessRequest0_LowVersion_StandardJsonOnly_SerializeWithFeatures() throws Exception { + RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, null); + request.setVersion(MQVersion.Version.V4_9_3.ordinal()); + + GetRouteInfoRequestHeader header = new GetRouteInfoRequestHeader(); + header.setTopic("TestTopic"); + header.setAcceptStandardJsonOnly(true); + request.writeCustomHeader(header); + + TopicRouteData topicRouteData = prepareTopicRouteData(); + + TopicRouteData spyTopicRouteData = Mockito.spy(topicRouteData); + + ProxyTopicRouteData proxyTopicRouteData = mock(ProxyTopicRouteData.class); + when(proxyTopicRouteData.buildTopicRouteData()).thenReturn(spyTopicRouteData); + when(messagingProcessor.getTopicRouteDataForProxy(any(ProxyContext.class), anyList(), any())) + .thenReturn(proxyTopicRouteData); + + RemotingCommand response = getTopicRouteActivity.processRequest0(ctx, request, context); + + assertNotNull(response); + assertEquals(ResponseCode.SUCCESS, response.getCode()); + + verify(spyTopicRouteData).encode(); + } + + private TopicRouteData prepareTopicRouteData() { + TopicRouteData result = new TopicRouteData(); + result.setOrderTopicConf("orderTopicConf"); + + List queueDatas = new ArrayList<>(); + QueueData queueData = new QueueData(); + queueData.setBrokerName("broker-a"); + queueData.setPerm(6); + queueData.setReadQueueNums(4); + queueData.setWriteQueueNums(4); + queueData.setTopicSysFlag(0); + queueDatas.add(queueData); + result.setQueueDatas(queueDatas); + + List brokerDatas = new ArrayList<>(); + BrokerData brokerData = new BrokerData(); + brokerData.setBrokerName("broker-a"); + HashMap brokerAddrs = new HashMap<>(); + brokerAddrs.put(0L, "127.0.0.1:10911"); + brokerData.setBrokerAddrs(brokerAddrs); + brokerDatas.add(brokerData); + result.setBrokerDatas(brokerDatas); + return result; + } +} \ No newline at end of file diff --git a/remoting/BUILD.bazel b/remoting/BUILD.bazel index 9f806be763..62273e5e9d 100644 --- a/remoting/BUILD.bazel +++ b/remoting/BUILD.bazel @@ -22,7 +22,7 @@ java_library( visibility = ["//visibility:public"], deps = [ "//common", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_google_guava_guava", "@maven//:com_google_code_findbugs_jsr305", "@maven//:com_squareup_okio_okio_jvm", @@ -51,7 +51,9 @@ java_library( ":remoting", "//common", "//:test_deps", + "@maven//:org_objenesis_objenesis", "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_google_code_gson_gson", "@maven//:com_google_guava_guava", "@maven//:com_google_code_findbugs_jsr305", diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java index 7ed977d99c..e39967e3f8 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java @@ -16,8 +16,8 @@ */ package org.apache.rocketmq.remoting.netty; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; import com.google.common.base.Stopwatch; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.PooledByteBufAllocator; @@ -46,6 +46,28 @@ import io.netty.util.Timeout; import io.netty.util.TimerTask; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.common.Pair; +import org.apache.rocketmq.common.ThreadFactoryImpl; +import org.apache.rocketmq.common.constant.LoggerName; +import org.apache.rocketmq.common.utils.FutureUtils; +import org.apache.rocketmq.common.utils.NetworkUtil; +import org.apache.rocketmq.common.utils.ThreadUtils; +import org.apache.rocketmq.logging.org.slf4j.Logger; +import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; +import org.apache.rocketmq.remoting.ChannelEventListener; +import org.apache.rocketmq.remoting.InvokeCallback; +import org.apache.rocketmq.remoting.RemotingClient; +import org.apache.rocketmq.remoting.common.RemotingHelper; +import org.apache.rocketmq.remoting.exception.RemotingConnectException; +import org.apache.rocketmq.remoting.exception.RemotingSendRequestException; +import org.apache.rocketmq.remoting.exception.RemotingTimeoutException; +import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException; +import org.apache.rocketmq.remoting.protocol.RemotingCommand; +import org.apache.rocketmq.remoting.protocol.RequestCode; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.apache.rocketmq.remoting.proxy.SocksProxyConfig; + import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -70,27 +92,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.apache.commons.lang3.StringUtils; -import org.apache.rocketmq.common.Pair; -import org.apache.rocketmq.common.ThreadFactoryImpl; -import org.apache.rocketmq.common.constant.LoggerName; -import org.apache.rocketmq.common.utils.FutureUtils; -import org.apache.rocketmq.common.utils.NetworkUtil; -import org.apache.rocketmq.common.utils.ThreadUtils; -import org.apache.rocketmq.logging.org.slf4j.Logger; -import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; -import org.apache.rocketmq.remoting.ChannelEventListener; -import org.apache.rocketmq.remoting.InvokeCallback; -import org.apache.rocketmq.remoting.RemotingClient; -import org.apache.rocketmq.remoting.common.RemotingHelper; -import org.apache.rocketmq.remoting.exception.RemotingConnectException; -import org.apache.rocketmq.remoting.exception.RemotingSendRequestException; -import org.apache.rocketmq.remoting.exception.RemotingTimeoutException; -import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException; -import org.apache.rocketmq.remoting.protocol.RemotingCommand; -import org.apache.rocketmq.remoting.protocol.RequestCode; -import org.apache.rocketmq.remoting.protocol.ResponseCode; -import org.apache.rocketmq.remoting.proxy.SocksProxyConfig; import static org.apache.rocketmq.remoting.common.RemotingHelper.convertChannelFutureToCompletableFuture; diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/BitSetSerializerDeserializer.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/BitSetSerializerDeserializer.java index 8f53c0250b..3fe8eb2a0b 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/BitSetSerializerDeserializer.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/BitSetSerializerDeserializer.java @@ -16,37 +16,46 @@ */ package org.apache.rocketmq.remoting.protocol; -import com.alibaba.fastjson.parser.DefaultJSONParser; -import com.alibaba.fastjson.parser.JSONToken; -import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; -import com.alibaba.fastjson.serializer.JSONSerializer; -import com.alibaba.fastjson.serializer.ObjectSerializer; -import com.alibaba.fastjson.serializer.SerializeWriter; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.reader.ObjectReader; +import com.alibaba.fastjson2.writer.ObjectWriter; -import java.io.IOException; import java.lang.reflect.Type; +import java.util.Base64; import java.util.BitSet; -public class BitSetSerializerDeserializer implements ObjectSerializer, ObjectDeserializer { +public class BitSetSerializerDeserializer implements ObjectReader, ObjectWriter { @Override - public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { - SerializeWriter out = serializer.out; - out.writeByteArray(((BitSet) object).toByteArray()); - } - - @SuppressWarnings("unchecked") - @Override - public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { - byte[] bytes = parser.parseObject(byte[].class); - if (bytes != null) { - return (T) BitSet.valueOf(bytes); + public void write(JSONWriter writer, Object object, Object fieldName, Type fieldType, long features) { + if (object == null) { + writer.writeBase64(null); + } else { + writer.writeBase64(((BitSet) object).toByteArray()); } - return null; } @Override - public int getFastMatchToken() { - return JSONToken.LITERAL_STRING; + public BitSet readObject(JSONReader reader, Type fieldType, Object fieldName, long features) { + if (reader.nextIfNull()) { + return null; + } + String base64 = reader.readString(); + if (base64 == null || base64.isEmpty()) { + return null; + } + byte[] bytes = Base64.getDecoder().decode(base64); + return BitSet.valueOf(bytes); + } + + @Override + public long getFeatures() { + return 0L; + } + + @Override + public Class getObjectClass() { + return ObjectReader.super.getObjectClass(); } } diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java index 9b2b0f07b4..e08a1627d1 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java @@ -16,10 +16,20 @@ */ package org.apache.rocketmq.remoting.protocol; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; import com.google.common.base.Stopwatch; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.common.BoundaryType; +import org.apache.rocketmq.common.constant.LoggerName; +import org.apache.rocketmq.logging.org.slf4j.Logger; +import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; +import org.apache.rocketmq.remoting.CommandCallback; +import org.apache.rocketmq.remoting.CommandCustomHeader; +import org.apache.rocketmq.remoting.annotation.CFNotNull; +import org.apache.rocketmq.remoting.exception.RemotingCommandException; + import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -34,16 +44,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import org.apache.commons.lang3.StringUtils; -import org.apache.rocketmq.common.BoundaryType; -import org.apache.rocketmq.common.constant.LoggerName; -import org.apache.rocketmq.logging.org.slf4j.Logger; -import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; -import org.apache.rocketmq.remoting.CommandCallback; -import org.apache.rocketmq.remoting.CommandCustomHeader; -import org.apache.rocketmq.remoting.annotation.CFNotNull; -import org.apache.rocketmq.remoting.exception.RemotingCommandException; - public class RemotingCommand { public static final String SERIALIZE_TYPE_PROPERTY = "rocketmq.serialize.type"; public static final String SERIALIZE_TYPE_ENV = "ROCKETMQ_SERIALIZE_TYPE"; diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingSerializable.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingSerializable.java index 139a7043d8..c4e4da1468 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingSerializable.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingSerializable.java @@ -16,8 +16,9 @@ */ package org.apache.rocketmq.remoting.protocol; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; + import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; @@ -34,7 +35,10 @@ public abstract class RemotingSerializable { } public static String toJson(final Object obj, boolean prettyFormat) { - return JSON.toJSONString(obj, prettyFormat); + if (prettyFormat) { + return JSON.toJSONString(obj, JSONWriter.Feature.PrettyFormat); + } + return JSON.toJSONString(obj); } public static T decode(final byte[] data, Class classOfT) { @@ -74,7 +78,7 @@ public abstract class RemotingSerializable { * @param features Features to apply * @return serialized data. */ - public byte[] encode(SerializerFeature...features) { + public byte[] encode(JSONWriter.Feature... features) { final String json = JSON.toJSONString(this, features); return json.getBytes(CHARSET_UTF8); } diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/BatchAck.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/BatchAck.java index 82dcd8567e..6107bdce02 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/BatchAck.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/BatchAck.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.remoting.protocol.body; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; import org.apache.rocketmq.remoting.protocol.BitSetSerializerDeserializer; import java.io.Serializable; diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/RegisterBrokerBody.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/RegisterBrokerBody.java index 7312b70233..03920855bb 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/RegisterBrokerBody.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/body/RegisterBrokerBody.java @@ -17,7 +17,17 @@ package org.apache.rocketmq.remoting.protocol.body; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; +import org.apache.rocketmq.common.MQVersion; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.TopicConfig; +import org.apache.rocketmq.common.constant.LoggerName; +import org.apache.rocketmq.logging.org.slf4j.Logger; +import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; +import org.apache.rocketmq.remoting.protocol.DataVersion; +import org.apache.rocketmq.remoting.protocol.RemotingSerializable; +import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingInfo; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -30,15 +40,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; -import org.apache.rocketmq.common.MQVersion; -import org.apache.rocketmq.common.MixAll; -import org.apache.rocketmq.common.TopicConfig; -import org.apache.rocketmq.common.constant.LoggerName; -import org.apache.rocketmq.logging.org.slf4j.Logger; -import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; -import org.apache.rocketmq.remoting.protocol.DataVersion; -import org.apache.rocketmq.remoting.protocol.RemotingSerializable; -import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingInfo; public class RegisterBrokerBody extends RemotingSerializable { diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/HeartbeatData.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/HeartbeatData.java index f7b4b9faef..d915bf0d16 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/HeartbeatData.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/HeartbeatData.java @@ -20,10 +20,12 @@ */ package org.apache.rocketmq.remoting.protocol.heartbeat; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import org.apache.rocketmq.remoting.protocol.RemotingSerializable; + import java.util.HashSet; import java.util.Set; -import com.alibaba.fastjson.JSON; -import org.apache.rocketmq.remoting.protocol.RemotingSerializable; public class HeartbeatData extends RemotingSerializable { private String clientID; @@ -79,7 +81,7 @@ public class HeartbeatData extends RemotingSerializable { } public int computeHeartbeatFingerprint() { - HeartbeatData heartbeatDataCopy = JSON.parseObject(JSON.toJSONString(this), HeartbeatData.class); + HeartbeatData heartbeatDataCopy = JSON.parseObject(JSON.toJSONString(this, JSONWriter.Feature.ReferenceDetection), HeartbeatData.class); for (ConsumerData consumerData : heartbeatDataCopy.getConsumerDataSet()) { for (SubscriptionData subscriptionData : consumerData.getSubscriptionDataSet()) { subscriptionData.setSubVersion(0L); @@ -88,6 +90,6 @@ public class HeartbeatData extends RemotingSerializable { heartbeatDataCopy.setWithoutSub(false); heartbeatDataCopy.setHeartbeatFingerprint(0); heartbeatDataCopy.setClientID(""); - return JSON.toJSONString(heartbeatDataCopy).hashCode(); + return JSON.toJSONString(heartbeatDataCopy, JSONWriter.Feature.ReferenceDetection).hashCode(); } } diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/SubscriptionData.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/SubscriptionData.java index 59088fc42e..f110fac061 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/SubscriptionData.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/heartbeat/SubscriptionData.java @@ -20,10 +20,11 @@ */ package org.apache.rocketmq.remoting.protocol.heartbeat; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; +import org.apache.rocketmq.common.filter.ExpressionType; + import java.util.HashSet; import java.util.Set; -import org.apache.rocketmq.common.filter.ExpressionType; public class SubscriptionData implements Comparable { public final static String SUB_ALL = "*"; diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/subscription/GroupRetryPolicy.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/subscription/GroupRetryPolicy.java index 14d5e53769..00bf2e9576 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/subscription/GroupRetryPolicy.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/subscription/GroupRetryPolicy.java @@ -17,7 +17,7 @@ package org.apache.rocketmq.remoting.protocol.subscription; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; import com.google.common.base.MoreObjects; public class GroupRetryPolicy { diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableCompatTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableCompatTest.java new file mode 100644 index 0000000000..35c1c7b891 --- /dev/null +++ b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableCompatTest.java @@ -0,0 +1,425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.remoting.protocol; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.JSON; +import org.apache.rocketmq.remoting.protocol.body.BatchAck; +import org.junit.Test; +import org.objenesis.ObjenesisStd; +import org.reflections.Reflections; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class RemotingSerializableCompatTest { + + @Test + public void testCompatibilityCheck() { + Reflections reflections = new Reflections("org.apache.rocketmq.remoting.protocol"); + Set> subTypes = reflections.getSubTypesOf(RemotingSerializable.class); + + for (Class clazz : subTypes) { + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()) || clazz.getSimpleName().endsWith("Test") + || clazz.isAnonymousClass() || clazz.getName().contains("$")) { + continue; + } + try { + RemotingSerializable instance; + try { + instance = clazz.getDeclaredConstructor().newInstance(); + } catch (NoSuchMethodException e) { + instance = allocateInstance(clazz); + } + fillDefaultFields(instance, clazz); + assertTrue(checkCompatible(instance, clazz)); + } catch (Exception e) { + System.err.printf("Class %s: incompatible, error: %s\n", clazz.getName(), e.getMessage()); + } + } + } + + @Test + public void testCompatibilityCheckWithBitSet() { + BitSet bitSet = new BitSet(); + bitSet.set(1); + bitSet.set(3); + bitSet.set(5); + String fastjson1Str = "{\"b\":\"Kg==\",\"c\":\"DEFAULT_CONSUMER\",\"it\":5000,\"pt\":1760694281326,\"q\":1,\"r\":\"0\",\"rq\":2,\"so\":100,\"t\":\"myTopic\"}"; + BatchAck batchAck = JSON.parseObject(fastjson1Str, BatchAck.class); + assertEquals(bitSet, batchAck.getBitSet()); + assertEquals("DEFAULT_CONSUMER", batchAck.getConsumerGroup()); + assertEquals(5000, batchAck.getInvisibleTime()); + assertEquals(1760694281326L, batchAck.getPopTime()); + assertEquals(1, batchAck.getQueueId()); + assertEquals("0", batchAck.getRetry()); + assertEquals(2, batchAck.getReviveQueueId()); + assertEquals(100, batchAck.getStartOffset()); + assertEquals("myTopic", batchAck.getTopic()); + } + + private void fillDefaultFields(final Object obj, final Class clazz) throws Exception { + if (null == clazz || clazz == Object.class) { + return; + } + for (Field field : clazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + field.setAccessible(true); + Class type = field.getType(); + + if (type.isArray()) { + Class componentType = type.getComponentType(); + Object arr = Array.newInstance(componentType, 1); + Object element = createElementOrDefault(componentType); + if (element != null) { + Array.set(arr, 0, element); + } + field.set(obj, arr); + } else if (Properties.class.isAssignableFrom(type)) { + field.set(obj, new Properties()); + } else if (type.isEnum()) { + Object[] enumConstants = type.getEnumConstants(); + if (enumConstants != null && enumConstants.length > 0) { + field.set(obj, enumConstants[0]); + } + } else if (ConcurrentHashMap.KeySetView.class.isAssignableFrom(type)) { + field.set(obj, ConcurrentHashMap.newKeySet()); + } else if (ConcurrentHashMap.class.isAssignableFrom(type) || ConcurrentMap.class.isAssignableFrom(type)) { + field.set(obj, new ConcurrentHashMap<>()); + } else if (Set.class.isAssignableFrom(type)) { + Set set = type.isInterface() ? new HashSet<>() : (Set) type.getDeclaredConstructor().newInstance(); + Class genericType = getFirstGenericType(field); + Object element = createElementOrDefault(genericType); + if (element != null) + set.add(element); + field.set(obj, set); + } else if (List.class.isAssignableFrom(type)) { + List list = new ArrayList<>(); + Class genericType = getFirstGenericType(field); + Object element = createElementOrDefault(genericType); + if (null != element) { + list.add(element); + } + field.set(obj, list); + } else if (Map.class.isAssignableFrom(type)) { + Map map = type.isInterface() ? new HashMap<>() : (Map) type.getDeclaredConstructor().newInstance(); + Class keyType = getGenericType(field, 0); + Class valueType = getGenericType(field, 1); + Object key = createElementOrDefault(keyType); + Object value = createElementOrDefault(valueType); + if (null != key && null != value) { + map.put(key, value); + } + field.set(obj, map); + } else if (type == AtomicLong.class) { + field.set(obj, new AtomicLong(1)); + } else { + Object value = getDefaultValue(type); + if (null != value) { + field.set(obj, value); + } else if (!type.isPrimitive() && !type.getName().startsWith("java.")) { + Object subObj; + try { + subObj = type.getDeclaredConstructor().newInstance(); + } catch (NoSuchMethodException e) { + subObj = allocateInstance(type); + } + fillDefaultFields(subObj, type); + field.set(obj, subObj); + } + } + } + fillDefaultFields(obj, clazz.getSuperclass()); + } + + private Object createElementOrDefault(final Class type) throws Exception { + if (null == type) { + return null; + } + Object value = getDefaultValue(type); + if (null != value) { + return value; + } + if (type.isEnum()) { + Object[] enumConstants = type.getEnumConstants(); + if (null != enumConstants && enumConstants.length > 0) { + return enumConstants[0]; + } + return null; + } + if (type.isArray()) { + Class componentType = type.getComponentType(); + Object arr = Array.newInstance(componentType, 1); + Object element = createElementOrDefault(componentType); + if (null != element) { + Array.set(arr, 0, element); + } + return arr; + } + if (!type.isPrimitive()) { + Object obj; + try { + obj = type.getDeclaredConstructor().newInstance(); + } catch (NoSuchMethodException e) { + obj = allocateInstance(type); + } + fillDefaultFields(obj, type); + return obj; + } + return null; + } + + private Class getFirstGenericType(final Field field) { + return getGenericType(field, 0); + } + + private Class getGenericType(final Field field, final int index) { + try { + java.lang.reflect.Type genericType = field.getGenericType(); + if (genericType instanceof java.lang.reflect.ParameterizedType) { + java.lang.reflect.Type[] types = ((java.lang.reflect.ParameterizedType) genericType).getActualTypeArguments(); + if (types.length > index && types[index] instanceof Class) { + return (Class) types[index]; + } + } + } catch (Exception ignored) { + } + return null; + } + + private Object getDefaultValue(final Class type) { + if (null == type) { + return null; + } + if (type == boolean.class || type == Boolean.class) { + return false; + } + if (type == byte.class || type == Byte.class) { + return (byte) 1; + } + if (type == short.class || type == Short.class) { + return (short) 1; + } + if (type == int.class || type == Integer.class) { + return 1; + } + if (type == long.class || type == Long.class) { + return 1L; + } + if (type == float.class || type == Float.class) { + return 1f; + } + if (type == double.class || type == Double.class) { + return 1d; + } + if (type == char.class || type == Character.class) { + return '\0'; + } + if (type == String.class) { + return "test"; + } + return null; + } + + private boolean checkCompatible(final Object original, final Object deserialized, final String path, final Map visited) { + if (null == original && null == deserialized) { + return true; + } + if (null == original || null == deserialized) { + System.err.printf("Objects at %s incompatible: one is null\n", path); + return false; + } + + if (!isPrimitiveOrWrapper(original.getClass())) { + if (visited.containsKey(original)) { + return true; + } + visited.put(original, deserialized); + } + + Class clazz = original.getClass(); + boolean result = true; + for (Field field : clazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + JSONField jsonField = field.getAnnotation(JSONField.class); + if (null != jsonField && !jsonField.serialize()) { + continue; + } + if ("hash".equals(field.getName()) || "serialVersionUID".equals(field.getName())) { + continue; + } + + field.setAccessible(true); + try { + Object v1 = field.get(original); + Object v2 = field.get(deserialized); + String fieldPath = path + "." + field.getName(); + + if (null == v1 && null == v2) { + continue; + } + if (v1 instanceof Random && v2 instanceof Random) { + continue; + } + if (v1 instanceof AtomicLong && v2 instanceof AtomicLong) { + if (((AtomicLong) v1).get() != ((AtomicLong) v2).get()) { + result = false; + System.err.printf("Field %s incompatible: original=%s, deserialized=%s\n", fieldPath, v1, v2); + } + continue; + } + if (v1 instanceof Set && v2 instanceof Set) { + Set s1 = (Set) v1, s2 = (Set) v2; + if (s1.size() != s2.size()) { + result = false; + System.err.printf("Field %s incompatible: set size original=%d, deserialized=%d\n", fieldPath, s1.size(), s2.size()); + } else if (!s1.isEmpty()) { + List list1 = new ArrayList<>(s1); + List list2 = new ArrayList<>(s2); + if (new HashSet<>(list1).equals(new HashSet<>(list2))) { + continue; + } + boolean elementsCompatible = true; + for (Object e1 : list1) { + boolean foundMatch = false; + for (Object e2 : list2) { + if (checkCompatible(e1, e2, fieldPath + ".element", new HashMap<>(visited))) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + elementsCompatible = false; + break; + } + } + if (!elementsCompatible) { + result = false; + System.err.printf("Field %s incompatible: sets have different elements\n", fieldPath); + } + } + continue; + } + if (v1 instanceof List && v2 instanceof List) { + List l1 = (List) v1, l2 = (List) v2; + if (l1.size() != l2.size()) { + result = false; + System.err.printf("Field %s incompatible: list size original=%d, deserialized=%d\n", fieldPath, l1.size(), l2.size()); + } else { + for (int i = 0; i < l1.size(); i++) { + Object e1 = l1.get(i); + Object e2 = l2.get(i); + if (!checkCompatible(e1, e2, fieldPath + "[" + i + "]", new HashMap<>(visited))) { + result = false; + } + } + } + continue; + } + if (v1 instanceof Map && v2 instanceof Map) { + Map m1 = (Map) v1, m2 = (Map) v2; + if (!m1.keySet().equals(m2.keySet())) { + result = false; + System.err.printf("Field %s incompatible: map keys original=%s, deserialized=%s\n", fieldPath, m1.keySet(), m2.keySet()); + } else { + for (Object key : m1.keySet()) { + Object val1 = m1.get(key), val2 = m2.get(key); + if (val1 != null && val2 != null) { + if (!checkCompatible(val1, val2, fieldPath + "[" + key + "]", new HashMap<>(visited))) { + result = false; + } + } else if (val1 != val2) { + result = false; + System.err.printf("Field %s key %s incompatible: original=%s, deserialized=%s\n", + fieldPath, key, val1, val2); + } + } + } + continue; + } + Class type = field.getType(); + if (null != v1 && null != v2 && !type.isPrimitive() && !type.getName().startsWith("java.")) { + if (!checkCompatible(v1, v2, fieldPath, new HashMap<>(visited))) { + result = false; + } + continue; + } + if (null == v1 || null == v2 || !v1.equals(v2)) { + result = false; + System.err.printf("Field %s incompatible: original=%s, deserialized=%s\n", fieldPath, v1, v2); + } + } catch (Exception e) { + result = false; + System.err.printf("Field %s error: %s\n", path + "." + field.getName(), e.getMessage()); + } + } + if (result) { + System.out.printf("Class %s compatible\n", path); + } + return result; + } + + private boolean isPrimitiveOrWrapper(final Class clazz) { + return clazz.isPrimitive() || + clazz == String.class || + clazz == Boolean.class || + clazz == Character.class || + clazz == Byte.class || + clazz == Short.class || + clazz == Integer.class || + clazz == Long.class || + clazz == Float.class || + clazz == Double.class; + } + + private boolean checkCompatible(final Object original, final Class clazz) { + String json = com.alibaba.fastjson.JSON.toJSONString(original); + Object deserialized; + try { + deserialized = com.alibaba.fastjson2.JSON.parseObject(json, clazz); + } catch (Exception e) { + System.err.printf("Deserialization failed for %s: %s\n", clazz.getName(), e.getMessage()); + return false; + } + return checkCompatible(original, deserialized, clazz.getSimpleName(), new HashMap<>()); + } + + private T allocateInstance(final Class clazz) { + return new ObjenesisStd().newInstance(clazz); + } +} diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableTest.java index 6bd80217da..e2655fd35d 100644 --- a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableTest.java +++ b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/RemotingSerializableTest.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.remoting.protocol; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSONWriter; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.TypeAdapter; @@ -105,8 +105,7 @@ public class RemotingSerializableTest { } Foo foo = new Foo(); String invalid = new String(foo.encode(), Charset.defaultCharset()); - String valid = new String(foo.encode(SerializerFeature.BrowserCompatible, SerializerFeature.QuoteFieldNames, - SerializerFeature.MapSortField), Charset.defaultCharset()); + String valid = new String(foo.encode(JSONWriter.Feature.BrowserCompatible, JSONWriter.Feature.MapSortField), Charset.defaultCharset()); Gson gson = new Gson(); final TypeAdapter strictAdapter = gson.getAdapter(JsonElement.class); diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/body/BatchAckTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/body/BatchAckTest.java index 427a132d64..81e8f13394 100644 --- a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/body/BatchAckTest.java +++ b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/body/BatchAckTest.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.remoting.protocol.body; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.apache.rocketmq.common.MixAll; import org.junit.Test; diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/statictopic/TopicQueueMappingTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/statictopic/TopicQueueMappingTest.java index 6b8a1392f5..7bb7b8ca7e 100644 --- a/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/statictopic/TopicQueueMappingTest.java +++ b/remoting/src/test/java/org/apache/rocketmq/remoting/protocol/statictopic/TopicQueueMappingTest.java @@ -17,15 +17,15 @@ package org.apache.rocketmq.remoting.protocol.statictopic; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.google.common.collect.ImmutableList; -import java.util.Map; import org.apache.rocketmq.remoting.protocol.RemotingSerializable; import org.junit.Assert; import org.junit.Test; +import java.util.Map; + public class TopicQueueMappingTest { @Test @@ -59,7 +59,6 @@ public class TopicQueueMappingTest { Assert.assertTrue(mappingDetailMap.containsKey("currIdMap")); Assert.assertEquals(8, mappingDetailMap.size()); Assert.assertEquals(1, ((JSONObject) mappingDetailMap.get("hostedQueues")).size()); - Assert.assertEquals(1, ((JSONArray)((JSONObject) mappingDetailMap.get("hostedQueues")).get("0")).size()); } { TopicQueueMappingDetail mappingDetailFromJson = RemotingSerializable.decode(mappingDetailJson.getBytes(), TopicQueueMappingDetail.class); @@ -69,9 +68,4 @@ public class TopicQueueMappingTest { Assert.assertEquals(mappingDetailJson, RemotingSerializable.toJson(mappingDetailFromJson, false)); } } - - @Test - public void test() { - - } } diff --git a/srvutil/BUILD.bazel b/srvutil/BUILD.bazel index 699d78bcd9..8909409810 100644 --- a/srvutil/BUILD.bazel +++ b/srvutil/BUILD.bazel @@ -26,7 +26,6 @@ java_library( "@maven//:commons_validator_commons_validator", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", "@maven//:io_netty_netty_all", "@maven//:commons_cli_commons_cli", "@maven//:com_googlecode_concurrentlinkedhashmap_concurrentlinkedhashmap_lru", diff --git a/store/BUILD.bazel b/store/BUILD.bazel index de98657b22..8986c9b236 100644 --- a/store/BUILD.bazel +++ b/store/BUILD.bazel @@ -23,7 +23,6 @@ java_library( deps = [ "//common", "//remoting", - "@maven//:com_alibaba_fastjson", "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_conversantmedia_disruptor", "@maven//:com_google_guava_guava", @@ -58,7 +57,7 @@ java_library( "//:test_deps", "//common", "//remoting", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:com_conversantmedia_disruptor", "@maven//:io_openmessaging_storage_dledger", "@maven//:org_apache_commons_commons_lang3", diff --git a/store/src/main/java/org/apache/rocketmq/store/pop/AckMsg.java b/store/src/main/java/org/apache/rocketmq/store/pop/AckMsg.java index 3e65c104b1..f722c123ea 100644 --- a/store/src/main/java/org/apache/rocketmq/store/pop/AckMsg.java +++ b/store/src/main/java/org/apache/rocketmq/store/pop/AckMsg.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.store.pop; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; public class AckMsg { diff --git a/store/src/main/java/org/apache/rocketmq/store/pop/BatchAckMsg.java b/store/src/main/java/org/apache/rocketmq/store/pop/BatchAckMsg.java index 991a1f085d..f2689dfdfb 100644 --- a/store/src/main/java/org/apache/rocketmq/store/pop/BatchAckMsg.java +++ b/store/src/main/java/org/apache/rocketmq/store/pop/BatchAckMsg.java @@ -16,7 +16,8 @@ */ package org.apache.rocketmq.store.pop; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; + import java.util.ArrayList; import java.util.List; diff --git a/store/src/main/java/org/apache/rocketmq/store/pop/PopCheckPoint.java b/store/src/main/java/org/apache/rocketmq/store/pop/PopCheckPoint.java index 38e0a20752..e3587aa28c 100644 --- a/store/src/main/java/org/apache/rocketmq/store/pop/PopCheckPoint.java +++ b/store/src/main/java/org/apache/rocketmq/store/pop/PopCheckPoint.java @@ -16,7 +16,8 @@ */ package org.apache.rocketmq.store.pop; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; + import java.util.ArrayList; import java.util.List; @@ -35,7 +36,6 @@ public class PopCheckPoint implements Comparable { private int queueId; @JSONField(name = "t") private String topic; - @JSONField(name = "c") private String cid; @JSONField(name = "ro") private long reviveOffset; @@ -114,10 +114,12 @@ public class PopCheckPoint implements Comparable { this.topic = topic; } + @JSONField(name = "c") public String getCId() { return cid; } + @JSONField(name = "c") public void setCId(String cid) { this.cid = cid; } diff --git a/store/src/main/java/org/apache/rocketmq/store/queue/CombineConsumeQueueStore.java b/store/src/main/java/org/apache/rocketmq/store/queue/CombineConsumeQueueStore.java index c8c3202a78..5de1664cdb 100644 --- a/store/src/main/java/org/apache/rocketmq/store/queue/CombineConsumeQueueStore.java +++ b/store/src/main/java/org/apache/rocketmq/store/queue/CombineConsumeQueueStore.java @@ -17,7 +17,7 @@ package org.apache.rocketmq.store.queue; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import com.google.common.annotations.VisibleForTesting; import java.util.Arrays; import java.util.LinkedList; diff --git a/store/src/main/java/org/apache/rocketmq/store/timer/TimerMetrics.java b/store/src/main/java/org/apache/rocketmq/store/timer/TimerMetrics.java index ba7240414a..338a62252f 100644 --- a/store/src/main/java/org/apache/rocketmq/store/timer/TimerMetrics.java +++ b/store/src/main/java/org/apache/rocketmq/store/timer/TimerMetrics.java @@ -16,9 +16,21 @@ */ package org.apache.rocketmq.store.timer; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import org.apache.rocketmq.common.ConfigManager; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.constant.LoggerName; +import org.apache.rocketmq.common.message.MessageConst; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.logging.org.slf4j.Logger; +import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; +import org.apache.rocketmq.remoting.protocol.DataVersion; +import org.apache.rocketmq.remoting.protocol.RemotingSerializable; + import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.io.StringWriter; import java.io.Writer; @@ -36,16 +48,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.apache.rocketmq.common.ConfigManager; -import org.apache.rocketmq.common.MixAll; -import org.apache.rocketmq.common.constant.LoggerName; -import org.apache.rocketmq.common.message.MessageConst; -import org.apache.rocketmq.common.message.MessageExt; -import org.apache.rocketmq.common.topic.TopicValidator; -import org.apache.rocketmq.logging.org.slf4j.Logger; -import org.apache.rocketmq.logging.org.slf4j.LoggerFactory; -import org.apache.rocketmq.remoting.protocol.DataVersion; -import org.apache.rocketmq.remoting.protocol.RemotingSerializable; public class TimerMetrics extends ConfigManager { private static final Logger log = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME); @@ -135,11 +137,11 @@ public class TimerMetrics extends ConfigManager { return timingCount; } - protected void write0(Writer writer) { + protected void write0(Writer writer) throws IOException { TimerMetricsSerializeWrapper wrapper = new TimerMetricsSerializeWrapper(); wrapper.setTimingCount(timingCount); wrapper.setDataVersion(dataVersion); - JSON.writeJSONString(writer, wrapper, SerializerFeature.BrowserCompatible); + writer.write(JSON.toJSONString(wrapper, JSONWriter.Feature.BrowserCompatible)); } @Override public String encode() { @@ -152,7 +154,7 @@ public class TimerMetrics extends ConfigManager { @Override public void decode(String jsonString) { if (jsonString != null) { - TimerMetricsSerializeWrapper timerMetricsSerializeWrapper = TimerMetricsSerializeWrapper.fromJson(jsonString, TimerMetricsSerializeWrapper.class); + TimerMetricsSerializeWrapper timerMetricsSerializeWrapper = TimerMetricsSerializeWrapper.fromJson(jsonString, TimerMetricsSerializeWrapper.class); if (timerMetricsSerializeWrapper != null) { this.timingCount.putAll(timerMetricsSerializeWrapper.getTimingCount()); this.dataVersion.assignNewOne(timerMetricsSerializeWrapper.getDataVersion()); @@ -179,7 +181,7 @@ public class TimerMetrics extends ConfigManager { while (iterator.hasNext()) { Map.Entry entry = iterator.next(); final String topic = entry.getKey(); - if (topic.startsWith(TopicValidator.SYSTEM_TOPIC_PREFIX) || topic.startsWith(MixAll.LMQ_PREFIX)) { + if (topic.startsWith(TopicValidator.SYSTEM_TOPIC_PREFIX) || topic.startsWith(MixAll.LMQ_PREFIX)) { continue; } if (topics.contains(topic)) { diff --git a/store/src/test/java/org/apache/rocketmq/store/pop/AckMsgTest.java b/store/src/test/java/org/apache/rocketmq/store/pop/AckMsgTest.java index b5a3ff6381..13df028e83 100644 --- a/store/src/test/java/org/apache/rocketmq/store/pop/AckMsgTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/pop/AckMsgTest.java @@ -17,7 +17,7 @@ package org.apache.rocketmq.store.pop; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.junit.Assert; import org.junit.Test; diff --git a/store/src/test/java/org/apache/rocketmq/store/pop/BatchAckMsgTest.java b/store/src/test/java/org/apache/rocketmq/store/pop/BatchAckMsgTest.java index 4bcfcf18be..0a1bc714cf 100644 --- a/store/src/test/java/org/apache/rocketmq/store/pop/BatchAckMsgTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/pop/BatchAckMsgTest.java @@ -17,7 +17,7 @@ package org.apache.rocketmq.store.pop; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.junit.Assert; import org.junit.Test; diff --git a/test/BUILD.bazel b/test/BUILD.bazel index a8a7a9d8bb..d34456f355 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -28,7 +28,6 @@ java_library( "//tools", "@maven//:ch_qos_logback_logback_classic", "@maven//:ch_qos_logback_logback_core", - "@maven//:com_alibaba_fastjson", "@maven//:com_github_luben_zstd_jni", "@maven//:com_google_guava_guava", "@maven//:com_google_protobuf_protobuf_java_util", diff --git a/tieredstore/BUILD.bazel b/tieredstore/BUILD.bazel index 8822280ff8..0a94302086 100644 --- a/tieredstore/BUILD.bazel +++ b/tieredstore/BUILD.bazel @@ -39,7 +39,7 @@ java_library( "@maven//:io_opentelemetry_opentelemetry_exporter_logging_otlp", "@maven//:org_apache_commons_commons_lang3", "@maven//:org_apache_tomcat_annotations_api", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:org_apache_rocketmq_rocketmq_rocksdb", "@maven//:commons_collections_commons_collections", "@maven//:org_slf4j_slf4j_api", @@ -57,7 +57,7 @@ java_library( "//common", "//remoting", "//store", - "@maven//:com_alibaba_fastjson", + "@maven//:com_alibaba_fastjson2_fastjson2", "@maven//:commons_io_commons_io", "@maven//:io_opentelemetry_opentelemetry_api", "@maven//:io_opentelemetry_opentelemetry_context", diff --git a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/FlatMessageFile.java b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/FlatMessageFile.java index b0e4dd6e3b..a7505b4bf4 100644 --- a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/FlatMessageFile.java +++ b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/FlatMessageFile.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.tieredstore.file; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import com.google.common.annotations.VisibleForTesting; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/DefaultMetadataStore.java b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/DefaultMetadataStore.java index 09500bf6da..eb726543cf 100644 --- a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/DefaultMetadataStore.java +++ b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/DefaultMetadataStore.java @@ -16,17 +16,9 @@ */ package org.apache.rocketmq.tieredstore.metadata; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import com.google.common.annotations.VisibleForTesting; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; import org.apache.rocketmq.common.ConfigManager; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.remoting.protocol.RemotingSerializable; @@ -36,6 +28,15 @@ import org.apache.rocketmq.tieredstore.metadata.entity.FileSegmentMetadata; import org.apache.rocketmq.tieredstore.metadata.entity.QueueMetadata; import org.apache.rocketmq.tieredstore.metadata.entity.TopicMetadata; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + public class DefaultMetadataStore extends ConfigManager implements MetadataStore { private static final int DEFAULT_CAPACITY = 1024; @@ -80,12 +81,9 @@ public class DefaultMetadataStore extends ConfigManager implements MetadataStore dataWrapper.setIndexFileSegmentTable(new ConcurrentHashMap<>(indexFileSegmentTable)); if (prettyFormat) { - return JSON.toJSONString( - dataWrapper, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.PrettyFormat); - } else { - return JSON.toJSONString( - dataWrapper, SerializerFeature.DisableCircularReferenceDetect); + return JSON.toJSONString(dataWrapper, JSONWriter.Feature.PrettyFormat); } + return JSON.toJSONString(dataWrapper); } @Override diff --git a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/FileSegmentMetadata.java b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/FileSegmentMetadata.java index 4f988ca241..da2a0fd06c 100644 --- a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/FileSegmentMetadata.java +++ b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/FileSegmentMetadata.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.tieredstore.metadata.entity; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; import java.util.Objects; public class FileSegmentMetadata { diff --git a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/QueueMetadata.java b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/QueueMetadata.java index 6720f1d08a..3f976f037d 100644 --- a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/QueueMetadata.java +++ b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/QueueMetadata.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.tieredstore.metadata.entity; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; import org.apache.rocketmq.common.message.MessageQueue; public class QueueMetadata { diff --git a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/TopicMetadata.java b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/TopicMetadata.java index 80e5230e7a..72b994fc84 100644 --- a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/TopicMetadata.java +++ b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/metadata/entity/TopicMetadata.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.tieredstore.metadata.entity; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; public class TopicMetadata { diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index ec6fa1eaac..a809a7a92e 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -29,7 +29,6 @@ java_library( "@maven//:commons_validator_commons_validator", "@maven//:com_github_luben_zstd_jni", "@maven//:org_lz4_lz4_java", - "@maven//:com_alibaba_fastjson", "@maven//:io_netty_netty_all", "@maven//:commons_cli_commons_cli", "@maven//:org_slf4j_slf4j_api", diff --git a/tools/pom.xml b/tools/pom.xml index 8eea4977e6..1d63ded9b8 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -44,10 +44,6 @@ ${project.groupId} rocketmq-srvutil - - com.alibaba - fastjson - org.apache.commons commons-lang3 diff --git a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java index 948c4430d5..f2e1cda2bd 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java @@ -16,7 +16,7 @@ */ package org.apache.rocketmq.tools.admin; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.client.QueryResult; diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetColdDataFlowCtrInfoSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetColdDataFlowCtrInfoSubCommand.java index 34b3ba7d30..76c111b95c 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetColdDataFlowCtrInfoSubCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetColdDataFlowCtrInfoSubCommand.java @@ -16,13 +16,9 @@ */ package org.apache.rocketmq.tools.command.broker; -import java.io.UnsupportedEncodingException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Map; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -37,6 +33,12 @@ import org.apache.rocketmq.tools.command.CommandUtil; import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; +import java.io.UnsupportedEncodingException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; + public class GetColdDataFlowCtrInfoSubCommand implements SubCommand { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -116,7 +118,7 @@ public class GetColdDataFlowCtrInfoSubCommand implements SubCommand { value.remove("createTimeMills"); }); - String formatStr = JSON.toJSONString(jsonObject, true); + String formatStr = JSON.toJSONString(jsonObject, JSONWriter.Feature.PrettyFormat); System.out.printf(formatStr); System.out.printf("%n"); } diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/UpdateSubGroupSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/UpdateSubGroupSubCommand.java index b17da4de45..d4782a0684 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/UpdateSubGroupSubCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/UpdateSubGroupSubCommand.java @@ -16,9 +16,7 @@ */ package org.apache.rocketmq.tools.command.consumer; -import com.alibaba.fastjson.JSON; -import java.util.Map; -import java.util.Set; +import com.alibaba.fastjson2.JSON; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -32,6 +30,9 @@ import org.apache.rocketmq.tools.command.CommandUtil; import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; +import java.util.Map; +import java.util.Set; + public class UpdateSubGroupSubCommand implements SubCommand { @Override diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportConfigsCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportConfigsCommand.java index c3f96d5972..ebb1cf82ba 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportConfigsCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportConfigsCommand.java @@ -16,15 +16,8 @@ */ package org.apache.rocketmq.tools.command.export; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Arrays; -import java.util.Properties; - -import com.alibaba.fastjson.JSON; - +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -35,6 +28,13 @@ import org.apache.rocketmq.tools.command.CommandUtil; import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + public class ExportConfigsCommand implements SubCommand { @Override public String commandName() { @@ -98,7 +98,7 @@ public class ExportConfigsCommand implements SubCommand { result.put("clusterScale", clusterScaleMap); String path = filePath + "/configs.json"; - MixAll.string2FileNotSafe(JSON.toJSONString(result, true), path); + MixAll.string2FileNotSafe(JSON.toJSONString(result, JSONWriter.Feature.PrettyFormat), path); System.out.printf("export %s success", path); } catch (Exception e) { throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e); diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataCommand.java index 748f7b16e1..559936bcb8 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataCommand.java @@ -16,10 +16,8 @@ */ package org.apache.rocketmq.tools.command.export; -import com.alibaba.fastjson.JSON; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -35,6 +33,10 @@ import org.apache.rocketmq.tools.command.CommandUtil; import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + public class ExportMetadataCommand implements SubCommand { private static final String DEFAULT_FILE_PATH = "/tmp/rocketmq/export"; @@ -99,13 +101,13 @@ public class ExportMetadataCommand implements SubCommand { filePath = filePath + "/topic.json"; TopicConfigSerializeWrapper topicConfigSerializeWrapper = defaultMQAdminExt.getUserTopicConfig( brokerAddr, specialTopic, 10000L); - MixAll.string2FileNotSafe(JSON.toJSONString(topicConfigSerializeWrapper, true), filePath); + MixAll.string2FileNotSafe(JSON.toJSONString(topicConfigSerializeWrapper, JSONWriter.Feature.PrettyFormat), filePath); System.out.printf("export %s success", filePath); } else if (commandLine.hasOption('g')) { filePath = filePath + "/subscriptionGroup.json"; SubscriptionGroupWrapper subscriptionGroupWrapper = defaultMQAdminExt.getUserSubscriptionGroup( brokerAddr, 10000L); - MixAll.string2FileNotSafe(JSON.toJSONString(subscriptionGroupWrapper, true), filePath); + MixAll.string2FileNotSafe(JSON.toJSONString(subscriptionGroupWrapper, JSONWriter.Feature.PrettyFormat), filePath); System.out.printf("export %s success", filePath); } } else if (commandLine.hasOption('c')) { @@ -163,7 +165,7 @@ public class ExportMetadataCommand implements SubCommand { exportPath = filePath + "/metadata.json"; } result.put("exportTime", System.currentTimeMillis()); - MixAll.string2FileNotSafe(JSON.toJSONString(result, true), exportPath); + MixAll.string2FileNotSafe(JSON.toJSONString(result, JSONWriter.Feature.PrettyFormat), exportPath); System.out.printf("export %s success%n", exportPath); } else { diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataInRocksDBCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataInRocksDBCommand.java index 438d17d668..1a3e5e5a6a 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataInRocksDBCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetadataInRocksDBCommand.java @@ -17,11 +17,9 @@ package org.apache.rocketmq.tools.command.export; -import com.alibaba.fastjson.JSONObject; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.BiConsumer; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -34,6 +32,11 @@ import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; import org.rocksdb.RocksIterator; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; + public class ExportMetadataInRocksDBCommand implements SubCommand { private static final String TOPICS_JSON_CONFIG = "topics"; private static final String SUBSCRIPTION_GROUP_JSON_CONFIG = "subscriptionGroups"; @@ -118,8 +121,8 @@ public class ExportMetadataInRocksDBCommand implements SubCommand { ); jsonConfig.put(configType.equalsIgnoreCase(TOPICS_JSON_CONFIG) ? "topicConfigTable" : "subscriptionGroupTable", - (JSONObject) JSONObject.toJSON(configTable)); - final String jsonConfigStr = JSONObject.toJSONString(jsonConfig, true); + (JSONObject) JSON.toJSON(configTable)); + final String jsonConfigStr = JSONObject.toJSONString(jsonConfig, JSONWriter.Feature.PrettyFormat); System.out.print(jsonConfigStr + "\n"); } else { AtomicLong count = new AtomicLong(0); diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetricsCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetricsCommand.java index 5d8bb37ba0..1b7d5d44e3 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetricsCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/export/ExportMetricsCommand.java @@ -16,12 +16,8 @@ */ package org.apache.rocketmq.tools.command.export; -import com.alibaba.fastjson.JSON; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -47,6 +43,12 @@ import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + public class ExportMetricsCommand implements SubCommand { @Override @@ -133,7 +135,7 @@ public class ExportMetricsCommand implements SubCommand { result.put("evaluateReport", evaluateReportMap); result.put("totalData", totalData); - MixAll.string2FileNotSafe(JSON.toJSONString(result, true), path); + MixAll.string2FileNotSafe(JSON.toJSONString(result, JSONWriter.Feature.PrettyFormat), path); System.out.printf("export %s success", path); } catch (Exception e) { throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e); diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/metadata/RocksDBConfigToJsonCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/metadata/RocksDBConfigToJsonCommand.java index 94899fce08..d65a04136b 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/metadata/RocksDBConfigToJsonCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/metadata/RocksDBConfigToJsonCommand.java @@ -17,22 +17,8 @@ package org.apache.rocketmq.tools.command.metadata; -import com.alibaba.fastjson.JSONObject; -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicLong; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; @@ -49,6 +35,22 @@ import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; import org.rocksdb.RocksIterator; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + public class RocksDBConfigToJsonCommand implements SubCommand { @Override @@ -136,10 +138,10 @@ public class RocksDBConfigToJsonCommand implements SubCommand { if (commandLine.hasOption("jsonEnable") && "false".equalsIgnoreCase(commandLine.getOptionValue("jsonEnable").trim())) { printConfigMapJsonDisable(configMap); } else { - System.out.print(JSONObject.toJSONString(configMap, true) + "\n"); + System.out.print(JSONObject.toJSONString(configMap, JSONWriter.Feature.PrettyFormat) + "\n"); } } else { - String jsonString = JSONObject.toJSONString(configMap, true); + String jsonString = JSONObject.toJSONString(configMap, JSONWriter.Feature.PrettyFormat); try { MixAll.string2File(jsonString, exportFile); } catch (IOException e) { diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/queue/QueryConsumeQueueCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/queue/QueryConsumeQueueCommand.java index 4902b8a881..24d9900b61 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/queue/QueryConsumeQueueCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/queue/QueryConsumeQueueCommand.java @@ -17,7 +17,8 @@ package org.apache.rocketmq.tools.command.queue; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; @@ -121,7 +122,7 @@ public class QueryConsumeQueueCommand implements SubCommand { ); if (queryConsumeQueueResponseBody.getSubscriptionData() != null) { - System.out.printf("Subscription data: \n%s\n", JSON.toJSONString(queryConsumeQueueResponseBody.getSubscriptionData(), true)); + System.out.printf("Subscription data: \n%s\n", JSON.toJSONString(queryConsumeQueueResponseBody.getSubscriptionData(), JSONWriter.Feature.PrettyFormat)); System.out.print("======================================\n"); } diff --git a/tools/src/test/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImplTest.java b/tools/src/test/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImplTest.java new file mode 100644 index 0000000000..c5ea051ab4 --- /dev/null +++ b/tools/src/test/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImplTest.java @@ -0,0 +1,825 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.tools.admin; + +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.impl.MQAdminImpl; +import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.common.TopicConfig; +import org.apache.rocketmq.common.constant.PermName; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.remoting.exception.RemotingConnectException; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.apache.rocketmq.remoting.exception.RemotingSendRequestException; +import org.apache.rocketmq.remoting.exception.RemotingTimeoutException; +import org.apache.rocketmq.remoting.protocol.ResponseCode; +import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats; +import org.apache.rocketmq.remoting.protocol.admin.OffsetWrapper; +import org.apache.rocketmq.remoting.protocol.admin.RollbackStats; +import org.apache.rocketmq.remoting.protocol.admin.TopicOffset; +import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable; +import org.apache.rocketmq.remoting.protocol.body.ClusterInfo; +import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult; +import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection; +import org.apache.rocketmq.remoting.protocol.body.GroupList; +import org.apache.rocketmq.remoting.protocol.body.QueueTimeSpan; +import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper; +import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper; +import org.apache.rocketmq.remoting.protocol.body.TopicList; +import org.apache.rocketmq.remoting.protocol.header.UpdateConsumerOffsetRequestHeader; +import org.apache.rocketmq.remoting.protocol.header.UpdateGroupForbiddenRequestHeader; +import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData; +import org.apache.rocketmq.remoting.protocol.route.BrokerData; +import org.apache.rocketmq.remoting.protocol.route.QueueData; +import org.apache.rocketmq.remoting.protocol.route.TopicRouteData; +import org.apache.rocketmq.remoting.protocol.subscription.GroupForbidden; +import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig; +import org.apache.rocketmq.tools.admin.api.BrokerOperatorResult; +import org.apache.rocketmq.tools.admin.api.MessageTrack; +import org.apache.rocketmq.tools.admin.common.AdminToolResult; +import org.apache.rocketmq.tools.admin.common.AdminToolsResultCodeEnum; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class DefaultMQAdminExtImplTest { + + private DefaultMQAdminExtImpl defaultMQAdminExtImpl; + + @Mock + private DefaultMQAdminExt defaultMQAdminExt; + + @Mock + private MQClientInstance mqClientInstance; + + @Mock + private MQClientAPIImpl mqClientAPIImpl; + + @Mock + private MQAdminImpl mqAdminImpl; + + private final String defaultTopic = "defaultTopic"; + + private final String defaultCluster = "cluster"; + + private final String defaultBroker = "broker1"; + + private final String defaultGroup = "consumerGroup"; + + private final String defaultBrokerAddr = "127.0.0.1:10911"; + + private final long timeoutMillis = 3000L; + + private final String defaultMsgId = "AC1A43AC00002A9F00008F214319C26B"; + + @Before + public void init() throws IllegalAccessException, RemotingException, InterruptedException, MQClientException, MQBrokerException { + defaultMQAdminExtImpl = new DefaultMQAdminExtImpl(defaultMQAdminExt, timeoutMillis); + FieldUtils.writeDeclaredField(defaultMQAdminExtImpl, "mqClientInstance", mqClientInstance, true); + FieldUtils.writeDeclaredField(defaultMQAdminExtImpl, "mqClientInstance", mqClientInstance, true); + FieldUtils.writeDeclaredField(defaultMQAdminExtImpl, "threadPoolExecutor", Executors.newFixedThreadPool(1), true); + when(mqClientInstance.getMQClientAPIImpl()).thenReturn(mqClientAPIImpl); + when(mqClientInstance.getMQAdminImpl()).thenReturn(mqAdminImpl); + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(createTopicRouteData()); + } + + @Test + public void testExamineTopicStats() throws Exception { + TopicStatsTable topicStatsTable = mock(TopicStatsTable.class); + Map offsetTable = new ConcurrentHashMap<>(); + offsetTable.put(new MessageQueue(), new TopicOffset()); + when(topicStatsTable.getOffsetTable()).thenReturn(offsetTable); + when(mqClientAPIImpl.getTopicStatsInfo(any(), any(), anyLong())).thenReturn(topicStatsTable); + TopicStatsTable actual = defaultMQAdminExtImpl.examineTopicStats(defaultTopic); + assertNotNull(actual); + assertEquals(offsetTable.size(), actual.getOffsetTable().size()); + } + + @Test + public void testExamineTopicStatsConcurrentTopicRouteDataNull() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(null); + AdminToolResult actual = defaultMQAdminExtImpl.examineTopicStatsConcurrent(defaultTopic); + assertNotNull(actual); + assertEquals(200, actual.getCode()); + assertEquals(0, actual.getData().getOffsetTable().size()); + } + + @Test + public void testExamineTopicStatsConcurrentBrokerDataEmpty() throws Exception { + TopicRouteData topicRouteData = mock(TopicRouteData.class); + when(topicRouteData.getBrokerDatas()).thenReturn(new ArrayList<>()); + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(topicRouteData); + AdminToolResult actual = defaultMQAdminExtImpl.examineTopicStatsConcurrent(defaultTopic); + assertNotNull(actual); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), actual.getCode()); + assertEquals(0, actual.getData().getOffsetTable().size()); + } + + @Test + public void testExamineTopicStatsConcurrent() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + doAnswer(invocation -> { + latch.countDown(); + return null; + }).when(mqClientAPIImpl).getTopicStatsInfo(any(), any(), anyLong()); + latch.await(1000, TimeUnit.MILLISECONDS); + AdminToolResult actual = defaultMQAdminExtImpl.examineTopicStatsConcurrent(defaultTopic); + assertNotNull(actual); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), actual.getCode()); + assertEquals(0, actual.getData().getOffsetTable().size()); + } + + @Test + public void testExamineTopicStatsConcurrentException() throws Exception { + doThrow(new MQBrokerException(ResponseCode.SYSTEM_ERROR, "Test Exception")).when(mqClientAPIImpl).getTopicStatsInfo(any(), any(), anyLong()); + assertNotNull(defaultMQAdminExtImpl.examineTopicStatsConcurrent(defaultTopic)); + } + + @Test + public void testExamineConsumeStatsConcurrentTopicRouteInfoNotExist() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(null); + AdminToolResult result = defaultMQAdminExtImpl.examineConsumeStatsConcurrent(defaultGroup, defaultTopic); + assertEquals(AdminToolsResultCodeEnum.TOPIC_ROUTE_INFO_NOT_EXIST.getCode(), result.getCode()); + } + + @Test + public void testExamineConsumeStatsConcurrent() throws Exception { + AtomicInteger count = new AtomicInteger(0); + AtomicInteger success = new AtomicInteger(0); + AtomicInteger fail = new AtomicInteger(0); + CountDownLatch latch = new CountDownLatch(10); + ExecutorService executorService = Executors.newFixedThreadPool(10); + List brokerDataList = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + BrokerData bd = new BrokerData(); + bd.setBrokerName("brokerName" + i); + bd.setCluster(defaultCluster); + bd.setBrokerAddrs(createBrokerAddrs()); + brokerDataList.add(bd); + } + TopicRouteData topicRouteData = new TopicRouteData(); + topicRouteData.setBrokerDatas(brokerDataList); + for (int i = 0; i < 10; i++) { + executorService.submit(() -> { + try { + Thread.sleep(100); + if (count.incrementAndGet() % 2 == 0) { + success.incrementAndGet(); + } else { + throw new RemotingException("Test Exception"); + } + latch.countDown(); + } catch (Exception e) { + fail.incrementAndGet(); + } + }); + } + latch.await(3000, TimeUnit.MILLISECONDS); + executorService.shutdown(); + assertEquals(5, success.get()); + assertEquals(5, fail.get()); + assertEquals(10, count.get()); + } + + @Test + public void testExamineConsumeStatsConcurrentEmptyOffsetTable() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(createTopicRouteData()); + when(mqClientAPIImpl.getConsumeStats(anyString(), anyString(), anyString(), anyLong())).thenReturn(new ConsumeStats()); + AdminToolResult actual = defaultMQAdminExtImpl.examineConsumeStatsConcurrent(defaultGroup, defaultTopic); + assertEquals(AdminToolsResultCodeEnum.CONSUMER_NOT_ONLINE.getCode(), actual.getCode()); + } + + @Test + public void testViewMessageValidMsgIdReturnsMessageExt() throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MessageExt expected = createMessageExt(); + when(mqAdminImpl.viewMessage(anyString(), anyString())).thenReturn(expected); + when(mqClientInstance.getMQAdminImpl()).thenReturn(mqAdminImpl); + MessageExt actual = defaultMQAdminExtImpl.viewMessage(expected.getTopic(), expected.getMsgId()); + assertNotNull(actual); + assertEquals(expected.getMsgId(), actual.getMsgId()); + assertEquals(expected.getTopic(), actual.getTopic()); + } + + @Test + public void testViewMessageInvalidMsgIdQueriesByUniqKey() throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MessageExt expected = createMessageExt(); + expected.setMsgId("invalidMsgId"); + when(mqAdminImpl.queryMessageByUniqKey(anyString(), anyString())).thenReturn(expected); + when(mqClientInstance.getMQAdminImpl()).thenReturn(mqAdminImpl); + MessageExt actual = defaultMQAdminExtImpl.viewMessage(expected.getTopic(), expected.getMsgId()); + assertNotNull(actual); + assertEquals(expected.getMsgId(), actual.getMsgId()); + assertEquals(expected.getTopic(), actual.getTopic()); + } + + @Test + public void testViewMessageExceptionInDecodeLogsWarningAndQueriesByUniqKey() throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + MessageExt expected = createMessageExt(); + expected.setMsgId("exceptionMsgId"); + when(mqAdminImpl.queryMessageByUniqKey(anyString(), anyString())).thenReturn(expected); + MessageExt actual = defaultMQAdminExtImpl.viewMessage(expected.getTopic(), expected.getMsgId()); + assertNotNull(actual); + assertEquals(expected.getMsgId(), actual.getMsgId()); + assertEquals(expected.getTopic(), actual.getTopic()); + } + + @Test + public void testQueryMessageInvalidMsgIdReturnsMessageExt() throws Exception { + MessageExt expected = createMessageExt(); + expected.setMsgId("invalidMsgId"); + when(mqAdminImpl.queryMessageByUniqKey(anyString(), anyString(), anyString())).thenReturn(expected); + MessageExt actual = defaultMQAdminExtImpl.queryMessage(defaultCluster, expected.getTopic(), expected.getMsgId()); + assertNotNull(actual); + assertEquals(expected.getMsgId(), actual.getMsgId()); + assertEquals(expected.getTopic(), actual.getTopic()); + } + + @Test + public void testQueryMessageRemotingException() throws MQBrokerException, RemotingException, InterruptedException, MQClientException { + when(mqAdminImpl.viewMessage(anyString(), anyString())).thenThrow(new RemotingException("Test Exception")); + assertNull(defaultMQAdminExtImpl.queryMessage(null, defaultTopic, defaultMsgId)); + } + + @Test + public void testDeleteTopicValidInput() throws Exception { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + when(defaultMQAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo); + Map> clusterAddrTable = new HashMap<>(); + clusterAddrTable.put(defaultCluster, new HashSet<>(Arrays.asList("broker1", "broker2"))); + when(clusterInfo.getClusterAddrTable()).thenReturn(clusterAddrTable); + Map brokerAddrTable = new HashMap<>(); + BrokerData brokerData = new BrokerData(); + brokerData.setBrokerName(defaultBroker); + HashMap brokerAddrs = new HashMap<>(); + brokerAddrs.put(0L, defaultBrokerAddr); + brokerData.setBrokerAddrs(brokerAddrs); + brokerAddrTable.put(defaultBroker, brokerData); + when(clusterInfo.getBrokerAddrTable()).thenReturn(brokerAddrTable); + List nsAddrs = new ArrayList<>(); + nsAddrs.add("127.0.0.1:9876"); + when(mqClientAPIImpl.getNameServerAddressList()).thenReturn(nsAddrs); + List kvNamespaceToDeleteList = new ArrayList<>(); + kvNamespaceToDeleteList.add("namespace"); + FieldUtils.writeDeclaredField(defaultMQAdminExtImpl, "kvNamespaceToDeleteList", kvNamespaceToDeleteList, true); + defaultMQAdminExtImpl.deleteTopic(defaultTopic, defaultCluster); + verify(mqClientAPIImpl, times(1)).deleteTopicInBroker(any(), any(), anyLong()); + verify(mqClientAPIImpl, times(1)).deleteTopicInNameServer(any(), any(), anyLong()); + verify(mqClientAPIImpl, times(1)).deleteKVConfigValue(any(), any(), anyLong()); + } + + @Test + public void testDeleteTopicInBrokerConcurrent() throws InterruptedException, RemotingException, MQClientException { + Set addrs = Collections.singleton(defaultBrokerAddr); + doNothing().when(mqClientAPIImpl).deleteTopicInBroker(anyString(), anyString(), anyLong()); + AdminToolResult result = defaultMQAdminExtImpl.deleteTopicInBrokerConcurrent(addrs, defaultTopic); + assertNotNull(result); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), result.getCode()); + BrokerOperatorResult brokerResult = result.getData(); + List successList = brokerResult.getSuccessList(); + List failureList = brokerResult.getFailureList(); + assertEquals(1, successList.size()); + assertEquals(0, failureList.size()); + assertEquals(addrs.iterator().next(), successList.get(0)); + } + + @Test + public void testDeleteTopicInBrokerConcurrentAllFailures() throws InterruptedException, RemotingException, MQClientException { + Set addrs = new HashSet<>(Collections.singleton(defaultBrokerAddr)); + String anotherAddr = "anotherBrokerAddr:10911"; + addrs.add(anotherAddr); + doThrow(new RuntimeException("deleteTopic error")).when(mqClientAPIImpl).deleteTopicInBroker(anyString(), anyString(), anyLong()); + AdminToolResult result = defaultMQAdminExtImpl.deleteTopicInBrokerConcurrent(addrs, defaultTopic); + assertNotNull(result); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), result.getCode()); + BrokerOperatorResult brokerResult = result.getData(); + List successList = brokerResult.getSuccessList(); + List failureList = brokerResult.getFailureList(); + assertEquals(0, successList.size()); + assertEquals(2, failureList.size()); + assertTrue(failureList.contains(defaultBrokerAddr)); + assertTrue(failureList.contains(anotherAddr)); + } + + @Test + public void testResetOffsetByTimestampOldThrowException() { + String topic = "nonExistentTopic"; + long timestamp = System.currentTimeMillis(); + assertThrows(NullPointerException.class, () -> defaultMQAdminExtImpl.resetOffsetByTimestampOld(defaultGroup, topic, timestamp, false)); + } + + @Test + public void testResetOffsetByTimestampOldValidInputShouldProcessCorrectly() throws Exception { + long timestamp = System.currentTimeMillis(); + ConsumeStats consumeStats = mock(ConsumeStats.class); + Map offsetTable = new ConcurrentHashMap<>(); + OffsetWrapper offsetWrapper = new OffsetWrapper(); + offsetWrapper.setBrokerOffset(5L); + offsetWrapper.setConsumerOffset(5L); + offsetTable.put(new MessageQueue(defaultTopic, defaultBroker, 0), offsetWrapper); + when(consumeStats.getOffsetTable()).thenReturn(offsetTable); + when(mqClientAPIImpl.getConsumeStats(any(), any(), anyLong())).thenReturn(consumeStats); + List rollbackStatsList = defaultMQAdminExtImpl.resetOffsetByTimestampOld(defaultGroup, defaultTopic, timestamp, false); + assertNotNull(rollbackStatsList); + assertEquals(1, rollbackStatsList.size()); + RollbackStats rollbackStats = rollbackStatsList.get(0); + assertEquals(defaultBroker, rollbackStats.getBrokerName()); + assertEquals(0, rollbackStats.getQueueId()); + assertEquals(5L, rollbackStats.getBrokerOffset()); + assertEquals(5L, rollbackStats.getConsumerOffset()); + } + + @Test + public void testResetOffsetNew() throws Exception { + defaultMQAdminExtImpl.resetOffsetNew(defaultGroup, defaultTopic, timeoutMillis); + verify(mqClientAPIImpl, times(1)).invokeBrokerToResetOffset( + anyString(), + anyString(), + anyString(), + anyLong(), + anyBoolean(), + anyLong(), + anyBoolean()); + } + + @Test + public void testResetOffsetNewConcurrent() { + AdminToolResult actual = defaultMQAdminExtImpl.resetOffsetNewConcurrent(defaultGroup, defaultTopic, timeoutMillis); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), actual.getCode()); + } + + @Test + public void testResetOffsetNewConcurrentTopicRouteInfoNotExist() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(null); + AdminToolResult actual = defaultMQAdminExtImpl.resetOffsetNewConcurrent(defaultGroup, defaultTopic, timeoutMillis); + assertEquals(AdminToolsResultCodeEnum.TOPIC_ROUTE_INFO_NOT_EXIST.getCode(), actual.getCode()); + } + + @Test + public void testResetOffsetNewConcurrentException() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenThrow(new MQClientException(ResponseCode.SYSTEM_ERROR, "Test Exception")); + AdminToolResult actual = defaultMQAdminExtImpl.resetOffsetNewConcurrent(defaultGroup, defaultTopic, timeoutMillis); + assertEquals(AdminToolsResultCodeEnum.MQ_CLIENT_ERROR.getCode(), actual.getCode()); + } + + @Test + public void testCreateOrUpdateOrderConfClusterConfig() throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + String key = "key1"; + String value = "value1"; + doNothing().when(mqClientAPIImpl).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + defaultMQAdminExtImpl.createOrUpdateOrderConf(key, value, true); + verify(mqClientAPIImpl, times(1)).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + } + + @Test + public void testCreateOrUpdateOrderConfNonClusterConfig() throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + String key = "key1"; + String value = "value1:value2"; + String oldOrderConfs = "key1:value1;key2:value2"; + when(mqClientAPIImpl.getKVConfigValue(anyString(), anyString(), anyLong())).thenReturn(oldOrderConfs); + doNothing().when(mqClientAPIImpl).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + defaultMQAdminExtImpl.createOrUpdateOrderConf(key, value, false); + verify(mqClientAPIImpl, times(1)).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + } + + @Test + public void testCreateOrUpdateOrderConfExceptionInPut() throws RemotingException, InterruptedException, MQClientException { + String key = "key1"; + String value = "value1:value2"; + String oldOrderConfs = "key1:value1;key2:value2"; + when(mqClientAPIImpl.getKVConfigValue(anyString(), anyString(), anyLong())).thenReturn(oldOrderConfs); + doThrow(new RemotingException("Test Exception")).when(mqClientAPIImpl).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + assertThrows(RemotingException.class, () -> defaultMQAdminExtImpl.createOrUpdateOrderConf(key, value, false)); + } + + @Test + public void testCreateOrUpdateOrderConfNoOldConfs() throws RemotingException, MQBrokerException, InterruptedException, MQClientException { + String key = "key1"; + String value = "value1:value2"; + when(mqClientAPIImpl.getKVConfigValue(anyString(), anyString(), anyLong())).thenReturn(null); + doNothing().when(mqClientAPIImpl).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + defaultMQAdminExtImpl.createOrUpdateOrderConf(key, value, false); + verify(mqClientAPIImpl, times(1)).putKVConfigValue(anyString(), anyString(), anyString(), anyLong()); + } + + @Test + public void testCreateOrUpdateOrderConfExceptionInGet() throws RemotingException, InterruptedException, MQClientException, MQBrokerException { + String key = "key1"; + String value = "value1:value2"; + when(mqClientAPIImpl.getKVConfigValue(anyString(), anyString(), anyLong())).thenThrow(new RemotingException("Test Exception")); + defaultMQAdminExtImpl.createOrUpdateOrderConf(key, value, false); + verify(mqClientAPIImpl, times(1)).getKVConfigValue(anyString(), anyString(), anyLong()); + } + + @Test + public void testQuerySubscriptionValidInput() throws InterruptedException, MQBrokerException, RemotingException, MQClientException { + when(mqClientAPIImpl.querySubscriptionByConsumer(anyString(), anyString(), anyString(), anyLong())).thenReturn(new SubscriptionData()); + assertNotNull(defaultMQAdminExtImpl.querySubscription("group", "topic")); + } + + @Test + public void testQueryTopicsByConsumer() throws Exception { + TopicList expected = new TopicList(); + expected.getTopicList().add(defaultTopic); + when(mqClientAPIImpl.queryTopicsByConsumer(anyString(), anyString(), anyLong())).thenReturn(expected); + TopicList actual = defaultMQAdminExtImpl.queryTopicsByConsumer(defaultGroup); + assertEquals(1, actual.getTopicList().size()); + assertEquals(expected.getTopicList().iterator().next(), actual.getTopicList().iterator().next()); + verify(mqClientAPIImpl, times(1)).queryTopicsByConsumer(anyString(), anyString(), anyLong()); + } + + @Test + public void testQueryTopicsByConsumerRemotingTimeoutException() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQBrokerException, InterruptedException { + when(mqClientAPIImpl.queryTopicsByConsumer(anyString(), anyString(), anyLong())).thenThrow(new RemotingTimeoutException("Test Exception")); + assertThrows(RemotingTimeoutException.class, () -> defaultMQAdminExtImpl.queryTopicsByConsumer(defaultGroup)); + verify(mqClientAPIImpl, times(1)).queryTopicsByConsumer(anyString(), anyString(), anyLong()); + } + + @Test + public void testQueryTopicsByConsumerMQBrokerException() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQBrokerException, InterruptedException { + when(mqClientAPIImpl.queryTopicsByConsumer(anyString(), anyString(), anyLong())).thenThrow(new MQBrokerException(ResponseCode.SYSTEM_ERROR, "Test Exception")); + assertThrows(MQBrokerException.class, () -> defaultMQAdminExtImpl.queryTopicsByConsumer(defaultGroup)); + verify(mqClientAPIImpl, times(1)).queryTopicsByConsumer(anyString(), anyString(), anyLong()); + } + + @Test + public void testQueryTopicsByConsumerMQClientException() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQBrokerException, InterruptedException { + when(mqClientAPIImpl.queryTopicsByConsumer(anyString(), anyString(), anyLong())).thenThrow(new MQBrokerException(ResponseCode.SYSTEM_ERROR, "Test Exception")); + assertThrows(MQBrokerException.class, () -> defaultMQAdminExtImpl.queryTopicsByConsumer(defaultGroup)); + verify(mqClientAPIImpl, times(1)).queryTopicsByConsumer(anyString(), anyString(), anyLong()); + } + + @Test + public void testQueryTopicsByConsumerNoBrokers() throws Exception { + TopicRouteData topicRouteData = new TopicRouteData(); + topicRouteData.setBrokerDatas(new ArrayList<>()); + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(topicRouteData); + TopicList actual = defaultMQAdminExtImpl.queryTopicsByConsumer(defaultGroup); + assertEquals(0, actual.getTopicList().size()); + } + + @Test + public void testQueryTopicsByConsumerConcurrentTopicRouteDataNull() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(null); + AdminToolResult actual = defaultMQAdminExtImpl.queryTopicsByConsumerConcurrent(defaultGroup); + assertEquals(AdminToolsResultCodeEnum.TOPIC_ROUTE_INFO_NOT_EXIST.getCode(), actual.getCode()); + assertEquals("router info not found.", actual.getErrorMsg()); + } + + @Test + public void testQueryTopicsByConsumerConcurrentNoBrokers() throws Exception { + TopicRouteData topicRouteData = new TopicRouteData(); + topicRouteData.setBrokerDatas(new ArrayList<>()); + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(any(), anyLong())).thenReturn(topicRouteData); + AdminToolResult actual = defaultMQAdminExtImpl.queryTopicsByConsumerConcurrent(defaultGroup); + assertEquals(AdminToolsResultCodeEnum.TOPIC_ROUTE_INFO_NOT_EXIST.getCode(), actual.getCode()); + assertEquals("router info not found.", actual.getErrorMsg()); + } + + @Test + public void testQueryTopicsByConsumerConcurrent() throws Exception { + TopicList expectedTopicList = new TopicList(); + expectedTopicList.setTopicList(new HashSet<>(Arrays.asList(defaultTopic, "topic2"))); + when(mqClientAPIImpl.queryTopicsByConsumer(any(), any(), anyLong())).thenReturn(expectedTopicList); + AdminToolResult result = defaultMQAdminExtImpl.queryTopicsByConsumerConcurrent(defaultGroup); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), result.getCode()); + Set actual = result.getData().getTopicList(); + assertFalse(actual.isEmpty()); + assertTrue(actual.containsAll(expectedTopicList.getTopicList())); + } + + @Test + public void testQueryConsumeTimeSpanConcurrentTopicRouteDataNull() throws Exception { + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(null); + AdminToolResult> actual = defaultMQAdminExtImpl.queryConsumeTimeSpanConcurrent(defaultTopic, defaultGroup); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), actual.getCode()); + assertEquals(0, actual.getData().size()); + } + + @Test + public void testQueryConsumeTimeSpanConcurrentNoBrokers() throws Exception { + TopicRouteData topicRouteData = new TopicRouteData(); + topicRouteData.setBrokerDatas(new ArrayList<>()); + when(mqClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(topicRouteData); + AdminToolResult> actual = defaultMQAdminExtImpl.queryConsumeTimeSpanConcurrent(defaultTopic, defaultGroup); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), actual.getCode()); + assertEquals(0, actual.getData().size()); + } + + @Test + public void testQueryConsumeTimeSpanConcurrent() throws Exception { + List spans = new ArrayList<>(); + QueueTimeSpan queueTimeSpan = new QueueTimeSpan(); + queueTimeSpan.setMinTimeStamp(1000L); + queueTimeSpan.setMaxTimeStamp(2000L); + spans.add(queueTimeSpan); + when(mqClientAPIImpl.queryConsumeTimeSpan(anyString(), anyString(), anyString(), anyLong())).thenReturn(spans); + AdminToolResult> actual = defaultMQAdminExtImpl.queryConsumeTimeSpanConcurrent(defaultTopic, defaultGroup); + assertEquals(AdminToolsResultCodeEnum.SUCCESS.getCode(), actual.getCode()); + assertEquals(1, actual.getData().size()); + } + + @Test + public void testDeleteExpiredCommitLog() throws Exception { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + when(clusterInfo.retrieveAllAddrByCluster(defaultCluster)).thenReturn(new String[]{"addr1", "addr2"}); + when(mqClientAPIImpl.getBrokerClusterInfo(anyLong())).thenReturn(clusterInfo); + when(mqClientAPIImpl.deleteExpiredCommitLog(anyString(), anyLong())).thenReturn(true); + boolean actual = defaultMQAdminExtImpl.deleteExpiredCommitLog(defaultCluster); + assertTrue(actual); + verify(mqClientAPIImpl, times(2)).deleteExpiredCommitLog(anyString(), anyLong()); + } + + @Test + public void testDeleteExpiredCommitLogByCluster() throws Exception { + ClusterInfo clusterInfo = mock(ClusterInfo.class); + when(clusterInfo.retrieveAllAddrByCluster(defaultCluster)).thenReturn(new String[]{"addr1", "addr2"}); + when(mqClientAPIImpl.deleteExpiredCommitLog(anyString(), anyLong())).thenReturn(true); + boolean actual = defaultMQAdminExtImpl.deleteExpiredCommitLogByCluster(clusterInfo, defaultCluster); + assertTrue(actual); + verify(mqClientAPIImpl, times(2)).deleteExpiredCommitLog(anyString(), anyLong()); + } + + @Test + public void testDeleteExpiredCommitLogByAddr() throws Exception { + when(mqClientAPIImpl.deleteExpiredCommitLog(defaultBrokerAddr, timeoutMillis)).thenReturn(true); + boolean actual = defaultMQAdminExtImpl.deleteExpiredCommitLogByAddr(defaultBrokerAddr); + assertTrue(actual); + verify(mqClientAPIImpl, times(1)).deleteExpiredCommitLog(defaultBrokerAddr, timeoutMillis); + } + + @Test + public void testConsumeMessageDirectly() throws Exception { + String clientId = "clientId"; + MessageExt messageExt = createMessageExt(); + when(mqAdminImpl.viewMessage(defaultTopic, defaultMsgId)).thenReturn(messageExt); + ConsumeMessageDirectlyResult consumeMessageDirectlyResult = mock(ConsumeMessageDirectlyResult.class); + when(mqClientAPIImpl.consumeMessageDirectly( + anyString(), + anyString(), + anyString(), + anyString(), + anyString(), + anyLong())) + .thenReturn(consumeMessageDirectlyResult); + ConsumeMessageDirectlyResult actual = defaultMQAdminExtImpl.consumeMessageDirectly(defaultGroup, clientId, defaultTopic, defaultMsgId); + assertNotNull(actual); + assertNull(actual.getRemark()); + assertFalse(actual.isAutoCommit()); + assertFalse(actual.isOrder()); + } + + @Test + public void testMessageTrackDetailConcurrent() throws Exception { + MessageExt messageExt = createMessageExt(); + GroupList groupList = mock(GroupList.class); + HashSet groupSet = new HashSet<>(); + groupSet.add(defaultGroup); + when(groupList.getGroupList()).thenReturn(groupSet); + when(mqClientAPIImpl.queryTopicConsumeByWho(anyString(), anyString(), anyLong())).thenReturn(groupList); + ConsumerConnection consumerConnection = mock(ConsumerConnection.class); + when(mqClientAPIImpl.getConsumerConnectionList(anyString(), anyString(), anyLong())).thenReturn(consumerConnection); + List actual = defaultMQAdminExtImpl.messageTrackDetailConcurrent(messageExt); + assertEquals(1, actual.size()); + } + +// @Test +// public void testConsumedConcurrent() throws Exception { +// ConsumeStats consumeStats = mock(ConsumeStats.class); +// ClusterInfo ci = mock(ClusterInfo.class); +// Map brokerAddrTable = new HashMap<>(); +// BrokerData brokerData = mock(BrokerData.class); +// HashMap brokerAddress = new HashMap<>(); +// brokerAddress.put(0L, defaultBrokerAddr); +// when(brokerData.getBrokerAddrs()).thenReturn(brokerAddress); +// brokerAddrTable.put(defaultBroker, brokerData); +// when(ci.getBrokerAddrTable()).thenReturn(brokerAddrTable); +// Map offsetTable = new HashMap<>(); +// OffsetWrapper offsetWrapper = new OffsetWrapper(); +// offsetWrapper.setConsumerOffset(1L); +// offsetTable.put(new MessageQueue(defaultTopic, defaultBroker, 0), offsetWrapper); +// when(consumeStats.getOffsetTable()).thenReturn(offsetTable); +//// when(mqClientAPIImpl.getConsumeStats(any(), any(), any(), anyLong())).thenReturn(consumeStats); +// when(mqClientAPIImpl.getBrokerClusterInfo(anyLong())).thenReturn(ci); +//// assertTrue(defaultMQAdminExtImpl.consumedConcurrent(createMessageExt(), defaultGroup)); +// } + + @Test + public void testCloneGroupOffsetValidInput() throws RemotingException, MQClientException, InterruptedException, MQBrokerException { + String srcGroup = "srcGroup"; + String destGroup = "destGroup"; + boolean isOffline = false; + defaultMQAdminExtImpl.cloneGroupOffset(srcGroup, destGroup, defaultTopic, isOffline); + verify(mqClientAPIImpl, times(1)).cloneGroupOffset( + anyString(), + anyString(), + anyString(), + anyString(), + anyBoolean(), + anyLong()); + } + + @Test + public void testGetUserSubscriptionGroup() throws Exception { + SubscriptionGroupWrapper subscriptionGroupWrapper = new SubscriptionGroupWrapper(); + ConcurrentMap subscriptionGroupTable = new ConcurrentHashMap<>(); + SubscriptionGroupConfig groupConfig1 = new SubscriptionGroupConfig(); + groupConfig1.setGroupName("CID_RMQ_SYS_GROUP"); + SubscriptionGroupConfig groupConfig2 = new SubscriptionGroupConfig(); + groupConfig2.setGroupName("DEFAULT_CONSUMER"); + SubscriptionGroupConfig groupConfig3 = new SubscriptionGroupConfig(); + groupConfig3.setGroupName("SYS_CONSUMER_GROUP"); + subscriptionGroupTable.put(groupConfig1.getGroupName(), groupConfig1); + subscriptionGroupTable.put(groupConfig2.getGroupName(), groupConfig2); + subscriptionGroupTable.put(groupConfig3.getGroupName(), groupConfig3); + subscriptionGroupWrapper.setSubscriptionGroupTable(subscriptionGroupTable); + when(mqClientAPIImpl.getAllSubscriptionGroup(any(), anyLong())).thenReturn(subscriptionGroupWrapper); + SubscriptionGroupWrapper actual = defaultMQAdminExtImpl.getUserSubscriptionGroup(defaultBrokerAddr, timeoutMillis); + assertEquals(1, actual.getSubscriptionGroupTable().size()); + assertTrue(actual.getSubscriptionGroupTable().containsKey("SYS_CONSUMER_GROUP")); + } + + @Test + public void testGetUserTopicConfig() throws Exception { + TopicConfigSerializeWrapper topicConfigSerializeWrapper = new TopicConfigSerializeWrapper(); + ConcurrentMap topicConfigMap = new ConcurrentHashMap<>(); + topicConfigMap.put("Topic1", new TopicConfig("Topic1", 1, 1, 0)); + topicConfigMap.put("Topic2", new TopicConfig("Topic2", 1, 1, 1)); + topicConfigSerializeWrapper.setTopicConfigTable(topicConfigMap); + TopicList topicList = new TopicList(); + Set topicSet = new HashSet<>(); + topicSet.add("Topic2"); + topicList.setTopicList(topicSet); + when(mqClientAPIImpl.getAllTopicConfig(any(), anyLong())).thenReturn(topicConfigSerializeWrapper); + when(mqClientAPIImpl.getSystemTopicListFromBroker(anyString(), anyLong())).thenReturn(topicList); + TopicConfigSerializeWrapper actual = defaultMQAdminExtImpl.getUserTopicConfig("brokerAddr", false, timeoutMillis); + assertEquals(1, actual.getTopicConfigTable().size()); + } + + @Test + public void testUpdateConsumeOffset() throws Exception { + doNothing().when(mqClientAPIImpl).updateConsumerOffset(any(), any(UpdateConsumerOffsetRequestHeader.class), anyLong()); + defaultMQAdminExtImpl.updateConsumeOffset(defaultBrokerAddr, defaultGroup, createMessageQueue(), 1L); + verify(mqClientAPIImpl, times(1)).updateConsumerOffset(any(), any(UpdateConsumerOffsetRequestHeader.class), anyLong()); + } + + @Test + public void testUpdateConsumeOffsetException() throws MQBrokerException, RemotingException, InterruptedException { + doThrow(new RemotingException("Test exception")).when(mqClientAPIImpl).updateConsumerOffset(anyString(), any(UpdateConsumerOffsetRequestHeader.class), anyLong()); + assertThrows(RemotingException.class, + () -> defaultMQAdminExtImpl.updateConsumeOffset(defaultBrokerAddr, defaultGroup, createMessageQueue(), 1L)); + } + + @Test + public void testResetOffsetByQueueId() throws Exception { + long resetOffset = 100; + doNothing().when(mqClientAPIImpl).updateConsumerOffset(any(), any(UpdateConsumerOffsetRequestHeader.class), anyLong()); + Map result = new HashMap<>(); + result.put(createMessageQueue(), resetOffset); + when(mqClientAPIImpl.invokeBrokerToResetOffset(anyString(), anyString(), anyString(), anyLong(), anyInt(), anyLong(), anyLong())).thenReturn(result); + defaultMQAdminExtImpl.resetOffsetByQueueId(defaultBrokerAddr, defaultGroup, defaultTopic, 0, resetOffset); + verify(mqClientAPIImpl, times(1)).updateConsumerOffset(any(), any(UpdateConsumerOffsetRequestHeader.class), anyLong()); + verify(mqClientAPIImpl, times(1)).invokeBrokerToResetOffset( + anyString(), + anyString(), + anyString(), + anyLong(), + anyInt(), + anyLong(), + anyLong()); + } + + @Test + public void testResetOffsetByQueueIdThrowsException() throws MQBrokerException, RemotingException, InterruptedException, MQClientException { + doNothing().when(mqClientAPIImpl).updateConsumerOffset(any(), any(UpdateConsumerOffsetRequestHeader.class), anyLong()); + when(mqClientAPIImpl.invokeBrokerToResetOffset( + anyString(), + anyString(), + anyString(), + anyLong(), + anyInt(), + anyLong(), + anyLong())) + .thenThrow(new MQClientException(1, "Exception")); + assertThrows(MQBrokerException.class, + () -> defaultMQAdminExtImpl.resetOffsetByQueueId(defaultBrokerAddr, defaultGroup, defaultTopic, 0, 100)); + } + + @Test + public void testUpdateAndGetGroupReadForbidden() throws RemotingException, InterruptedException, MQBrokerException { + boolean readable = true; + GroupForbidden expectedResponse = new GroupForbidden(); + expectedResponse.setGroup(defaultGroup); + expectedResponse.setTopic(defaultTopic); + expectedResponse.setReadable(readable); + when(mqClientAPIImpl.updateAndGetGroupForbidden(any(), any(UpdateGroupForbiddenRequestHeader.class), anyLong())).thenReturn(expectedResponse); + GroupForbidden actual = defaultMQAdminExtImpl.updateAndGetGroupReadForbidden(defaultBrokerAddr, defaultGroup, defaultTopic, readable); + assertNotNull(actual); + assertEquals(defaultGroup, actual.getGroup()); + assertEquals(defaultTopic, actual.getTopic()); + assertEquals(readable, actual.getReadable()); + } + + @Test + public void testUpdateAndGetGroupReadForbiddenException() throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQBrokerException, InterruptedException { + when(mqClientAPIImpl.updateAndGetGroupForbidden(any(), any(UpdateGroupForbiddenRequestHeader.class), anyLong())) + .thenThrow(new MQBrokerException(ResponseCode.SYSTEM_ERROR, "Test Exception")); + assertThrows(MQBrokerException.class, + () -> defaultMQAdminExtImpl.updateAndGetGroupReadForbidden(defaultBrokerAddr, defaultGroup, defaultTopic, true)); + } + + private HashMap createBrokerAddrs() { + HashMap result = new HashMap<>(); + result.put(0L, defaultBrokerAddr); + return result; + } + + private TopicRouteData createTopicRouteData() { + BrokerData bd = new BrokerData(defaultCluster, defaultBroker, new HashMap<>()); + bd.setBrokerAddrs(new HashMap<>()); + bd.getBrokerAddrs().put(0L, defaultBrokerAddr); + QueueData qd = new QueueData(); + qd.setBrokerName(defaultBroker); + qd.setPerm(PermName.PERM_WRITE); + qd.setReadQueueNums(1); + qd.setTopicSysFlag(0); + qd.setWriteQueueNums(1); + TopicRouteData result = new TopicRouteData(); + result.getBrokerDatas().add(bd); + result.getQueueDatas().add(qd); + return result; + } + + private MessageQueue createMessageQueue() { + return new MessageQueue(defaultTopic, defaultBroker, 0); + } + + private MessageExt createMessageExt() { + MessageExt result = new MessageExt(); + result.setMsgId(defaultMsgId); + result.setTopic(defaultTopic); + result.setQueueId(0); + InetAddress inetAddress = mock(InetAddress.class); + InetSocketAddress address = new InetSocketAddress(inetAddress, 10911); + when(inetAddress.getHostAddress()).thenReturn("127.0.0.1"); + result.setStoreHost(address); + return result; + } +} diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommandTest.java index f5967f5a30..a2ad8c5d85 100644 --- a/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommandTest.java +++ b/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommandTest.java @@ -16,7 +16,6 @@ */ package org.apache.rocketmq.tools.command.connection; -import java.util.HashSet; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; @@ -30,7 +29,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.mock; +import java.util.HashSet; public class ConsumerConnectionSubCommandTest { private ServerResponseMocker brokerMocker; @@ -62,7 +61,7 @@ public class ConsumerConnectionSubCommandTest { private ServerResponseMocker startOneBroker() { ConsumerConnection consumerConnection = new ConsumerConnection(); HashSet connectionSet = new HashSet<>(); - Connection connection = mock(Connection.class); + Connection connection = new Connection(); connectionSet.add(connection); consumerConnection.setConnectionSet(connectionSet); // start broker diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommandTest.java index 672e4113d6..812edde9e0 100644 --- a/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommandTest.java +++ b/tools/src/test/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommandTest.java @@ -16,7 +16,6 @@ */ package org.apache.rocketmq.tools.command.connection; -import java.util.HashSet; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; @@ -30,7 +29,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.mock; +import java.util.HashSet; public class ProducerConnectionSubCommandTest { @@ -64,7 +63,7 @@ public class ProducerConnectionSubCommandTest { private ServerResponseMocker startOneBroker() { ProducerConnection producerConnection = new ProducerConnection(); HashSet connectionSet = new HashSet<>(); - Connection connection = mock(Connection.class); + Connection connection = new Connection(); connectionSet.add(connection); producerConnection.setConnectionSet(connectionSet); diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommandTest.java index 4651113c68..9f4da15fa3 100644 --- a/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommandTest.java +++ b/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommandTest.java @@ -16,7 +16,6 @@ */ package org.apache.rocketmq.tools.command.consumer; -import java.util.HashSet; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; @@ -30,7 +29,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.mock; +import java.util.HashSet; public class ConsumerStatusSubCommandTest { @@ -65,7 +64,7 @@ public class ConsumerStatusSubCommandTest { private ServerResponseMocker startOneBroker() { ConsumerConnection consumerConnection = new ConsumerConnection(); HashSet connectionSet = new HashSet<>(); - Connection connection = mock(Connection.class); + Connection connection = new Connection(); connectionSet.add(connection); consumerConnection.setConnectionSet(connectionSet); // start broker diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/GetConsumerConfigSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/GetConsumerConfigSubCommandTest.java index e4e5e970c6..7ff4c42851 100644 --- a/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/GetConsumerConfigSubCommandTest.java +++ b/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/GetConsumerConfigSubCommandTest.java @@ -16,9 +16,6 @@ */ package org.apache.rocketmq.tools.command.consumer; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; @@ -33,7 +30,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.mock; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; public class GetConsumerConfigSubCommandTest { @@ -91,7 +90,7 @@ public class GetConsumerConfigSubCommandTest { private ServerResponseMocker startOneBroker() { ConsumerConnection consumerConnection = new ConsumerConnection(); HashSet connectionSet = new HashSet<>(); - Connection connection = mock(Connection.class); + Connection connection = new Connection(); connectionSet.add(connection); consumerConnection.setConnectionSet(connectionSet); // start broker diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/producer/ProducerSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/producer/ProducerSubCommandTest.java index 7039c05fba..b1ebb924e7 100644 --- a/tools/src/test/java/org/apache/rocketmq/tools/command/producer/ProducerSubCommandTest.java +++ b/tools/src/test/java/org/apache/rocketmq/tools/command/producer/ProducerSubCommandTest.java @@ -17,13 +17,12 @@ package org.apache.rocketmq.tools.command.producer; -import java.util.HashMap; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; -import org.apache.rocketmq.common.message.MessageQueue; -import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats; -import org.apache.rocketmq.remoting.protocol.admin.OffsetWrapper; +import org.apache.rocketmq.remoting.protocol.LanguageCode; +import org.apache.rocketmq.remoting.protocol.body.ProducerInfo; +import org.apache.rocketmq.remoting.protocol.body.ProducerTableInfo; import org.apache.rocketmq.srvutil.ServerUtil; import org.apache.rocketmq.tools.command.SubCommandException; import org.apache.rocketmq.tools.command.server.NameServerMocker; @@ -32,6 +31,10 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + public class ProducerSubCommandTest { private ServerResponseMocker brokerMocker; @@ -61,21 +64,16 @@ public class ProducerSubCommandTest { } private ServerResponseMocker startOneBroker() { - ConsumeStats consumeStats = new ConsumeStats(); - HashMap offsetTable = new HashMap<>(); - MessageQueue messageQueue = new MessageQueue(); - messageQueue.setBrokerName("mockBrokerName"); - messageQueue.setQueueId(1); - messageQueue.setBrokerName("mockTopicName"); + ProducerTableInfo producerTableInfo = new ProducerTableInfo(new HashMap<>()); + List producerInfo = new ArrayList<>(); + producerInfo.add(new ProducerInfo( + "xxxx-client-id", + "127.0.0.1:18978", + LanguageCode.JAVA, + 400, + System.currentTimeMillis())); - OffsetWrapper offsetWrapper = new OffsetWrapper(); - offsetWrapper.setBrokerOffset(1); - offsetWrapper.setConsumerOffset(1); - offsetWrapper.setLastTimestamp(System.currentTimeMillis()); - - offsetTable.put(messageQueue, offsetWrapper); - consumeStats.setOffsetTable(offsetTable); - // start broker - return ServerResponseMocker.startServer(consumeStats.encode()); + producerTableInfo.getData().put("mockTopicName", producerInfo); + return ServerResponseMocker.startServer(producerTableInfo.encode()); } }