From 79704419293819f3e20d7fe917c4836e5a69a9cf Mon Sep 17 00:00:00 2001
From: yukon
Date: Wed, 15 Mar 2017 11:23:56 +0800
Subject: [PATCH 01/74] [ROCKETMQ-143][HOTFIX] Update fastjson from 1.2.12 to
1.2.28
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 8e3a9c8d5d..84e69126a7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -605,7 +605,7 @@
com.alibabafastjson
- 1.2.12
+ 1.2.28org.javassist
From ac8941b03be3e011d72f7bdd6f65a79d52196b8f Mon Sep 17 00:00:00 2001
From: vongosling
Date: Thu, 16 Mar 2017 09:43:54 +0800
Subject: [PATCH 02/74] Update community projects illustration
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 61ee05033c..81eded4f55 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,8 @@ It offers a variety of features as follows:
----------
## Apache RocketMQ Community
-* [RocketMQ Community Projects](https://github.com/rocketmq)
+* [RocketMQ Community Incubator Projects](https://github.com/rocketmq)
+* [RocketMQ Community Projects](https://github.com/apache/incubator-rocketmq-externals)
----------
From 155823ef75314fba328200bd0a1a7aec9f2003d2 Mon Sep 17 00:00:00 2001
From: yukon
Date: Sat, 18 Mar 2017 15:34:39 +0800
Subject: [PATCH 03/74] [ROCKETMQ-145][HOTFIX] Resolve concureent issue in
HAService and GroupCommitService
---
.../org/apache/rocketmq/store/CommitLog.java | 52 ++++++++++---------
.../apache/rocketmq/store/ha/HAService.java | 38 +++++++-------
2 files changed, 47 insertions(+), 43 deletions(-)
diff --git a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
index b4bf298ba8..3bbe675854 100644
--- a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
+++ b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
@@ -995,12 +995,12 @@ public class CommitLog {
private volatile List requestsWrite = new ArrayList();
private volatile List requestsRead = new ArrayList();
- public void putRequest(final GroupCommitRequest request) {
- synchronized (this) {
+ public synchronized void putRequest(final GroupCommitRequest request) {
+ synchronized (this.requestsWrite) {
this.requestsWrite.add(request);
- if (hasNotified.compareAndSet(false, true)) {
- waitPoint.countDown(); // notify
- }
+ }
+ if (hasNotified.compareAndSet(false, true)) {
+ waitPoint.countDown(); // notify
}
}
@@ -1011,32 +1011,34 @@ public class CommitLog {
}
private void doCommit() {
- if (!this.requestsRead.isEmpty()) {
- for (GroupCommitRequest req : this.requestsRead) {
- // There may be a message in the next file, so a maximum of
- // two times the flush
- boolean flushOK = false;
- for (int i = 0; i < 2 && !flushOK; i++) {
- flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset();
+ synchronized (this.requestsRead) {
+ if (!this.requestsRead.isEmpty()) {
+ for (GroupCommitRequest req : this.requestsRead) {
+ // There may be a message in the next file, so a maximum of
+ // two times the flush
+ boolean flushOK = false;
+ for (int i = 0; i < 2 && !flushOK; i++) {
+ flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset();
- if (!flushOK) {
- CommitLog.this.mappedFileQueue.flush(0);
+ if (!flushOK) {
+ CommitLog.this.mappedFileQueue.flush(0);
+ }
}
+
+ req.wakeupCustomer(flushOK);
}
- req.wakeupCustomer(flushOK);
- }
+ long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp();
+ if (storeTimestamp > 0) {
+ CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);
+ }
- long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp();
- if (storeTimestamp > 0) {
- CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);
+ this.requestsRead.clear();
+ } else {
+ // Because of individual messages is set to not sync flush, it
+ // will come to this process
+ CommitLog.this.mappedFileQueue.flush(0);
}
-
- this.requestsRead.clear();
- } else {
- // Because of individual messages is set to not sync flush, it
- // will come to this process
- CommitLog.this.mappedFileQueue.flush(0);
}
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java b/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java
index 762bdb6adc..f507b368f0 100644
--- a/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java
+++ b/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java
@@ -253,12 +253,12 @@ public class HAService {
private volatile List requestsWrite = new ArrayList<>();
private volatile List requestsRead = new ArrayList<>();
- public void putRequest(final CommitLog.GroupCommitRequest request) {
- synchronized (this) {
+ public synchronized void putRequest(final CommitLog.GroupCommitRequest request) {
+ synchronized (this.requestsWrite) {
this.requestsWrite.add(request);
- if (hasNotified.compareAndSet(false, true)) {
- waitPoint.countDown(); // notify
- }
+ }
+ if (hasNotified.compareAndSet(false, true)) {
+ waitPoint.countDown(); // notify
}
}
@@ -273,22 +273,24 @@ public class HAService {
}
private void doWaitTransfer() {
- if (!this.requestsRead.isEmpty()) {
- for (CommitLog.GroupCommitRequest req : this.requestsRead) {
- boolean transferOK = HAService.this.push2SlaveMaxOffset.get() >= req.getNextOffset();
- for (int i = 0; !transferOK && i < 5; i++) {
- this.notifyTransferObject.waitForRunning(1000);
- transferOK = HAService.this.push2SlaveMaxOffset.get() >= req.getNextOffset();
+ synchronized (this.requestsRead) {
+ if (!this.requestsRead.isEmpty()) {
+ for (CommitLog.GroupCommitRequest req : this.requestsRead) {
+ boolean transferOK = HAService.this.push2SlaveMaxOffset.get() >= req.getNextOffset();
+ for (int i = 0; !transferOK && i < 5; i++) {
+ this.notifyTransferObject.waitForRunning(1000);
+ transferOK = HAService.this.push2SlaveMaxOffset.get() >= req.getNextOffset();
+ }
+
+ if (!transferOK) {
+ log.warn("transfer messsage to slave timeout, " + req.getNextOffset());
+ }
+
+ req.wakeupCustomer(transferOK);
}
- if (!transferOK) {
- log.warn("transfer messsage to slave timeout, " + req.getNextOffset());
- }
-
- req.wakeupCustomer(transferOK);
+ this.requestsRead.clear();
}
-
- this.requestsRead.clear();
}
}
From a947fe21ba9bbdea08d20661eacabe7c88677363 Mon Sep 17 00:00:00 2001
From: yukon
Date: Sun, 19 Mar 2017 22:57:24 +0800
Subject: [PATCH 04/74] [ROCKETMQ-148] Polish the contributing guide.
---
CONTRIBUTING.md | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f212787f38..5043192356 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,16 +1,17 @@
## How To Contribute
We are always very happy to have contributions, whether for trivial cleanups or big new features.
-We want to have high quality, well documented codes for each programming language, as well as the surrounding [ecosystem](https://github.com/rocketmq) of integration tools that people use with RocketMQ.
+We want to have high quality, well documented codes for each programming language, as well as the surrounding [ecosystem](https://github.com/apache/incubator-rocketmq-externals) of integration tools that people use with RocketMQ.
Nor is code the only way to contribute to the project. We strongly value documentation, integration with other project, and gladly accept improvements for these aspects.
## Contributing code
To submit a change for inclusion, please do the following:
-####If the change is non-trivial please include some unit tests that cover the new functionality.
-####If you are introducing a completely new feature or API it is a good idea to start a wiki and get consensus on the basic design first.
-####It is our job to follow up on patches in a timely fashion. Nag us if we aren't doing our job (sometimes we drop things).
+
+#### If the change is non-trivial please include some unit tests that cover the new functionality.
+#### If you are introducing a completely new feature or API it is a good idea to start a wiki and get consensus on the basic design first.
+#### It is our job to follow up on patches in a timely fashion. Nag us if we aren't doing our job (sometimes we drop things).
## Becoming a Committer
@@ -27,5 +28,5 @@ If you want to contribute the above listing points, you must abide our some prer
###### Readability - API must have Javadoc,some very important methods also must have javadoc
###### Testability - 80% above unit test coverage about main process
-###### Maintainability - Comply with our [checkstyle spec](checkstyle/checkstyle.xml),and at least 3 month update frequency
+###### Maintainability - Comply with our [checkstyle spec](style/rmq_checkstyle.xml), and at least 3 month update frequency
###### Deployability - We encourage you to deploy into [maven repository](http://search.maven.org/)
From 4df3155ba0dabadbdd325f2590c2d9299396dc47 Mon Sep 17 00:00:00 2001
From: yukon
Date: Thu, 23 Mar 2017 18:15:51 +0800
Subject: [PATCH 05/74] [ROCKETMQ-143][HOTFIX] Update fastjson to 1.2.29
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 84e69126a7..dfd191903a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -605,7 +605,7 @@
com.alibabafastjson
- 1.2.28
+ 1.2.29org.javassist
From fa85abcdf6ee46e56403ddf86828a38683103fd0 Mon Sep 17 00:00:00 2001
From: yukon
Date: Fri, 24 Mar 2017 11:05:19 +0800
Subject: [PATCH 06/74] [ROCKETMQ-153][HOTFIX] Fetch name server address
dynamically.
---
.../apache/rocketmq/client/impl/factory/MQClientInstance.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 1343e7639b..b0c3b04966 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
@@ -223,7 +223,7 @@ public class MQClientInstance {
this.serviceState = ServiceState.START_FAILED;
// If not specified,looking address from name server
if (null == this.clientConfig.getNamesrvAddr()) {
- this.clientConfig.setNamesrvAddr(this.mQClientAPIImpl.fetchNameServerAddr());
+ this.mQClientAPIImpl.fetchNameServerAddr();
}
// Start request-response channel
this.mQClientAPIImpl.start();
From 486d03d9cdd9ea1ede2310cacbca555cd95685af Mon Sep 17 00:00:00 2001
From: yilingfeng
Date: Tue, 28 Feb 2017 19:22:00 +0800
Subject: [PATCH 07/74] [ROCKETMQ-111] Fix possible MQClientException when
query message before today, closes apache/incubator-rocketmq#69
---
.../apache/rocketmq/common/message/MessageClientIDSetter.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageClientIDSetter.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageClientIDSetter.java
index fe83cddecc..a1d3ede211 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageClientIDSetter.java
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageClientIDSetter.java
@@ -52,7 +52,7 @@ public class MessageClientIDSetter {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(millis);
cal.set(Calendar.DAY_OF_MONTH, 1);
- cal.set(Calendar.HOUR, 0);
+ cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
@@ -74,7 +74,7 @@ public class MessageClientIDSetter {
Calendar cal = Calendar.getInstance();
long now = cal.getTimeInMillis();
cal.set(Calendar.DAY_OF_MONTH, 1);
- cal.set(Calendar.HOUR, 0);
+ cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
From 2314eaf497e1e19fd49bee21711245126bbb21d1 Mon Sep 17 00:00:00 2001
From: shroman
Date: Tue, 28 Feb 2017 19:33:15 +0800
Subject: [PATCH 08/74] [ROCKETMQ-104] Make MQAdmin commands throw exceptions,
closes apache/incubator-rocketmq#65
---
.../rocketmq/tools/command/SubCommand.java | 8 +-
.../tools/command/SubCommandException.java | 36 +++
.../broker/BrokerConsumeStatsSubCommad.java | 5 +-
.../broker/BrokerStatusSubCommand.java | 5 +-
.../broker/CleanExpiredCQSubCommand.java | 5 +-
.../broker/CleanUnusedTopicCommand.java | 5 +-
.../broker/GetBrokerConfigCommand.java | 5 +-
.../command/broker/SendMsgStatusCommand.java | 5 +-
.../broker/UpdateBrokerConfigSubCommand.java | 5 +-
.../cluster/CLusterSendMsgRTCommand.java | 5 +-
.../cluster/ClusterListSubCommand.java | 5 +-
.../ConsumerConnectionSubCommand.java | 5 +-
.../ProducerConnectionSubCommand.java | 5 +-
.../consumer/ConsumerProgressSubCommand.java | 5 +-
.../consumer/ConsumerStatusSubCommand.java | 5 +-
.../command/consumer/ConsumerSubCommand.java | 5 +-
.../DeleteSubscriptionGroupCommand.java | 5 +-
.../consumer/StartMonitoringSubCommand.java | 5 +-
.../consumer/UpdateSubGroupSubCommand.java | 5 +-
.../message/CheckMsgSendRTCommand.java | 5 +-
.../message/DecodeMessageIdCommond.java | 5 +-
.../message/PrintMessageByQueueCommand.java | 5 +-
.../message/PrintMessageSubCommand.java | 5 +-
.../message/QueryMsgByIdSubCommand.java | 5 +-
.../message/QueryMsgByKeySubCommand.java | 5 +-
.../message/QueryMsgByOffsetSubCommand.java | 5 +-
.../QueryMsgByUniqueKeySubCommand.java | 5 +-
.../rocketmq/tools/command/message/Store.java | 265 ------------------
.../namesrv/DeleteKvConfigCommand.java | 5 +-
.../namesrv/GetNamesrvConfigCommand.java | 5 +-
.../namesrv/UpdateKvConfigCommand.java | 5 +-
.../namesrv/UpdateNamesrvConfigCommand.java | 5 +-
.../namesrv/WipeWritePermSubCommand.java | 5 +-
.../offset/CloneGroupOffsetCommand.java | 5 +-
.../offset/GetConsumerStatusCommand.java | 5 +-
.../offset/ResetOffsetByTimeCommand.java | 15 +-
.../offset/ResetOffsetByTimeOldCommand.java | 8 +-
.../command/stats/StatsAllSubCommand.java | 5 +-
.../command/topic/AllocateMQSubCommand.java | 5 +-
.../command/topic/DeleteTopicSubCommand.java | 5 +-
.../command/topic/TopicClusterSubCommand.java | 5 +-
.../command/topic/TopicListSubCommand.java | 5 +-
.../command/topic/TopicRouteSubCommand.java | 5 +-
.../command/topic/TopicStatusSubCommand.java | 5 +-
.../command/topic/UpdateOrderConfCommand.java | 5 +-
.../topic/UpdateTopicPermSubCommand.java | 5 +-
.../command/topic/UpdateTopicSubCommand.java | 5 +-
.../BrokerConsumeStatsSubCommadTest.java | 8 +-
.../broker/BrokerStatusSubCommandTest.java | 8 +-
.../broker/CleanExpiredCQSubCommandTest.java | 8 +-
.../broker/CleanUnusedTopicCommandTest.java | 8 +-
.../broker/GetBrokerConfigCommandTest.java | 7 +-
.../broker/SendMsgStatusCommandTest.java | 2 +-
.../UpdateBrokerConfigSubCommandTest.java | 7 +-
.../ConsumerConnectionSubCommandTest.java | 7 +-
.../ProducerConnectionSubCommandTest.java | 7 +-
.../ConsumerProgressSubCommandTest.java | 7 +-
.../ConsumerStatusSubCommandTest.java | 7 +-
.../namesrv/GetNamesrvConfigCommandTest.java | 7 +-
.../namesrv/WipeWritePermSubCommandTest.java | 7 +-
.../offset/GetConsumerStatusCommandTest.java | 7 +-
.../offset/ResetOffsetByTimeCommandTest.java | 7 +-
62 files changed, 245 insertions(+), 401 deletions(-)
create mode 100644 tools/src/main/java/org/apache/rocketmq/tools/command/SubCommandException.java
delete mode 100644 tools/src/main/java/org/apache/rocketmq/tools/command/message/Store.java
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommand.java
index 3e5ed2b517..b42db08cb4 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommand.java
@@ -21,11 +21,11 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
public interface SubCommand {
- public String commandName();
+ String commandName();
- public String commandDesc();
+ String commandDesc();
- public Options buildCommandlineOptions(final Options options);
+ Options buildCommandlineOptions(final Options options);
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook);
+ void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException;
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommandException.java b/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommandException.java
new file mode 100644
index 0000000000..fadd8532ef
--- /dev/null
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/SubCommandException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.command;
+
+public class SubCommandException extends Exception {
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param msg Message.
+ */
+ public SubCommandException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * @param msg Message.
+ * @param cause Cause.
+ */
+ public SubCommandException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommad.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommad.java
index bcd4c9cddf..aee2f6b583 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommad.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommad.java
@@ -32,6 +32,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumeStatsList;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class BrokerConsumeStatsSubCommad implements SubCommand {
@@ -67,7 +68,7 @@ public class BrokerConsumeStatsSubCommad implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -134,7 +135,7 @@ public class BrokerConsumeStatsSubCommad implements SubCommand {
}
System.out.printf("%nDiff Total: %d%n", consumeStatsList.getTotalDiff());
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommand.java
index 9c26d50d2c..789ba00f72 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommand.java
@@ -32,6 +32,7 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class BrokerStatusSubCommand implements SubCommand {
@@ -59,7 +60,7 @@ public class BrokerStatusSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -84,7 +85,7 @@ public class BrokerStatusSubCommand implements SubCommand {
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommand.java
index 1cc2148dcc..94a49d401a 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommand.java
@@ -23,6 +23,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class CleanExpiredCQSubCommand implements SubCommand {
@@ -50,7 +51,7 @@ public class CleanExpiredCQSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -69,7 +70,7 @@ public class CleanExpiredCQSubCommand implements SubCommand {
}
System.out.printf(result ? "success" : "false");
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommand.java
index 6e2fecb70e..2aabeeb4b8 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommand.java
@@ -23,6 +23,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class CleanUnusedTopicCommand implements SubCommand {
@@ -50,7 +51,7 @@ public class CleanUnusedTopicCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -69,7 +70,7 @@ public class CleanUnusedTopicCommand implements SubCommand {
}
System.out.printf(result ? "success" : "false");
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommand.java
index 03fef3914d..919f673e29 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommand.java
@@ -33,6 +33,7 @@ import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class GetBrokerConfigCommand implements SubCommand {
@Override
@@ -59,7 +60,7 @@ public class GetBrokerConfigCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, final RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, final RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -100,7 +101,7 @@ public class GetBrokerConfigCommand implements SubCommand {
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommand.java
index eb2bbf1d76..e0a628aa4a 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommand.java
@@ -26,6 +26,7 @@ import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class SendMsgStatusCommand implements SubCommand {
@@ -69,7 +70,7 @@ public class SendMsgStatusCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
final DefaultMQProducer producer = new DefaultMQProducer("PID_SMSC", rpcHook);
producer.setInstanceName("PID_SMSC_" + System.currentTimeMillis());
@@ -87,7 +88,7 @@ public class SendMsgStatusCommand implements SubCommand {
System.out.printf("rt:" + (System.currentTimeMillis() - begin) + "ms, SendResult=" + result);
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
producer.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommand.java
index 7fadce1d90..a94fa50518 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommand.java
@@ -26,6 +26,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateBrokerConfigSubCommand implements SubCommand {
@@ -61,7 +62,7 @@ public class UpdateBrokerConfigSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -101,7 +102,7 @@ public class UpdateBrokerConfigSubCommand implements SubCommand {
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java
index 72aad50863..37d66e4c76 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java
@@ -34,6 +34,7 @@ import org.apache.rocketmq.common.protocol.body.ClusterInfo;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class CLusterSendMsgRTCommand implements SubCommand {
@@ -80,7 +81,7 @@ public class CLusterSendMsgRTCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -183,7 +184,7 @@ public class CLusterSendMsgRTCommand implements SubCommand {
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
producer.shutdown();
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/ClusterListSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/ClusterListSubCommand.java
index bd79bc7f38..c7b8ac5467 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/ClusterListSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/ClusterListSubCommand.java
@@ -33,6 +33,7 @@ import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ClusterListSubCommand implements SubCommand {
@@ -60,7 +61,7 @@ public class ClusterListSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -88,7 +89,7 @@ public class ClusterListSubCommand implements SubCommand {
}
while (enableInterval);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommand.java
index 48a97e0567..4e53caf6f5 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ConsumerConnectionSubCommand.java
@@ -28,6 +28,7 @@ import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ConsumerConnectionSubCommand implements SubCommand {
@@ -51,7 +52,7 @@ public class ConsumerConnectionSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -92,7 +93,7 @@ public class ConsumerConnectionSubCommand implements SubCommand {
System.out.printf("MessageModel: %s%n", cc.getMessageModel());
System.out.printf("ConsumeFromWhere: %s%n", cc.getConsumeFromWhere());
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommand.java
index ae3c6acf7c..bc867246fa 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/connection/ProducerConnectionSubCommand.java
@@ -25,6 +25,7 @@ import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ProducerConnectionSubCommand implements SubCommand {
@@ -52,7 +53,7 @@ public class ProducerConnectionSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -76,7 +77,7 @@ public class ProducerConnectionSubCommand implements SubCommand {
);
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java
index da738dce63..35fd26010a 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java
@@ -37,6 +37,7 @@ import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.slf4j.Logger;
public class ConsumerProgressSubCommand implements SubCommand {
@@ -62,7 +63,7 @@ public class ConsumerProgressSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -168,7 +169,7 @@ public class ConsumerProgressSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommand.java
index d08c58b34a..0e49c725fd 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerStatusSubCommand.java
@@ -31,6 +31,7 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.MQAdminStartup;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ConsumerStatusSubCommand implements SubCommand {
@@ -67,7 +68,7 @@ public class ConsumerStatusSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -130,7 +131,7 @@ public class ConsumerStatusSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerSubCommand.java
index cf74da9aab..0eea98de9d 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerSubCommand.java
@@ -31,6 +31,7 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.MQAdminStartup;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ConsumerSubCommand implements SubCommand {
@@ -63,7 +64,7 @@ public class ConsumerSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -129,7 +130,7 @@ public class ConsumerSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java
index 35735a1b2d..96d81956e4 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java
@@ -26,6 +26,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.apache.rocketmq.tools.command.topic.DeleteTopicSubCommand;
public class DeleteSubscriptionGroupCommand implements SubCommand {
@@ -57,7 +58,7 @@ public class DeleteSubscriptionGroupCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt adminExt = new DefaultMQAdminExt(rpcHook);
adminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -98,7 +99,7 @@ public class DeleteSubscriptionGroupCommand implements SubCommand {
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
adminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/StartMonitoringSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/StartMonitoringSubCommand.java
index 373c8953f4..1480575277 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/StartMonitoringSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/StartMonitoringSubCommand.java
@@ -21,6 +21,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.client.log.ClientLogger;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.apache.rocketmq.tools.monitor.DefaultMonitorListener;
import org.apache.rocketmq.tools.monitor.MonitorConfig;
import org.apache.rocketmq.tools.monitor.MonitorService;
@@ -45,14 +46,14 @@ public class StartMonitoringSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
try {
MonitorService monitorService =
new MonitorService(new MonitorConfig(), new DefaultMonitorListener(), rpcHook);
monitorService.start();
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
}
}
}
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 3cddf616dc..910eb1c032 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
@@ -26,6 +26,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateSubGroupSubCommand implements SubCommand {
@@ -89,7 +90,7 @@ public class UpdateSubGroupSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -180,7 +181,7 @@ public class UpdateSubGroupSubCommand implements SubCommand {
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/CheckMsgSendRTCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/CheckMsgSendRTCommand.java
index 428da3bfc5..14cd720615 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/CheckMsgSendRTCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/CheckMsgSendRTCommand.java
@@ -27,6 +27,7 @@ import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class CheckMsgSendRTCommand implements SubCommand {
private static String brokerName = "";
@@ -59,7 +60,7 @@ public class CheckMsgSendRTCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQProducer producer = new DefaultMQProducer(rpcHook);
producer.setProducerGroup(Long.toString(System.currentTimeMillis()));
@@ -117,7 +118,7 @@ public class CheckMsgSendRTCommand implements SubCommand {
double rt = (double) timeElapsed / (amount - 1);
System.out.printf("Avg RT: %s%n", String.format("%.2f", rt));
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
producer.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/DecodeMessageIdCommond.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/DecodeMessageIdCommond.java
index 17e34f7cc4..64f634e92d 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/DecodeMessageIdCommond.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/DecodeMessageIdCommond.java
@@ -23,6 +23,7 @@ import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.message.MessageClientIDSetter;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class DecodeMessageIdCommond implements SubCommand {
@Override
@@ -44,7 +45,7 @@ public class DecodeMessageIdCommond implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
String messageId = commandLine.getOptionValue('i').trim();
try {
@@ -57,7 +58,7 @@ public class DecodeMessageIdCommond implements SubCommand {
String date = UtilAll.formatDate(MessageClientIDSetter.getNearlyTimeFromID(messageId), UtilAll.YYYY_MM_DD_HH_MM_SS_SSS);
System.out.printf("date=" + date);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
}
}
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageByQueueCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageByQueueCommand.java
index adeb138a03..ac51267161 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageByQueueCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageByQueueCommand.java
@@ -36,6 +36,7 @@ import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class PrintMessageByQueueCommand implements SubCommand {
@@ -154,7 +155,7 @@ public class PrintMessageByQueueCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
try {
@@ -214,7 +215,7 @@ public class PrintMessageByQueueCommand implements SubCommand {
printCalculateByTag(tagCalmap, calByTag);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
consumer.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageSubCommand.java
index 591d27ea81..ac485199c0 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/PrintMessageSubCommand.java
@@ -30,6 +30,7 @@ import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class PrintMessageSubCommand implements SubCommand {
@@ -101,7 +102,7 @@ public class PrintMessageSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
try {
@@ -161,7 +162,7 @@ public class PrintMessageSubCommand implements SubCommand {
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
consumer.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByIdSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByIdSubCommand.java
index 826a99350c..05ae003084 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByIdSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByIdSubCommand.java
@@ -39,6 +39,7 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class QueryMsgByIdSubCommand implements SubCommand {
public static void queryById(final DefaultMQAdminExt admin, final String msgId) throws MQClientException,
@@ -209,7 +210,7 @@ public class QueryMsgByIdSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
DefaultMQProducer defaultMQProducer = new DefaultMQProducer("ReSendMsgById");
@@ -254,7 +255,7 @@ public class QueryMsgByIdSubCommand implements SubCommand {
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQProducer.shutdown();
defaultMQAdminExt.shutdown();
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByKeySubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByKeySubCommand.java
index bdc5f5202a..c9303f7eab 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByKeySubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByKeySubCommand.java
@@ -25,6 +25,7 @@ import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class QueryMsgByKeySubCommand implements SubCommand {
@@ -52,7 +53,7 @@ public class QueryMsgByKeySubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -63,7 +64,7 @@ public class QueryMsgByKeySubCommand implements SubCommand {
this.queryByKey(defaultMQAdminExt, topic, key);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByOffsetSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByOffsetSubCommand.java
index 3a178828a8..75a4cd059c 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByOffsetSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByOffsetSubCommand.java
@@ -26,6 +26,7 @@ import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class QueryMsgByOffsetSubCommand implements SubCommand {
@@ -61,7 +62,7 @@ public class QueryMsgByOffsetSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
DefaultMQPullConsumer defaultMQPullConsumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
@@ -96,7 +97,7 @@ public class QueryMsgByOffsetSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQPullConsumer.shutdown();
defaultMQAdminExt.shutdown();
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByUniqueKeySubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByUniqueKeySubCommand.java
index 7c8af3771b..5c93ad7985 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByUniqueKeySubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/message/QueryMsgByUniqueKeySubCommand.java
@@ -35,6 +35,7 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class QueryMsgByUniqueKeySubCommand implements SubCommand {
@@ -179,7 +180,7 @@ public class QueryMsgByUniqueKeySubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -198,7 +199,7 @@ public class QueryMsgByUniqueKeySubCommand implements SubCommand {
queryById(defaultMQAdminExt, topic, msgId);
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/message/Store.java b/tools/src/main/java/org/apache/rocketmq/tools/command/message/Store.java
deleted file mode 100644
index 6db78137a2..0000000000
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/message/Store.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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.command.message;
-
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import org.apache.rocketmq.common.MixAll;
-import org.apache.rocketmq.store.ConsumeQueue;
-import org.apache.rocketmq.store.MappedFile;
-import org.apache.rocketmq.store.MappedFileQueue;
-import org.apache.rocketmq.store.SelectMappedBufferResult;
-import org.apache.rocketmq.store.config.StorePathConfigHelper;
-
-public class Store {
- public final static int MESSAGE_MAGIC_CODE = 0xAABBCCDD ^ 1880681586 + 8;
- private final static int BLANK_MAGIC_CODE = 0xBBCCDDEE ^ 1880681586 + 8;
- private MappedFileQueue mapedFileQueue;
- private ConcurrentHashMap> consumeQueueTable;
-
- private String cStorePath;
- private int cSize;
- private String lStorePath;
- private int lSize;
-
- public Store(String cStorePath, int cSize, String lStorePath, int lSize) {
- this.cStorePath = cStorePath;
- this.cSize = cSize;
- this.lStorePath = lStorePath;
- this.lSize = lSize;
- mapedFileQueue = new MappedFileQueue(cStorePath, cSize, null);
- consumeQueueTable =
- new ConcurrentHashMap>();
- }
-
- public boolean load() {
- boolean result = this.mapedFileQueue.load();
- System.out.printf("load commit log " + (result ? "OK" : "Failed"));
- if (result) {
- result = loadConsumeQueue();
- }
- System.out.printf("load logics log " + (result ? "OK" : "Failed"));
- return result;
- }
-
- private boolean loadConsumeQueue() {
- File dirLogic = new File(StorePathConfigHelper.getStorePathConsumeQueue(lStorePath));
- File[] fileTopicList = dirLogic.listFiles();
- if (fileTopicList != null) {
-
- for (File fileTopic : fileTopicList) {
- String topic = fileTopic.getName();
-
- File[] fileQueueIdList = fileTopic.listFiles();
- if (fileQueueIdList != null) {
- for (File fileQueueId : fileQueueIdList) {
- int queueId = Integer.parseInt(fileQueueId.getName());
- ConsumeQueue logic = new ConsumeQueue(
- topic,
- queueId,
- StorePathConfigHelper.getStorePathConsumeQueue(lStorePath),
- lSize,
- null);
- this.putConsumeQueue(topic, queueId, logic);
- if (!logic.load()) {
- return false;
- }
- }
- }
- }
- }
- System.out.printf("load logics queue all over, OK");
- return true;
- }
-
- private void putConsumeQueue(final String topic, final int queueId, final ConsumeQueue consumeQueue) {
- ConcurrentHashMap map = this.consumeQueueTable.get(topic);
- if (null == map) {
- map = new ConcurrentHashMap();
- map.put(queueId, consumeQueue);
- this.consumeQueueTable.put(topic, map);
- } else {
- map.put(queueId, consumeQueue);
- }
- }
-
- public void traval(boolean openAll) {
- boolean success = true;
- byte[] bytesContent = new byte[1024];
- List mapedFiles = this.mapedFileQueue.getMappedFiles();
- ALL:
- for (MappedFile mapedFile : mapedFiles) {
- long startOffset = mapedFile.getFileFromOffset();
- int position = 0;
- int msgCount = 0;
- int errorCount = 0;
-
- System.out.printf("start travel " + mapedFile.getFileName());
- long startTime = System.currentTimeMillis();
- ByteBuffer byteBuffer = mapedFile.sliceByteBuffer();
- while (byteBuffer.hasRemaining()) {
- // 1 TOTALSIZE
- int totalSize = byteBuffer.getInt();
- // 2 MAGICCODE
- int magicCode = byteBuffer.getInt();
- if (BLANK_MAGIC_CODE == magicCode) {
- position = byteBuffer.limit();
- break;
- }
- // 3 BODYCRC
- int bodyCRC = byteBuffer.getInt();
-
- // 4 QUEUEID
- int queueId = byteBuffer.getInt();
-
- // 5 FLAG
- int flag = byteBuffer.getInt();
-
- // 6 QUEUEOFFSET
- long queueOffset = byteBuffer.getLong();
-
- // 7 PHYSICALOFFSET
- long physicOffset = byteBuffer.getLong();
-
- // 8 SYSFLAG
- int sysFlag = byteBuffer.getInt();
-
- // 9 BORNTIMESTAMP
- long bornTimeStamp = byteBuffer.getLong();
-
- // 10 BORNHOST(IP+PORT)
- byteBuffer.position(byteBuffer.position() + 8);
-
- // 11 STORETIMESTAMP
- long storeTimestamp = byteBuffer.getLong();
-
- // 12 STOREHOST(IP+PORT)
- byteBuffer.position(byteBuffer.position() + 8);
-
- // 13 RECONSUMETIMES
- int reconsumeTimes = byteBuffer.getInt();
-
- // 14 Prepared Transaction Offset
- long preparedTransactionOffset = byteBuffer.getLong();
-
- // 15 BODY
- int bodyLen = byteBuffer.getInt();
- if (bodyLen > 0) {
- byteBuffer.position(byteBuffer.position() + bodyLen);
- }
-
- // 16 TOPIC
- byte topicLen = byteBuffer.get();
- byteBuffer.get(bytesContent, 0, topicLen);
- String topic = null;
- try {
- topic = new String(bytesContent, 0, topicLen, MixAll.DEFAULT_CHARSET);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
-
- Date storeTime = new Date(storeTimestamp);
-
- long currentPhyOffset = startOffset + position;
- if (physicOffset != currentPhyOffset) {
- System.out.printf(storeTime
- + " [fetal error] physicOffset != currentPhyOffset. position=" + position
- + ", msgCount=" + msgCount + ", physicOffset=" + physicOffset
- + ", currentPhyOffset=" + currentPhyOffset);
- errorCount++;
- if (!openAll) {
- success = false;
- break ALL;
- }
- }
-
- ConsumeQueue consumeQueue = findConsumeQueue(topic, queueId);
- SelectMappedBufferResult smb = consumeQueue.getIndexBuffer(queueOffset);
- try {
- long offsetPy = smb.getByteBuffer().getLong();
- int sizePy = smb.getByteBuffer().getInt();
- if (physicOffset != offsetPy) {
- System.out.printf(storeTime + " [fetal error] physicOffset != offsetPy. position="
- + position + ", msgCount=" + msgCount + ", physicOffset=" + physicOffset
- + ", offsetPy=" + offsetPy);
- errorCount++;
- if (!openAll) {
- success = false;
- break ALL;
- }
- }
- if (totalSize != sizePy) {
- System.out.printf(storeTime + " [fetal error] totalSize != sizePy. position="
- + position + ", msgCount=" + msgCount + ", totalSize=" + totalSize
- + ", sizePy=" + sizePy);
- errorCount++;
- if (!openAll) {
- success = false;
- break ALL;
- }
- }
- } finally {
- smb.release();
- }
-
- msgCount++;
- position += totalSize;
- byteBuffer.position(position);
- }
-
- System.out.printf("end travel " + mapedFile.getFileName() + ", total msg=" + msgCount
- + ", error count=" + errorCount + ", cost:" + (System.currentTimeMillis() - startTime));
- }
-
- System.out.printf("travel " + (success ? "ok" : "fail"));
- }
-
- public ConsumeQueue findConsumeQueue(String topic, int queueId) {
- ConcurrentHashMap map = consumeQueueTable.get(topic);
- if (null == map) {
- ConcurrentHashMap newMap =
- new ConcurrentHashMap(128);
- ConcurrentHashMap oldMap = consumeQueueTable.putIfAbsent(topic, newMap);
- if (oldMap != null) {
- map = oldMap;
- } else {
- map = newMap;
- }
- }
- ConsumeQueue logic = map.get(queueId);
- if (null == logic) {
- ConsumeQueue newLogic = new ConsumeQueue(
- topic,
- queueId,
- StorePathConfigHelper.getStorePathConsumeQueue(lStorePath),
- lSize,
- null);
- ConsumeQueue oldLogic = map.putIfAbsent(queueId, newLogic);
- if (oldLogic != null) {
- logic = oldLogic;
- } else {
- logic = newLogic;
- }
- }
- return logic;
- }
-}
\ No newline at end of file
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/DeleteKvConfigCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/DeleteKvConfigCommand.java
index a8ac6a45da..0a0cc06312 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/DeleteKvConfigCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/DeleteKvConfigCommand.java
@@ -22,6 +22,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class DeleteKvConfigCommand implements SubCommand {
@Override
@@ -47,7 +48,7 @@ public class DeleteKvConfigCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -60,7 +61,7 @@ public class DeleteKvConfigCommand implements SubCommand {
defaultMQAdminExt.deleteKvConfig(namespace, key);
System.out.printf("delete kv config from namespace success.%n");
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommand.java
index c36e333426..ce63616dce 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommand.java
@@ -26,6 +26,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class GetNamesrvConfigCommand implements SubCommand {
@@ -45,7 +46,7 @@ public class GetNamesrvConfigCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, final RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, final RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -72,7 +73,7 @@ public class GetNamesrvConfigCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateKvConfigCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateKvConfigCommand.java
index 254eaf314e..56b4e72c0e 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateKvConfigCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateKvConfigCommand.java
@@ -22,6 +22,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateKvConfigCommand implements SubCommand {
@Override
@@ -51,7 +52,7 @@ public class UpdateKvConfigCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -66,7 +67,7 @@ public class UpdateKvConfigCommand implements SubCommand {
defaultMQAdminExt.createAndUpdateKvConfig(namespace, key, value);
System.out.printf("create or update kv config to namespace success.%n");
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateNamesrvConfigCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateNamesrvConfigCommand.java
index 0b33ca227b..250937027b 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateNamesrvConfigCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/UpdateNamesrvConfigCommand.java
@@ -26,6 +26,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateNamesrvConfigCommand implements SubCommand {
@Override
@@ -52,7 +53,7 @@ public class UpdateNamesrvConfigCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, final RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, final RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -81,7 +82,7 @@ public class UpdateNamesrvConfigCommand implements SubCommand {
System.out.printf("update name server config success!%s\n%s : %s\n",
serverList == null ? "" : serverList, key, value);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommand.java
index f24b855bc0..f8868335a1 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommand.java
@@ -23,6 +23,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class WipeWritePermSubCommand implements SubCommand {
@@ -45,7 +46,7 @@ public class WipeWritePermSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -74,7 +75,7 @@ public class WipeWritePermSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/CloneGroupOffsetCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/CloneGroupOffsetCommand.java
index 2bc1fd618e..460f59c377 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/CloneGroupOffsetCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/CloneGroupOffsetCommand.java
@@ -28,6 +28,7 @@ import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class CloneGroupOffsetCommand implements SubCommand {
@Override
@@ -64,7 +65,7 @@ public class CloneGroupOffsetCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
String srcGroup = commandLine.getOptionValue("s").trim();
String destGroup = commandLine.getOptionValue("d").trim();
String topic = commandLine.getOptionValue("t").trim();
@@ -95,7 +96,7 @@ public class CloneGroupOffsetCommand implements SubCommand {
System.out.printf("clone group offset success. srcGroup[%s], destGroup=[%s], topic[%s]",
srcGroup, destGroup, topic);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommand.java
index 758b78f462..be5ec61e37 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommand.java
@@ -26,6 +26,7 @@ import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class GetConsumerStatusCommand implements SubCommand {
@Override
@@ -56,7 +57,7 @@ public class GetConsumerStatusCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -92,7 +93,7 @@ public class GetConsumerStatusCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommand.java
index bc0230a404..3f2bc1b6b5 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommand.java
@@ -22,25 +22,16 @@ import java.util.Map;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.apache.commons.cli.PosixParser;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.remoting.RPCHook;
-import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ResetOffsetByTimeCommand implements SubCommand {
- public static void main(String[] args) {
- ResetOffsetByTimeCommand cmd = new ResetOffsetByTimeCommand();
- Options options = ServerUtil.buildCommandlineOptions(new Options());
- String[] subargs = new String[] {"-t Jodie_rest_test", "-g CID_Jodie_rest_test", "-s -1", "-f true"};
- final CommandLine commandLine =
- ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
- cmd.execute(commandLine, options, null);
- }
@Override
public String commandName() {
@@ -77,7 +68,7 @@ public class ResetOffsetByTimeCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -134,7 +125,7 @@ public class ResetOffsetByTimeCommand implements SubCommand {
entry.getValue());
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeOldCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeOldCommand.java
index c505d8e6e8..0c02d8f791 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeOldCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeOldCommand.java
@@ -30,9 +30,11 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class ResetOffsetByTimeOldCommand implements SubCommand {
- public static void resetOffset(DefaultMQAdminExt defaultMQAdminExt, String consumerGroup, String topic, long timestamp, boolean force,
+ public static void resetOffset(DefaultMQAdminExt defaultMQAdminExt, String consumerGroup, String topic,
+ long timestamp, boolean force,
String timeStampStr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
List rollbackStatsList = defaultMQAdminExt.resetOffsetByTimestampOld(consumerGroup, topic, timestamp, force);
System.out.printf(
@@ -91,7 +93,7 @@ public class ResetOffsetByTimeOldCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -121,7 +123,7 @@ public class ResetOffsetByTimeOldCommand implements SubCommand {
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/stats/StatsAllSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/stats/StatsAllSubCommand.java
index 9f7625bf8c..802d9c7cfe 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/stats/StatsAllSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/stats/StatsAllSubCommand.java
@@ -35,6 +35,7 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.store.stats.BrokerStatsManager;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class StatsAllSubCommand implements SubCommand {
public static void printTopicDetail(final DefaultMQAdminExt admin, final String topic, final boolean activeTopic)
@@ -160,7 +161,7 @@ public class StatsAllSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -198,7 +199,7 @@ public class StatsAllSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/AllocateMQSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/AllocateMQSubCommand.java
index 22e70c98fd..a9b9ab0347 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/AllocateMQSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/AllocateMQSubCommand.java
@@ -31,6 +31,7 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class AllocateMQSubCommand implements SubCommand {
@Override
@@ -57,7 +58,7 @@ public class AllocateMQSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt adminExt = new DefaultMQAdminExt(rpcHook);
adminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -86,7 +87,7 @@ public class AllocateMQSubCommand implements SubCommand {
final String json = RemotingSerializable.toJson(rr, false);
System.out.printf("%s%n", json);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
adminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/DeleteTopicSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/DeleteTopicSubCommand.java
index 0af1014bd2..25d36ce8a8 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/DeleteTopicSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/DeleteTopicSubCommand.java
@@ -30,6 +30,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class DeleteTopicSubCommand implements SubCommand {
public static void deleteTopic(final DefaultMQAdminExt adminExt,
@@ -75,7 +76,7 @@ public class DeleteTopicSubCommand implements SubCommand {
}
@Override
- public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt adminExt = new DefaultMQAdminExt(rpcHook);
adminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -91,7 +92,7 @@ public class DeleteTopicSubCommand implements SubCommand {
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
adminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicClusterSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicClusterSubCommand.java
index f91b894078..7e2c7e3ae9 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicClusterSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicClusterSubCommand.java
@@ -23,6 +23,7 @@ import org.apache.commons.cli.Options;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class TopicClusterSubCommand implements SubCommand {
@@ -45,7 +46,7 @@ public class TopicClusterSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
String topic = commandLine.getOptionValue('t').trim();
@@ -56,7 +57,7 @@ public class TopicClusterSubCommand implements SubCommand {
System.out.printf("%s%n", value);
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicListSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicListSubCommand.java
index 464f4abd4b..70f296ba9b 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicListSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicListSubCommand.java
@@ -34,6 +34,7 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class TopicListSubCommand implements SubCommand {
@@ -56,7 +57,7 @@ public class TopicListSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -108,7 +109,7 @@ public class TopicListSubCommand implements SubCommand {
}
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java
index 1241b008b3..d4efd90034 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java
@@ -23,6 +23,7 @@ import org.apache.rocketmq.common.protocol.route.TopicRouteData;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class TopicRouteSubCommand implements SubCommand {
@@ -46,7 +47,7 @@ public class TopicRouteSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -59,7 +60,7 @@ public class TopicRouteSubCommand implements SubCommand {
String json = topicRouteData.toJson(true);
System.out.printf("%s%n", json);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicStatusSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicStatusSubCommand.java
index d8602bc83d..f4ca80e530 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicStatusSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicStatusSubCommand.java
@@ -29,6 +29,7 @@ import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class TopicStatusSubCommand implements SubCommand {
@@ -51,7 +52,7 @@ public class TopicStatusSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -90,7 +91,7 @@ public class TopicStatusSubCommand implements SubCommand {
);
}
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateOrderConfCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateOrderConfCommand.java
index 11a1e349de..de26255d2a 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateOrderConfCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateOrderConfCommand.java
@@ -25,6 +25,7 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateOrderConfCommand implements SubCommand {
@@ -56,7 +57,7 @@ public class UpdateOrderConfCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -98,7 +99,7 @@ public class UpdateOrderConfCommand implements SubCommand {
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java
index 282a70c5c9..764c72f5bf 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java
@@ -29,6 +29,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateTopicPermSubCommand implements SubCommand {
@@ -64,7 +65,7 @@ public class UpdateTopicPermSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
try {
@@ -110,7 +111,7 @@ public class UpdateTopicPermSubCommand implements SubCommand {
}
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicSubCommand.java
index 8c2b814278..4c84cd47e0 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicSubCommand.java
@@ -27,6 +27,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.command.CommandUtil;
import org.apache.rocketmq.tools.command.SubCommand;
+import org.apache.rocketmq.tools.command.SubCommandException;
public class UpdateTopicSubCommand implements SubCommand {
@@ -82,7 +83,7 @@ public class UpdateTopicSubCommand implements SubCommand {
}
@Override
- public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) {
+ public void execute(final CommandLine commandLine, final Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
@@ -177,7 +178,7 @@ public class UpdateTopicSubCommand implements SubCommand {
ServerUtil.printCommandLineHelp("mqadmin " + this.commandName(), options);
} catch (Exception e) {
- e.printStackTrace();
+ throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e);
} finally {
defaultMQAdminExt.shutdown();
}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommadTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommadTest.java
index 35231752c4..48e6515313 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommadTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerConsumeStatsSubCommadTest.java
@@ -36,8 +36,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -79,8 +81,9 @@ public class BrokerConsumeStatsSubCommadTest {
public static void terminate() {
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
BrokerConsumeStatsSubCommad cmd = new BrokerConsumeStatsSubCommad();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b 127.0.0.1:10911", "-t 3000", "-l 5", "-o true"};
@@ -88,5 +91,4 @@ public class BrokerConsumeStatsSubCommadTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommandTest.java
index 1b08735c91..b1d92d79a3 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/BrokerStatusSubCommandTest.java
@@ -34,8 +34,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -75,8 +77,9 @@ public class BrokerStatusSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
BrokerStatusSubCommand cmd = new BrokerStatusSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b 127.0.0.1:10911", "-c default-cluster"};
@@ -84,5 +87,4 @@ public class BrokerStatusSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommandTest.java
index 6fcf044d9d..a5ba24f650 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanExpiredCQSubCommandTest.java
@@ -32,8 +32,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -71,8 +73,9 @@ public class CleanExpiredCQSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
CleanExpiredCQSubCommand cmd = new CleanExpiredCQSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b 127.0.0.1:10911", "-c default-cluster"};
@@ -80,5 +83,4 @@ public class CleanExpiredCQSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommandTest.java
index 3ae2c48b79..9537380072 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/CleanUnusedTopicCommandTest.java
@@ -32,8 +32,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -71,8 +73,9 @@ public class CleanUnusedTopicCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
CleanUnusedTopicCommand cmd = new CleanUnusedTopicCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b 127.0.0.1:10911", "-c default-cluster"};
@@ -80,5 +83,4 @@ public class CleanUnusedTopicCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommandTest.java
index 88a8ea8ba0..978a2fd596 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/GetBrokerConfigCommandTest.java
@@ -34,8 +34,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -77,8 +79,9 @@ public class GetBrokerConfigCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
GetBrokerConfigCommand cmd = new GetBrokerConfigCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b 127.0.0.1:10911", "-c default-cluster"};
@@ -86,4 +89,4 @@ public class GetBrokerConfigCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommandTest.java
index 9089a39b1d..c0f7639d93 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/SendMsgStatusCommandTest.java
@@ -75,4 +75,4 @@ public class SendMsgStatusCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
//cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommandTest.java
index cc459ba087..46c6eb3e74 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/broker/UpdateBrokerConfigSubCommandTest.java
@@ -33,8 +33,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.Mockito.mock;
@@ -67,8 +69,9 @@ public class UpdateBrokerConfigSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
UpdateBrokerConfigSubCommand cmd = new UpdateBrokerConfigSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b 127.0.0.1:10911", "-c default-cluster", "-k topicname", "-v unit_test"};
@@ -76,4 +79,4 @@ public class UpdateBrokerConfigSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
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 88530e22a5..3d64ce2422 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
@@ -40,8 +40,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -87,8 +89,9 @@ public class ConsumerConnectionSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
ConsumerConnectionSubCommand cmd = new ConsumerConnectionSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-g default-consumer-group"};
@@ -96,4 +99,4 @@ public class ConsumerConnectionSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
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 8df66fb1d0..76c8dc4b3c 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
@@ -35,8 +35,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -81,8 +83,9 @@ public class ProducerConnectionSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
ProducerConnectionSubCommand cmd = new ProducerConnectionSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-g default-producer-group", "-t unit-test"};
@@ -90,4 +93,4 @@ public class ProducerConnectionSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommandTest.java
index a5af04a6ca..6d69c10b5a 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommandTest.java
@@ -39,8 +39,10 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -99,8 +101,9 @@ public class ConsumerProgressSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
ConsumerProgressSubCommand cmd = new ConsumerProgressSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-g default-group"};
@@ -108,4 +111,4 @@ public class ConsumerProgressSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
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 8e846bcf24..aa6f27de09 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
@@ -50,8 +50,10 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -119,8 +121,9 @@ public class ConsumerStatusSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
ConsumerStatusSubCommand cmd = new ConsumerStatusSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-g default-group", "-i cid_one"};
@@ -128,4 +131,4 @@ public class ConsumerStatusSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommandTest.java
index 49802b91da..8d356fd2db 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/GetNamesrvConfigCommandTest.java
@@ -38,8 +38,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
@@ -79,8 +81,9 @@ public class GetNamesrvConfigCommandTest {
defaultMQAdminExt.shutdown();
}
+// @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
GetNamesrvConfigCommand cmd = new GetNamesrvConfigCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {};
@@ -88,4 +91,4 @@ public class GetNamesrvConfigCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommandTest.java
index 5d2781ad98..11711d0886 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/namesrv/WipeWritePermSubCommandTest.java
@@ -35,8 +35,10 @@ import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -78,8 +80,9 @@ public class WipeWritePermSubCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
WipeWritePermSubCommand cmd = new WipeWritePermSubCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-b default-broker"};
@@ -87,4 +90,4 @@ public class WipeWritePermSubCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommandTest.java
index 07dda80ff7..86454008c2 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/offset/GetConsumerStatusCommandTest.java
@@ -33,8 +33,10 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -73,8 +75,9 @@ public class GetConsumerStatusCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
GetConsumerStatusCommand cmd = new GetConsumerStatusCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-g default-group", "-t unit-test", "-i clientid"};
@@ -82,4 +85,4 @@ public class GetConsumerStatusCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
diff --git a/tools/src/test/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommandTest.java b/tools/src/test/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommandTest.java
index de32660f23..b7af8c870b 100644
--- a/tools/src/test/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommandTest.java
+++ b/tools/src/test/java/org/apache/rocketmq/tools/command/offset/ResetOffsetByTimeCommandTest.java
@@ -38,8 +38,10 @@ import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.srvutil.ServerUtil;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
+import org.apache.rocketmq.tools.command.SubCommandException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -93,8 +95,9 @@ public class ResetOffsetByTimeCommandTest {
defaultMQAdminExt.shutdown();
}
+ @Ignore
@Test
- public void testExecute() {
+ public void testExecute() throws SubCommandException {
ResetOffsetByTimeCommand cmd = new ResetOffsetByTimeCommand();
Options options = ServerUtil.buildCommandlineOptions(new Options());
String[] subargs = new String[] {"-g default-group", "-t unit-test", "-s 1412131213231", "-f false"};
@@ -102,4 +105,4 @@ public class ResetOffsetByTimeCommandTest {
ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options), new PosixParser());
cmd.execute(commandLine, options, null);
}
-}
\ No newline at end of file
+}
From fcdc34231ce8e30adcd780fd298e2a1919cefcf5 Mon Sep 17 00:00:00 2001
From: lizhanhui
Date: Tue, 28 Feb 2017 19:38:47 +0800
Subject: [PATCH 09/74] [ROCKETMQ-99] Add scripts for Windows, closes
apache/incubator-rocketmq#62
---
bin/mqadmin.cmd | 20 ++++++++++++++++++++
bin/mqbroker.cmd | 21 +++++++++++++++++++++
bin/mqfiltersrv.cmd | 21 +++++++++++++++++++++
bin/mqnamesrv.cmd | 21 +++++++++++++++++++++
bin/mqshutdown.cmd | 37 +++++++++++++++++++++++++++++++++++++
bin/play.cmd | 19 +++++++++++++++++++
bin/runbroker.cmd | 44 ++++++++++++++++++++++++++++++++++++++++++++
bin/runserver.cmd | 39 +++++++++++++++++++++++++++++++++++++++
bin/tools.cmd | 37 +++++++++++++++++++++++++++++++++++++
9 files changed, 259 insertions(+)
create mode 100644 bin/mqadmin.cmd
create mode 100644 bin/mqbroker.cmd
create mode 100644 bin/mqfiltersrv.cmd
create mode 100644 bin/mqnamesrv.cmd
create mode 100644 bin/mqshutdown.cmd
create mode 100644 bin/play.cmd
create mode 100644 bin/runbroker.cmd
create mode 100644 bin/runserver.cmd
create mode 100644 bin/tools.cmd
diff --git a/bin/mqadmin.cmd b/bin/mqadmin.cmd
new file mode 100644
index 0000000000..9fee2d337c
--- /dev/null
+++ b/bin/mqadmin.cmd
@@ -0,0 +1,20 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%ROCKETMQ_HOME%\bin\tools.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+call "%ROCKETMQ_HOME%\bin\tools.cmd" org.apache.rocketmq.tools.command.MQAdminStartup %*
+
+:end
\ No newline at end of file
diff --git a/bin/mqbroker.cmd b/bin/mqbroker.cmd
new file mode 100644
index 0000000000..d34a354e8c
--- /dev/null
+++ b/bin/mqbroker.cmd
@@ -0,0 +1,21 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%ROCKETMQ_HOME%\bin\runbroker.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+
+call "%ROCKETMQ_HOME%\bin\runbroker.cmd" org.apache.rocketmq.broker.BrokerStartup %*
+
+:end
\ No newline at end of file
diff --git a/bin/mqfiltersrv.cmd b/bin/mqfiltersrv.cmd
new file mode 100644
index 0000000000..a56657d938
--- /dev/null
+++ b/bin/mqfiltersrv.cmd
@@ -0,0 +1,21 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%ROCKETMQ_HOME%\bin\runbroker.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+
+call "%ROCKETMQ_HOME%\bin\runserver.cmd" org.apache.rocketmq.filtersrv.FiltersrvStartup %*
+
+:end
\ No newline at end of file
diff --git a/bin/mqnamesrv.cmd b/bin/mqnamesrv.cmd
new file mode 100644
index 0000000000..5156378beb
--- /dev/null
+++ b/bin/mqnamesrv.cmd
@@ -0,0 +1,21 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%ROCKETMQ_HOME%\bin\runserver.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+
+call "%ROCKETMQ_HOME%\bin\runserver.cmd" org.apache.rocketmq.namesrv.NamesrvStartup %*
+
+:end
\ No newline at end of file
diff --git a/bin/mqshutdown.cmd b/bin/mqshutdown.cmd
new file mode 100644
index 0000000000..47317d8c78
--- /dev/null
+++ b/bin/mqshutdown.cmd
@@ -0,0 +1,37 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%JAVA_HOME%\bin\jps.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+
+setlocal
+
+set "PATH=%JAVA_HOME%\bin;%PATH%"
+
+if /I "%1" == "broker" (
+ echo killing broker
+ for /f "tokens=1" %%i in ('jps -m ^| find "BrokerStartup"') do ( taskkill /F /PID %%i )
+ echo Done!
+) else if /I "%1" == "namesrv" (
+ echo killing name server
+
+ for /f "tokens=1" %%i in ('jps -m ^| find "NamesrvStartup"') do ( taskkill /F /PID %%i )
+
+ echo Done!
+) else (
+ echo Unknown role to kill, please specify broker or namesrv
+)
+
+:end
\ No newline at end of file
diff --git a/bin/play.cmd b/bin/play.cmd
new file mode 100644
index 0000000000..545864af78
--- /dev/null
+++ b/bin/play.cmd
@@ -0,0 +1,19 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+START /B mqnamesrv > ns.log
+START /B mqbroker -n localhost:9876 > bk.log
+echo "Start Name Server and Broker Successfully."
\ No newline at end of file
diff --git a/bin/runbroker.cmd b/bin/runbroker.cmd
new file mode 100644
index 0000000000..d18d47b8b1
--- /dev/null
+++ b/bin/runbroker.cmd
@@ -0,0 +1,44 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+set "JAVA=%JAVA_HOME%\bin\java.exe"
+
+setlocal
+
+set BASE_DIR=%~dp0
+set BASE_DIR=%BASE_DIR:~0,-1%
+for %%d in (%BASE_DIR%) do set BASE_DIR=%%~dpd
+
+set CLASSPATH=.;%BASE_DIR%conf;%CLASSPATH%
+
+rem ===========================================================================================
+rem JVM Configuration
+rem ===========================================================================================
+set "JAVA_OPT=%JAVA_OPT% -server -Xms8g -Xmx8g -Xmn4g"
+set "JAVA_OPT=%JAVA_OPT% -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -XX:+DisableExplicitGC"
+set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:%HOMEPATH%\mq_gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
+set "JAVA_OPT=%JAVA_OPT% -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
+set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow"
+set "JAVA_OPT=%JAVA_OPT% -XX:+AlwaysPreTouch"
+set "JAVA_OPT=%JAVA_OPT% -XX:MaxDirectMemorySize=15g"
+set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages -XX:-UseBiasedLocking"
+set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib"
+set "JAVA_OPT=%JAVA_OPT% -cp %CLASSPATH%"
+
+"%JAVA%" %JAVA_OPT% %*
+
+:end
\ No newline at end of file
diff --git a/bin/runserver.cmd b/bin/runserver.cmd
new file mode 100644
index 0000000000..2d561f24ed
--- /dev/null
+++ b/bin/runserver.cmd
@@ -0,0 +1,39 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+
+if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+set "JAVA=%JAVA_HOME%\bin\java.exe"
+
+setlocal
+
+set BASE_DIR=%~dp0
+set BASE_DIR=%BASE_DIR:~0,-1%
+for %%d in (%BASE_DIR%) do set BASE_DIR=%%~dpd
+
+set CLASSPATH=.;%BASE_DIR%conf;%CLASSPATH%
+
+set "JAVA_OPT=%JAVA_OPT% -server -Xms4g -Xmx4g -Xmn2g -XX:PermSize=128m -XX:MaxPermSize=320m"
+set "JAVA_OPT=%JAVA_OPT% -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:+DisableExplicitGC -XX:-UseParNewGC"
+set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:"%HOMEPATH%\rmq_srv_gc.log" -XX:+PrintGCDetails"
+set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow"
+set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages"
+set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib"
+set "JAVA_OPT=%JAVA_OPT% -cp "%CLASSPATH%""
+
+"%JAVA%" %JAVA_OPT% %*
+
+:end
\ No newline at end of file
diff --git a/bin/tools.cmd b/bin/tools.cmd
new file mode 100644
index 0000000000..daf60864e6
--- /dev/null
+++ b/bin/tools.cmd
@@ -0,0 +1,37 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+
+set "JAVA=%JAVA_HOME%\bin\java.exe"
+
+setlocal
+set BASE_DIR=%~dp0
+set BASE_DIR=%BASE_DIR:~0,-1%
+for %%d in (%BASE_DIR%) do set BASE_DIR=%%~dpd
+
+set CLASSPATH=.;%BASE_DIR%conf;%CLASSPATH%
+
+rem ===========================================================================================
+rem JVM Configuration
+rem ===========================================================================================
+set "JAVA_OPT=%JAVA_OPT% -server -Xms1g -Xmx1g -Xmn256m -XX:PermSize=128m -XX:MaxPermSize=128m"
+set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs="%BASE_DIR%\lib";"%JAVA_HOME%\jre\lib\ext""
+set "JAVA_OPT=%JAVA_OPT% -cp "%CLASSPATH%""
+
+"%JAVA%" %JAVA_OPT% %*
+
+:end
\ No newline at end of file
From a36c86dfad78a32698a9c17426e897d4e3f8cb83 Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Fri, 3 Mar 2017 16:51:09 +0800
Subject: [PATCH 10/74] Fix Windows script to handle return code properly
---
bin/mqadmin.cmd | 6 ++----
bin/mqbroker.cmd | 6 ++++--
bin/mqfiltersrv.cmd | 6 ++++--
bin/mqnamesrv.cmd | 6 ++++--
bin/mqshutdown.cmd | 6 ++----
bin/play.cmd | 15 +++++++++++++--
bin/runbroker.cmd | 10 ++++------
bin/runserver.cmd | 10 ++++------
bin/tools.cmd | 6 ++----
9 files changed, 39 insertions(+), 32 deletions(-)
diff --git a/bin/mqadmin.cmd b/bin/mqadmin.cmd
index 9fee2d337c..4e061f0ef9 100644
--- a/bin/mqadmin.cmd
+++ b/bin/mqadmin.cmd
@@ -14,7 +14,5 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%ROCKETMQ_HOME%\bin\tools.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
-call "%ROCKETMQ_HOME%\bin\tools.cmd" org.apache.rocketmq.tools.command.MQAdminStartup %*
-
-:end
\ No newline at end of file
+if not exist "%ROCKETMQ_HOME%\bin\tools.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & EXIT /B 1
+call "%ROCKETMQ_HOME%\bin\tools.cmd" org.apache.rocketmq.tools.command.MQAdminStartup %*
\ No newline at end of file
diff --git a/bin/mqbroker.cmd b/bin/mqbroker.cmd
index d34a354e8c..3efb47577c 100644
--- a/bin/mqbroker.cmd
+++ b/bin/mqbroker.cmd
@@ -14,8 +14,10 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%ROCKETMQ_HOME%\bin\runbroker.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+if not exist "%ROCKETMQ_HOME%\bin\runbroker.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & EXIT /B 1
call "%ROCKETMQ_HOME%\bin\runbroker.cmd" org.apache.rocketmq.broker.BrokerStartup %*
-:end
\ No newline at end of file
+IF %ERRORLEVEL% EQU 0 (
+ ECHO "Broker starts OK"
+)
\ No newline at end of file
diff --git a/bin/mqfiltersrv.cmd b/bin/mqfiltersrv.cmd
index a56657d938..0503026a39 100644
--- a/bin/mqfiltersrv.cmd
+++ b/bin/mqfiltersrv.cmd
@@ -14,8 +14,10 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%ROCKETMQ_HOME%\bin\runbroker.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+if not exist "%ROCKETMQ_HOME%\bin\runbroker.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & EXIT /B 1
call "%ROCKETMQ_HOME%\bin\runserver.cmd" org.apache.rocketmq.filtersrv.FiltersrvStartup %*
-:end
\ No newline at end of file
+IF %ERRORLEVEL% EQU 0 (
+ ECHO "Filtersrv starts OK"
+)
\ No newline at end of file
diff --git a/bin/mqnamesrv.cmd b/bin/mqnamesrv.cmd
index 5156378beb..2828bdc28d 100644
--- a/bin/mqnamesrv.cmd
+++ b/bin/mqnamesrv.cmd
@@ -14,8 +14,10 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%ROCKETMQ_HOME%\bin\runserver.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & goto end
+if not exist "%ROCKETMQ_HOME%\bin\runserver.cmd" echo Please set the ROCKETMQ_HOME variable in your environment! & EXIT /B 1
call "%ROCKETMQ_HOME%\bin\runserver.cmd" org.apache.rocketmq.namesrv.NamesrvStartup %*
-:end
\ No newline at end of file
+IF %ERRORLEVEL% EQU 0 (
+ ECHO "Namesrv starts OK"
+)
\ No newline at end of file
diff --git a/bin/mqshutdown.cmd b/bin/mqshutdown.cmd
index 47317d8c78..50af026f7c 100644
--- a/bin/mqshutdown.cmd
+++ b/bin/mqshutdown.cmd
@@ -14,7 +14,7 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%JAVA_HOME%\bin\jps.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+if not exist "%JAVA_HOME%\bin\jps.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & EXIT /B 1
setlocal
@@ -32,6 +32,4 @@ if /I "%1" == "broker" (
echo Done!
) else (
echo Unknown role to kill, please specify broker or namesrv
-)
-
-:end
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/bin/play.cmd b/bin/play.cmd
index 545864af78..f1737d574d 100644
--- a/bin/play.cmd
+++ b/bin/play.cmd
@@ -14,6 +14,17 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-START /B mqnamesrv > ns.log
-START /B mqbroker -n localhost:9876 > bk.log
+START /B mqnamesrv > ns.log 2>&1
+IF %ERRORLEVEL% NEQ 0 (
+ echo "Failed to start name server. Please check ns.log"
+ EXIT /B 1
+)
+
+START /B mqbroker -n localhost:9876 > bk.log 2>&1
+
+IF %ERRORLEVEL% NEQ 0 (
+ ECHO "Failed to start broker. Please check bk.log"
+ EXIT /B 1
+)
+
echo "Start Name Server and Broker Successfully."
\ No newline at end of file
diff --git a/bin/runbroker.cmd b/bin/runbroker.cmd
index d18d47b8b1..033d8225e4 100644
--- a/bin/runbroker.cmd
+++ b/bin/runbroker.cmd
@@ -14,7 +14,7 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & EXIT /B 1
set "JAVA=%JAVA_HOME%\bin\java.exe"
setlocal
@@ -28,9 +28,9 @@ set CLASSPATH=.;%BASE_DIR%conf;%CLASSPATH%
rem ===========================================================================================
rem JVM Configuration
rem ===========================================================================================
-set "JAVA_OPT=%JAVA_OPT% -server -Xms8g -Xmx8g -Xmn4g"
+set "JAVA_OPT=%JAVA_OPT% -server -Xms2g -Xmx2g -Xmn1g"
set "JAVA_OPT=%JAVA_OPT% -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -XX:+DisableExplicitGC"
-set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:%HOMEPATH%\mq_gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
+set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:%USERPROFILE%\mq_gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
set "JAVA_OPT=%JAVA_OPT% -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow"
set "JAVA_OPT=%JAVA_OPT% -XX:+AlwaysPreTouch"
@@ -39,6 +39,4 @@ set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages -XX:-UseBiasedLocking"
set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib"
set "JAVA_OPT=%JAVA_OPT% -cp %CLASSPATH%"
-"%JAVA%" %JAVA_OPT% %*
-
-:end
\ No newline at end of file
+"%JAVA%" %JAVA_OPT% %*
\ No newline at end of file
diff --git a/bin/runserver.cmd b/bin/runserver.cmd
index 2d561f24ed..e157d55fe9 100644
--- a/bin/runserver.cmd
+++ b/bin/runserver.cmd
@@ -15,7 +15,7 @@ rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & EXIT /B 1
set "JAVA=%JAVA_HOME%\bin\java.exe"
setlocal
@@ -26,14 +26,12 @@ for %%d in (%BASE_DIR%) do set BASE_DIR=%%~dpd
set CLASSPATH=.;%BASE_DIR%conf;%CLASSPATH%
-set "JAVA_OPT=%JAVA_OPT% -server -Xms4g -Xmx4g -Xmn2g -XX:PermSize=128m -XX:MaxPermSize=320m"
+set "JAVA_OPT=%JAVA_OPT% -server -Xms2g -Xmx2g -Xmn1g -XX:PermSize=128m -XX:MaxPermSize=320m"
set "JAVA_OPT=%JAVA_OPT% -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:+DisableExplicitGC -XX:-UseParNewGC"
-set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:"%HOMEPATH%\rmq_srv_gc.log" -XX:+PrintGCDetails"
+set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:"%USERPROFILE%\rmq_srv_gc.log" -XX:+PrintGCDetails"
set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow"
set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages"
set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib"
set "JAVA_OPT=%JAVA_OPT% -cp "%CLASSPATH%""
-"%JAVA%" %JAVA_OPT% %*
-
-:end
\ No newline at end of file
+"%JAVA%" %JAVA_OPT% %*
\ No newline at end of file
diff --git a/bin/tools.cmd b/bin/tools.cmd
index daf60864e6..28ce765326 100644
--- a/bin/tools.cmd
+++ b/bin/tools.cmd
@@ -14,7 +14,7 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & goto end
+if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! & EXIT /B 1
set "JAVA=%JAVA_HOME%\bin\java.exe"
@@ -32,6 +32,4 @@ set "JAVA_OPT=%JAVA_OPT% -server -Xms1g -Xmx1g -Xmn256m -XX:PermSize=128m -XX:Ma
set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs="%BASE_DIR%\lib";"%JAVA_HOME%\jre\lib\ext""
set "JAVA_OPT=%JAVA_OPT% -cp "%CLASSPATH%""
-"%JAVA%" %JAVA_OPT% %*
-
-:end
\ No newline at end of file
+"%JAVA%" %JAVA_OPT% %*
\ No newline at end of file
From 44e4216b4d81f8d6f628f2e45ba55c918885bf31 Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Mon, 6 Mar 2017 14:17:29 +0800
Subject: [PATCH 11/74] Trivial Changes: Specify maven-assembly-plugin version
to 2.8 for release-client profile similar to release-all profile, reason of
performing this change is that latest version of maven-assembly-plugin has
removed finalName property.
---
pom.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pom.xml b/pom.xml
index dfd191903a..ce6a7f1791 100644
--- a/pom.xml
+++ b/pom.xml
@@ -461,6 +461,7 @@
maven-assembly-plugin
+ 2.6apache-rocketmq
From e9a0d62dbd8a1a6163870828d583300eb27e324a Mon Sep 17 00:00:00 2001
From: yukon
Date: Mon, 6 Mar 2017 20:01:10 +0800
Subject: [PATCH 12/74] [ROCKETMQ-119] Add ThreadUtils and shutdown
PullMessageService properly
---
.../impl/consumer/PullMessageService.java | 7 +
.../rocketmq/common/utils/ThreadUtils.java | 174 ++++++++++++++++++
2 files changed, 181 insertions(+)
create mode 100644 common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/PullMessageService.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/PullMessageService.java
index 8ddd483ad3..ed4b837182 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/PullMessageService.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/PullMessageService.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
import org.apache.rocketmq.client.log.ClientLogger;
import org.apache.rocketmq.common.ServiceThread;
+import org.apache.rocketmq.common.utils.ThreadUtils;
import org.slf4j.Logger;
public class PullMessageService extends ServiceThread {
@@ -97,6 +98,12 @@ public class PullMessageService extends ServiceThread {
log.info(this.getServiceName() + " service end");
}
+ @Override
+ public void shutdown(boolean interrupt) {
+ super.shutdown(interrupt);
+ ThreadUtils.shutdownGracefully(this.scheduledExecutorService, 1000, TimeUnit.MILLISECONDS);
+ }
+
@Override
public String getServiceName() {
return PullMessageService.class.getSimpleName();
diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java b/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java
new file mode 100644
index 0000000000..8c28d70027
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java
@@ -0,0 +1,174 @@
+/*
+ * 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.utils;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ThreadUtils {
+ private static final Logger log = LoggerFactory.getLogger(LoggerName.TOOLS_LOGGER_NAME);
+
+ public static ExecutorService newThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
+ TimeUnit unit, BlockingQueue workQueue, String processName, boolean isDaemon) {
+ return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, newThreadFactory(processName, isDaemon));
+ }
+
+ public static ExecutorService newSingleThreadExecutor(String processName, boolean isDaemon) {
+ return Executors.newSingleThreadExecutor(newThreadFactory(processName, isDaemon));
+ }
+
+ public static ScheduledExecutorService newSingleThreadScheduledExecutor(String processName, boolean isDaemon) {
+ return Executors.newSingleThreadScheduledExecutor(newThreadFactory(processName, isDaemon));
+ }
+
+ public static ScheduledExecutorService newFixedThreadScheduledPool(int nThreads, String processName,
+ boolean isDaemon) {
+ return Executors.newScheduledThreadPool(nThreads, newThreadFactory(processName, isDaemon));
+ }
+
+ public static ThreadFactory newThreadFactory(String processName, boolean isDaemon) {
+ return newGenericThreadFactory("Remoting-" + processName, isDaemon);
+ }
+
+ public static ThreadFactory newGenericThreadFactory(String processName) {
+ return newGenericThreadFactory(processName, false);
+ }
+
+ public static ThreadFactory newGenericThreadFactory(String processName, int threads) {
+ return newGenericThreadFactory(processName, threads, false);
+ }
+
+ public static ThreadFactory newGenericThreadFactory(final String processName, final boolean isDaemon) {
+ return new ThreadFactory() {
+ private AtomicInteger threadIndex = new AtomicInteger(0);
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r, String.format("%s_%d", processName, this.threadIndex.incrementAndGet()));
+ thread.setDaemon(isDaemon);
+ return thread;
+ }
+ };
+ }
+
+ public static ThreadFactory newGenericThreadFactory(final String processName, final int threads,
+ final boolean isDaemon) {
+ return new ThreadFactory() {
+ private AtomicInteger threadIndex = new AtomicInteger(0);
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r, String.format("%s_%d_%d", processName, threads, this.threadIndex.incrementAndGet()));
+ thread.setDaemon(isDaemon);
+ return thread;
+ }
+ };
+ }
+
+ /**
+ * Create a new thread
+ *
+ * @param name The name of the thread
+ * @param runnable The work for the thread to do
+ * @param daemon Should the thread block JVM stop?
+ * @return The unstarted thread
+ */
+ public static Thread newThread(String name, Runnable runnable, boolean daemon) {
+ Thread thread = new Thread(runnable, name);
+ thread.setDaemon(daemon);
+ thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread t, Throwable e) {
+ log.error("Uncaught exception in thread '" + t.getName() + "':", e);
+ }
+ });
+ return thread;
+ }
+
+ /**
+ * Shutdown passed thread using isAlive and join.
+ *
+ * @param t Thread to stop
+ */
+ public static void shutdownGracefully(final Thread t) {
+ shutdownGracefully(t, 0);
+ }
+
+ /**
+ * Shutdown passed thread using isAlive and join.
+ *
+ * @param millis Pass 0 if we're to wait forever.
+ * @param t Thread to stop
+ */
+ public static void shutdownGracefully(final Thread t, final long millis) {
+ if (t == null)
+ return;
+ while (t.isAlive()) {
+ try {
+ t.interrupt();
+ t.join(millis);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * An implementation of the graceful stop sequence recommended by
+ * {@link ExecutorService}.
+ *
+ * @param executor executor
+ * @param timeout timeout
+ * @param timeUnit timeUnit
+ */
+ public static void shutdownGracefully(ExecutorService executor, long timeout, TimeUnit timeUnit) {
+ // Disable new tasks from being submitted.
+ executor.shutdown();
+ try {
+ // Wait a while for existing tasks to terminate.
+ if (!executor.awaitTermination(timeout, timeUnit)) {
+ executor.shutdownNow();
+ // Wait a while for tasks to respond to being cancelled.
+ if (!executor.awaitTermination(timeout, timeUnit)) {
+ log.warn(String.format("%s didn't terminate!", executor));
+ }
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted.
+ executor.shutdownNow();
+ // Preserve interrupt status.
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * A constructor to stop this class being constructed.
+ */
+ private ThreadUtils() {
+ // Unused
+
+ }
+}
From eaba61ffe81b65abe689922ce85d596a1808ab11 Mon Sep 17 00:00:00 2001
From: shtykh_roman
Date: Thu, 9 Mar 2017 18:07:54 +0900
Subject: [PATCH 13/74] Corrected spellings.
---
.../org/apache/rocketmq/broker/BrokerStartup.java | 10 ++++++----
.../broker/pagecache/ManyMessageTransfer.java | 14 ++++++++------
.../broker/pagecache/OneMessageTransfer.java | 14 ++++++++------
.../broker/pagecache/QueryMessageTransfer.java | 14 ++++++++------
.../processor/AbstractSendMessageProcessor.java | 2 +-
.../broker/processor/EndTransactionProcessor.java | 2 +-
.../broker/processor/PullMessageProcessor.java | 2 +-
.../transaction/jdbc/JDBCTransactionStore.java | 2 +-
8 files changed, 34 insertions(+), 26 deletions(-)
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
index f613894d66..39ee8dd2e5 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
@@ -125,7 +125,7 @@ public class BrokerStartup {
properties = new Properties();
properties.load(in);
- parsePropertie2SystemEnv(properties);
+ properties2SystemEnv(properties);
MixAll.properties2Object(properties, brokerConfig);
MixAll.properties2Object(properties, nettyServerConfig);
MixAll.properties2Object(properties, nettyClientConfig);
@@ -212,9 +212,9 @@ public class BrokerStartup {
log.info("Shutdown hook was invoked, {}", this.shutdownTimes.incrementAndGet());
if (!this.hasShutdown) {
this.hasShutdown = true;
- long begineTime = System.currentTimeMillis();
+ long beginTime = System.currentTimeMillis();
controller.shutdown();
- long consumingTimeTotal = System.currentTimeMillis() - begineTime;
+ long consumingTimeTotal = System.currentTimeMillis() - beginTime;
log.info("Shutdown hook over, consuming total time(ms): {}", consumingTimeTotal);
}
}
@@ -230,10 +230,12 @@ public class BrokerStartup {
return null;
}
- private static void parsePropertie2SystemEnv(Properties properties) {
+ private static void properties2SystemEnv(Properties properties) {
if (properties == null) {
+ log.info("No properties to set system environment");
return;
}
+
String rmqAddressServerDomain = properties.getProperty("rmqAddressServerDomain", MixAll.DEFAULT_NAMESRV_ADDR_LOOKUP);
String rmqAddressServerSubGroup = properties.getProperty("rmqAddressServerSubGroup", "nsaddr");
System.setProperty("rocketmq.namesrv.domain", rmqAddressServerDomain);
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/ManyMessageTransfer.java b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/ManyMessageTransfer.java
index 85a43595f5..5b9df3e2ab 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/ManyMessageTransfer.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/ManyMessageTransfer.java
@@ -27,7 +27,9 @@ import org.apache.rocketmq.store.GetMessageResult;
public class ManyMessageTransfer extends AbstractReferenceCounted implements FileRegion {
private final ByteBuffer byteBufferHeader;
private final GetMessageResult getMessageResult;
- private long transfered; // the bytes which was transfered already
+
+ /** Bytes which were transferred already. */
+ private long transferred;
public ManyMessageTransfer(ByteBuffer byteBufferHeader, GetMessageResult getMessageResult) {
this.byteBufferHeader = byteBufferHeader;
@@ -46,7 +48,7 @@ public class ManyMessageTransfer extends AbstractReferenceCounted implements Fil
@Override
public long transfered() {
- return transfered;
+ return transferred;
}
@Override
@@ -57,14 +59,14 @@ public class ManyMessageTransfer extends AbstractReferenceCounted implements Fil
@Override
public long transferTo(WritableByteChannel target, long position) throws IOException {
if (this.byteBufferHeader.hasRemaining()) {
- transfered += target.write(this.byteBufferHeader);
- return transfered;
+ transferred += target.write(this.byteBufferHeader);
+ return transferred;
} else {
List messageBufferList = this.getMessageResult.getMessageBufferList();
for (ByteBuffer bb : messageBufferList) {
if (bb.hasRemaining()) {
- transfered += target.write(bb);
- return transfered;
+ transferred += target.write(bb);
+ return transferred;
}
}
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java
index e5d2298db7..254c2ab81a 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java
@@ -26,7 +26,9 @@ import org.apache.rocketmq.store.SelectMappedBufferResult;
public class OneMessageTransfer extends AbstractReferenceCounted implements FileRegion {
private final ByteBuffer byteBufferHeader;
private final SelectMappedBufferResult selectMappedBufferResult;
- private long transfered; // the bytes which was transfered already
+
+ /** Bytes which were transferred already. */
+ private long transferred;
public OneMessageTransfer(ByteBuffer byteBufferHeader, SelectMappedBufferResult selectMappedBufferResult) {
this.byteBufferHeader = byteBufferHeader;
@@ -40,7 +42,7 @@ public class OneMessageTransfer extends AbstractReferenceCounted implements File
@Override
public long transfered() {
- return transfered;
+ return transferred;
}
@Override
@@ -51,11 +53,11 @@ public class OneMessageTransfer extends AbstractReferenceCounted implements File
@Override
public long transferTo(WritableByteChannel target, long position) throws IOException {
if (this.byteBufferHeader.hasRemaining()) {
- transfered += target.write(this.byteBufferHeader);
- return transfered;
+ transferred += target.write(this.byteBufferHeader);
+ return transferred;
} else if (this.selectMappedBufferResult.getByteBuffer().hasRemaining()) {
- transfered += target.write(this.selectMappedBufferResult.getByteBuffer());
- return transfered;
+ transferred += target.write(this.selectMappedBufferResult.getByteBuffer());
+ return transferred;
}
return 0;
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransfer.java b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransfer.java
index 66c36b6d08..aaa1304d06 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransfer.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/QueryMessageTransfer.java
@@ -27,7 +27,9 @@ import org.apache.rocketmq.store.QueryMessageResult;
public class QueryMessageTransfer extends AbstractReferenceCounted implements FileRegion {
private final ByteBuffer byteBufferHeader;
private final QueryMessageResult queryMessageResult;
- private long transfered; // the bytes which was transfered already
+
+ /** Bytes which were transferred already. */
+ private long transferred;
public QueryMessageTransfer(ByteBuffer byteBufferHeader, QueryMessageResult queryMessageResult) {
this.byteBufferHeader = byteBufferHeader;
@@ -46,7 +48,7 @@ public class QueryMessageTransfer extends AbstractReferenceCounted implements Fi
@Override
public long transfered() {
- return transfered;
+ return transferred;
}
@Override
@@ -57,14 +59,14 @@ public class QueryMessageTransfer extends AbstractReferenceCounted implements Fi
@Override
public long transferTo(WritableByteChannel target, long position) throws IOException {
if (this.byteBufferHeader.hasRemaining()) {
- transfered += target.write(this.byteBufferHeader);
- return transfered;
+ transferred += target.write(this.byteBufferHeader);
+ return transferred;
} else {
List messageBufferList = this.queryMessageResult.getMessageBufferList();
for (ByteBuffer bb : messageBufferList) {
if (bb.hasRemaining()) {
- transfered += target.write(bb);
- return transfered;
+ transferred += target.write(bb);
+ return transferred;
}
}
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java
index f5dc1f9057..9f23bade96 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java
@@ -214,7 +214,7 @@ public abstract class AbstractSendMessageProcessor implements NettyRequestProces
int queueIdInt = requestHeader.getQueueId();
int idValid = Math.max(topicConfig.getWriteQueueNums(), topicConfig.getReadQueueNums());
if (queueIdInt >= idValid) {
- String errorInfo = String.format("request queueId[%d] is illagal, %s Producer: %s",
+ String errorInfo = String.format("request queueId[%d] is illegal, %s Producer: %s",
queueIdInt,
topicConfig.toString(),
RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java
index fc15bbfd78..68e416768b 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java
@@ -157,7 +157,7 @@ public class EndTransactionProcessor implements NettyRequestProcessor {
// Failed
case CREATE_MAPEDFILE_FAILED:
response.setCode(ResponseCode.SYSTEM_ERROR);
- response.setRemark("create maped file failed.");
+ response.setRemark("create mapped file failed.");
break;
case MESSAGE_ILLEGAL:
case PROPERTIES_SIZE_EXCEEDED:
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java
index 7d15894353..89967d8f6a 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java
@@ -133,7 +133,7 @@ public class PullMessageProcessor implements NettyRequestProcessor {
}
if (requestHeader.getQueueId() < 0 || requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) {
- String errorInfo = String.format("queueId[%d] is illagal, topic:[%s] topicConfig.readQueueNums:[%d] consumer:[%s]",
+ String errorInfo = String.format("queueId[%d] is illegal, topic:[%s] topicConfig.readQueueNums:[%d] consumer:[%s]",
requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(), channel.remoteAddress());
LOG.warn(errorInfo);
response.setCode(ResponseCode.SYSTEM_ERROR);
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/transaction/jdbc/JDBCTransactionStore.java b/broker/src/main/java/org/apache/rocketmq/broker/transaction/jdbc/JDBCTransactionStore.java
index ae6400c822..056d93902f 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/transaction/jdbc/JDBCTransactionStore.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/transaction/jdbc/JDBCTransactionStore.java
@@ -63,7 +63,7 @@ public class JDBCTransactionStore implements TransactionStore {
return true;
} catch (SQLException e) {
- log.info("Create JDBC Connection Exeption", e);
+ log.info("Create JDBC Connection Exception", e);
}
}
From 68ebb780031483f215524019c2db96d0563fdc3e Mon Sep 17 00:00:00 2001
From: shtykh_roman
Date: Fri, 10 Mar 2017 15:50:04 +0900
Subject: [PATCH 14/74] [ROCKETMQ-75] Logging when RemotingCommand header
decoding swallows exceptions. closes #51
---
.../rocketmq/remoting/protocol/RemotingCommand.java | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
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 4f017ad205..bee9b12c80 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
@@ -110,7 +110,8 @@ public class RemotingCommand {
return createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR, "not set any response code", classHeader);
}
- public static RemotingCommand createResponseCommand(int code, String remark, Class extends CommandCustomHeader> classHeader) {
+ public static RemotingCommand createResponseCommand(int code, String remark,
+ Class extends CommandCustomHeader> classHeader) {
RemotingCommand cmd = new RemotingCommand();
cmd.markResponseType();
cmd.setCode(code);
@@ -230,7 +231,8 @@ public class RemotingCommand {
this.customHeader = customHeader;
}
- public CommandCustomHeader decodeCommandCustomHeader(Class extends CommandCustomHeader> classHeader) throws RemotingCommandException {
+ public CommandCustomHeader decodeCommandCustomHeader(
+ Class extends CommandCustomHeader> classHeader) throws RemotingCommandException {
CommandCustomHeader objectHeader;
try {
objectHeader = classHeader.newInstance();
@@ -279,6 +281,7 @@ public class RemotingCommand {
field.set(objectHeader, valueParsed);
} catch (Throwable e) {
+ log.error("Failed field [{}] decoding", fieldName, e);
}
}
}
@@ -384,8 +387,8 @@ public class RemotingCommand {
try {
field.setAccessible(true);
value = field.get(this.customHeader);
- } catch (IllegalArgumentException e) {
- } catch (IllegalAccessException e) {
+ } catch (Exception e) {
+ log.error("Failed to access field [{}]", name, e);
}
if (value != null) {
@@ -401,7 +404,6 @@ public class RemotingCommand {
return encodeHeader(this.body != null ? this.body.length : 0);
}
-
public ByteBuffer encodeHeader(final int bodyLength) {
// 1> header length size
int length = 4;
From aa535dd3bad19278520a653bd91bdabd1b46bae6 Mon Sep 17 00:00:00 2001
From: yukon
Date: Fri, 10 Mar 2017 16:41:58 +0800
Subject: [PATCH 15/74] [ROCKETMQ-139] Degrade the client related modules' JDK
version to 1.6
---
client/pom.xml | 6 +++++-
.../rocketmq/client/consumer/DefaultMQPushConsumer.java | 2 +-
.../rocketmq/client/impl/factory/MQClientInstance.java | 6 +++++-
.../client/consumer/DefaultMQPullConsumerTest.java | 4 ++--
.../client/consumer/DefaultMQPushConsumerTest.java | 2 +-
.../client/consumer/store/LocalFileOffsetStoreTest.java | 2 +-
.../consumer/store/RemoteBrokerOffsetStoreTest.java | 2 +-
.../client/impl/factory/MQClientInstanceTest.java | 6 +++---
.../rocketmq/client/producer/DefaultMQProducerTest.java | 6 +++---
.../producer/selector/SelectMessageQueueByHashTest.java | 2 +-
common/pom.xml | 4 ++++
.../src/main/java/org/apache/rocketmq/common/MixAll.java | 4 ++--
.../test/java/org/apache/rocketmq/common/MixAllTest.java | 6 +++---
.../org/apache/rocketmq/common/filter/FilterAPITest.java | 2 +-
remoting/pom.xml | 4 ++++
.../rocketmq/remoting/netty/NettyRemotingClient.java | 8 ++++----
16 files changed, 41 insertions(+), 25 deletions(-)
diff --git a/client/pom.xml b/client/pom.xml
index 7c952ac44e..24d0a54b02 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -22,12 +22,16 @@
4.1.0-incubating-SNAPSHOT
-
4.0.0jarrocketmq-clientrocketmq-client ${project.version}
+
+ 1.6
+ 1.6
+
+
${project.groupId}
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
index 2cce03d347..3903fe2b4f 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
@@ -133,7 +133,7 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
/**
* Subscription relationship
*/
- private Map subscription = new HashMap<>();
+ private Map subscription = new HashMap();
/**
* Message listener
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 b0c3b04966..7b1486082a 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
@@ -842,7 +842,11 @@ public class MQClientInstance {
try {
this.mQClientAPIImpl.unregisterClient(addr, this.clientId, producerGroup, consumerGroup, 3000);
log.info("unregister client[Producer: {} Consumer: {}] from broker[{} {} {}] success", producerGroup, consumerGroup, brokerName, entry1.getKey(), addr);
- } catch (RemotingException | InterruptedException | MQBrokerException e) {
+ } catch (RemotingException e) {
+ log.error("unregister client exception from broker: " + addr, e);
+ } catch (InterruptedException e) {
+ log.error("unregister client exception from broker: " + addr, e);
+ } catch (MQBrokerException e) {
log.error("unregister client exception from broker: " + addr, e);
}
}
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumerTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumerTest.java
index 6672b1e88d..33142617d0 100644
--- a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumerTest.java
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPullConsumerTest.java
@@ -99,7 +99,7 @@ public class DefaultMQPullConsumerTest {
assertThat(pullResult.getNextBeginOffset()).isEqualTo(1024 + 1);
assertThat(pullResult.getMinOffset()).isEqualTo(123);
assertThat(pullResult.getMaxOffset()).isEqualTo(2048);
- assertThat(pullResult.getMsgFoundList()).isEqualTo(new ArrayList<>());
+ assertThat(pullResult.getMsgFoundList()).isEqualTo(new ArrayList
+
+
+
+ maven-assembly-plugin
+ 3.0.0
+
+
+
@@ -438,40 +447,6 @@
-
- release-all
-
-
-
- maven-assembly-plugin
- 2.6
-
- apache-rocketmq
-
- release.xml
-
-
-
-
-
-
-
- release-client
-
-
-
- maven-assembly-plugin
- 2.6
-
- apache-rocketmq
-
- release-client.xml
-
-
-
-
-
- it-test
@@ -578,6 +553,11 @@
rocketmq-srvutil${project.version}
+
+ ${project.groupId}
+ rocketmq-example
+ ${project.version}
+ org.slf4jslf4j-api
diff --git a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
index eeb359808d..919c6372ee 100644
--- a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
+++ b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
@@ -130,9 +130,7 @@ public class ConsumeQueue {
MappedFile mappedFile = this.mappedFileQueue.getMappedFileByTime(timestamp);
if (mappedFile != null) {
long offset = 0;
- int low =
- minLogicOffset > mappedFile.getFileFromOffset() ? (int) (minLogicOffset - mappedFile
- .getFileFromOffset()) : 0;
+ int low = minLogicOffset > mappedFile.getFileFromOffset() ? (int) (minLogicOffset - mappedFile.getFileFromOffset()) : 0;
int high = 0;
int midOffset = -1, targetOffset = -1, leftOffset = -1, rightOffset = -1;
long leftIndexValue = -1L, rightIndexValue = -1L;
@@ -302,7 +300,6 @@ public class ConsumeQueue {
SelectMappedBufferResult result = mappedFile.selectMappedBuffer(0);
if (result != null) {
try {
-
for (int i = 0; i < result.getSize(); i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = result.getByteBuffer().getLong();
result.getByteBuffer().getInt();
@@ -310,13 +307,13 @@ public class ConsumeQueue {
if (offsetPy >= phyMinOffset) {
this.minLogicOffset = result.getMappedFile().getFileFromOffset() + i;
- log.info("compute logics min offset: " + this.getMinOffsetInQueue() + ", topic: "
- + this.topic + ", queueId: " + this.queueId);
+ log.info("Compute logical min offset: {}, topic: {}, queueId: {}",
+ this.getMinOffsetInQueue(), this.topic, this.queueId);
break;
}
}
} catch (Exception e) {
- e.printStackTrace();
+ log.error("Exception thrown when correctMinOffset", e);
} finally {
result.release();
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/MappedFile.java b/store/src/main/java/org/apache/rocketmq/store/MappedFile.java
index 5cb72ce2dd..550e578c21 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MappedFile.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MappedFile.java
@@ -402,7 +402,7 @@ public class MappedFile extends ReferenceResource {
public boolean cleanup(final long currentRef) {
if (this.isAvailable()) {
log.error("this file[REF:" + currentRef + "] " + this.fileName
- + " have not shutdown, stop unmaping.");
+ + " have not shutdown, stop unmapping.");
return false;
}
From 34012960717fb28168cd8eed2ba18a3dd2e6b4aa Mon Sep 17 00:00:00 2001
From: shtykh_roman
Date: Wed, 29 Mar 2017 16:13:51 +0800
Subject: [PATCH 24/74] [ROCKETMQ-76] Expose IntegrationTestBase to be used by
other integration projects closes apache/incubator-rocketmq#52
---
test/pom.xml | 16 +++++++++++
.../apache/rocketmq/test/util/MQAdmin.java | 2 +-
.../apache/rocketmq/test/util/TestUtils.java | 2 +-
.../apache/rocketmq/test/base/BaseConf.java | 17 +----------
.../test/base/IntegrationTestBase.java | 28 ++++++++++++++++++-
.../TagMessageWithSameGroupConsumerIT.java | 8 +++---
.../producer/async/AsyncSendExceptionIT.java | 4 +--
.../producer/querymsg/QueryMsgByIdIT.java | 2 +-
.../producer/querymsg/QueryMsgByKeyIT.java | 4 +--
9 files changed, 55 insertions(+), 28 deletions(-)
diff --git a/test/pom.xml b/test/pom.xml
index 42ebea7cf1..adb51fd4a2 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -47,4 +47,20 @@
0.30
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+
diff --git a/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java b/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java
index c3e0572d4e..680780aeb5 100644
--- a/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java
+++ b/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java
@@ -52,7 +52,7 @@ public class MQAdmin {
while (!createResult) {
createResult = checkTopicExist(mqAdminExt, topic);
if (System.currentTimeMillis() - startTime < waitTimeSec * 1000) {
- TestUtils.waitForMonment(100);
+ TestUtils.waitForMoment(100);
} else {
log.error(String.format("timeout,but create topic[%s] failed!", topic));
break;
diff --git a/test/src/main/java/org/apache/rocketmq/test/util/TestUtils.java b/test/src/main/java/org/apache/rocketmq/test/util/TestUtils.java
index 6326d46dc3..3eb1f7d741 100644
--- a/test/src/main/java/org/apache/rocketmq/test/util/TestUtils.java
+++ b/test/src/main/java/org/apache/rocketmq/test/util/TestUtils.java
@@ -20,7 +20,7 @@ package org.apache.rocketmq.test.util;
import java.util.concurrent.TimeUnit;
public class TestUtils {
- public static void waitForMonment(long time) {
+ public static void waitForMoment(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException var3) {
diff --git a/test/src/test/java/org/apache/rocketmq/test/base/BaseConf.java b/test/src/test/java/org/apache/rocketmq/test/base/BaseConf.java
index 57462a247a..92f77b8533 100644
--- a/test/src/test/java/org/apache/rocketmq/test/base/BaseConf.java
+++ b/test/src/test/java/org/apache/rocketmq/test/base/BaseConf.java
@@ -42,7 +42,6 @@ public class BaseConf {
protected static int brokerNum;
protected static int waitTime = 5;
protected static int consumeTime = 1 * 60 * 1000;
- protected static int topicCreateTime = 30 * 1000;
protected static NamesrvController namesrvController;
protected static BrokerController brokerController1;
protected static BrokerController brokerController2;
@@ -66,22 +65,8 @@ public class BaseConf {
}
public static String initTopic() {
- long startTime = System.currentTimeMillis();
String topic = MQRandomUtils.getRandomTopic();
- boolean createResult = false;
- while (true) {
- createResult = MQAdmin.createTopic(nsAddr, clusterName, topic, 8);
- if (createResult) {
- break;
- } else if (System.currentTimeMillis() - startTime > topicCreateTime) {
- Assert.fail(String.format("topic[%s] is created failed after:%d ms", topic,
- System.currentTimeMillis() - startTime));
- break;
- } else {
- TestUtils.waitForMonment(500);
- continue;
- }
- }
+ IntegrationTestBase.initTopic(topic, nsAddr, clusterName);
return topic;
}
diff --git a/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java b/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java
index ff9996d45b..5329991a3e 100644
--- a/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java
+++ b/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java
@@ -30,18 +30,23 @@ import org.apache.rocketmq.namesrv.NamesrvController;
import org.apache.rocketmq.remoting.netty.NettyClientConfig;
import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.apache.rocketmq.test.util.MQAdmin;
+import org.apache.rocketmq.test.util.TestUtils;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IntegrationTestBase {
+ public static Logger logger = LoggerFactory.getLogger(IntegrationTestBase.class);
+
protected static final String SEP = File.separator;
protected static final String BROKER_NAME_PREFIX = "TestBrokerName_";
protected static final AtomicInteger BROKER_INDEX = new AtomicInteger(0);
protected static final List TMPE_FILES = new ArrayList<>();
protected static final List BROKER_CONTROLLERS = new ArrayList<>();
protected static final List NAMESRV_CONTROLLERS = new ArrayList<>();
- public static Logger logger = LoggerFactory.getLogger(IntegrationTestBase.class);
+ protected static int topicCreateTime = 30 * 1000;
+
protected static Random random = new Random();
static {
@@ -125,6 +130,27 @@ public class IntegrationTestBase {
return brokerController;
}
+ public static boolean initTopic(String topic, String nsAddr, String clusterName) {
+ long startTime = System.currentTimeMillis();
+ boolean createResult;
+
+ while (true) {
+ createResult = MQAdmin.createTopic(nsAddr, clusterName, topic, 8);
+ if (createResult) {
+ break;
+ } else if (System.currentTimeMillis() - startTime > topicCreateTime) {
+ Assert.fail(String.format("topic[%s] is created failed after:%d ms", topic,
+ System.currentTimeMillis() - startTime));
+ break;
+ } else {
+ TestUtils.waitForMoment(500);
+ continue;
+ }
+ }
+
+ return createResult;
+ }
+
public static void deleteFile(File file) {
if (!file.exists()) {
return;
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java
index 03e81eb593..4cf8161e39 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java
@@ -78,10 +78,10 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
RMQNormalConsumer consumer1 = getConsumer(nsAddr, topic, tag,
new RMQNormalListner(originMsgDCName, msgBodyDCName));
producer.send(tag, msgSize, 100);
- TestUtils.waitForMonment(5);
+ TestUtils.waitForMoment(5);
RMQNormalConsumer consumer2 = getConsumer(nsAddr, consumer1.getConsumerGroup(), tag,
new RMQNormalListner(originMsgDCName, msgBodyDCName));
- TestUtils.waitForMonment(5);
+ TestUtils.waitForMoment(5);
consumer1.getListner().waitForMessageConsume(producer.getAllMsgBody(), consumeTime);
assertThat(VerifyUtils.getFilterdMessage(producer.getAllMsgBody(),
@@ -102,10 +102,10 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
new RMQNormalListner(originMsgDCName, msgBodyDCName));
producer.send(tag, msgSize, 100);
- TestUtils.waitForMonment(5);
+ TestUtils.waitForMoment(5);
consumer2.shutdown();
mqClients.remove(1);
- TestUtils.waitForMonment(5);
+ TestUtils.waitForMoment(5);
consumer1.getListner().waitForMessageConsume(producer.getAllMsgBody(), consumeTime);
assertThat(VerifyUtils.getFilterdMessage(producer.getAllMsgBody(),
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendExceptionIT.java b/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendExceptionIT.java
index 4125433bab..b3d258f6fb 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendExceptionIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendExceptionIT.java
@@ -112,7 +112,7 @@ public class AsyncSendExceptionIT extends BaseConf {
int checkNum = 50;
while (!sendFail && checkNum > 0) {
checkNum--;
- TestUtils.waitForMonment(100);
+ TestUtils.waitForMoment(100);
}
producer.shutdown();
assertThat(sendFail).isEqualTo(true);
@@ -141,7 +141,7 @@ public class AsyncSendExceptionIT extends BaseConf {
int checkNum = 50;
while (sendFail && checkNum > 0) {
checkNum--;
- TestUtils.waitForMonment(100);
+ TestUtils.waitForMoment(100);
}
producer.shutdown();
assertThat(sendFail).isEqualTo(false);
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByIdIT.java b/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByIdIT.java
index 92c40c7ff7..2cdd66c992 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByIdIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByIdIT.java
@@ -64,7 +64,7 @@ public class QueryMsgByIdIT extends BaseConf {
MessageExt recvMsg = (MessageExt) consumer.getListner().getFirstMsg();
MessageExt queryMsg = null;
try {
- TestUtils.waitForMonment(3000);
+ TestUtils.waitForMoment(3000);
queryMsg = producer.getProducer().viewMessage(((MessageClientExt) recvMsg).getOffsetMsgId());
} catch (Exception e) {
}
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByKeyIT.java b/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByKeyIT.java
index ec45a2925b..68dd8db2dd 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByKeyIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/producer/querymsg/QueryMsgByKeyIT.java
@@ -59,7 +59,7 @@ public class QueryMsgByKeyIT extends BaseConf {
List queryMsgs = null;
try {
- TestUtils.waitForMonment(500 * 3);
+ TestUtils.waitForMoment(500 * 3);
queryMsgs = producer.getProducer().queryMessage(topic, key, msgSize, begin - 5000,
System.currentTimeMillis() + 5000).getMessageList();
} catch (Exception e) {
@@ -89,7 +89,7 @@ public class QueryMsgByKeyIT extends BaseConf {
i--;
queryMsgs = producer.getProducer().queryMessage(topic, key, msgSize, begin - 15000,
System.currentTimeMillis() + 15000).getMessageList();
- TestUtils.waitForMonment(1000);
+ TestUtils.waitForMoment(1000);
if (i == 0 || (queryMsgs != null && queryMsgs.size() == max)) {
break;
From 8c793c09077455bf44e961517514f3551d3d2436 Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Wed, 29 Mar 2017 21:50:59 +0800
Subject: [PATCH 25/74] Include client IP per message queue of consumer
progress command output
---
.../consumer/ConsumerProgressSubCommand.java | 41 +++++++++++++++----
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java
index 35fd26010a..f341362c3f 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java
@@ -16,10 +16,6 @@
*/
package org.apache.rocketmq.tools.command.consumer;
-import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
@@ -30,7 +26,9 @@ import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.admin.ConsumeStats;
import org.apache.rocketmq.common.admin.OffsetWrapper;
import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.protocol.body.Connection;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
+import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.TopicList;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
@@ -40,6 +38,13 @@ import org.apache.rocketmq.tools.command.SubCommand;
import org.apache.rocketmq.tools.command.SubCommandException;
import org.slf4j.Logger;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
public class ConsumerProgressSubCommand implements SubCommand {
private final Logger log = ClientLogger.getLog();
@@ -62,6 +67,24 @@ public class ConsumerProgressSubCommand implements SubCommand {
return options;
}
+ private Map getMessageQueueAllocationResult(DefaultMQAdminExt defaultMQAdminExt,
+ String groupName) {
+ Map results = new HashMap<>();
+ try {
+ ConsumerConnection consumerConnection = defaultMQAdminExt.examineConsumerConnectionInfo(groupName);
+ for (Connection connection : consumerConnection.getConnectionSet()) {
+ String clientId = connection.getClientId();
+ ConsumerRunningInfo consumerRunningInfo = defaultMQAdminExt.getConsumerRunningInfo(groupName, clientId,
+ false);
+ for (MessageQueue messageQueue : consumerRunningInfo.getMqTable().keySet()) {
+ results.put(messageQueue, clientId.split("@")[0]);
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ return results;
+ }
+
@Override
public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) throws SubCommandException {
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
@@ -75,13 +98,14 @@ public class ConsumerProgressSubCommand implements SubCommand {
List mqList = new LinkedList();
mqList.addAll(consumeStats.getOffsetTable().keySet());
Collections.sort(mqList);
-
- System.out.printf("%-32s %-32s %-4s %-20s %-20s %-20s %s%n",
+ Map messageQueueAllocationResult = getMessageQueueAllocationResult(defaultMQAdminExt, consumerGroup);
+ System.out.printf("%-32s %-32s %-4s %-20s %-20s %-20s %-20s %s%n",
"#Topic",
"#Broker Name",
"#QID",
"#Broker Offset",
"#Consumer Offset",
+ "#Client IP",
"#Diff",
"#LastTime");
@@ -95,12 +119,15 @@ public class ConsumerProgressSubCommand implements SubCommand {
lastTime = UtilAll.formatDate(new Date(offsetWrapper.getLastTimestamp()), UtilAll.YYYY_MM_DD_HH_MM_SS);
} catch (Exception e) {
}
- System.out.printf("%-32s %-32s %-4d %-20d %-20d %-20d %s%n",
+
+ String clientIP = messageQueueAllocationResult.get(mq);
+ System.out.printf("%-32s %-32s %-4d %-20d %-20d %-20s %-20d %s%n",
UtilAll.frontStringAtLeast(mq.getTopic(), 32),
UtilAll.frontStringAtLeast(mq.getBrokerName(), 32),
mq.getQueueId(),
offsetWrapper.getBrokerOffset(),
offsetWrapper.getConsumerOffset(),
+ null != clientIP ? clientIP : "NA",
diff,
lastTime
);
From 3d35eed1a388a8e4d43e1f9034d62895d8fd7d9e Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Tue, 11 Apr 2017 14:38:05 +0800
Subject: [PATCH 26/74] [ROCKETMQ-138] Remove hard coded Aliyun authentication
code, closes apache/incubator-rocketmq#75
---
.../rocketmq/remoting/netty/NettyRemotingAbstract.java | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
index 08b51db310..83eeb02da4 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
@@ -126,11 +126,8 @@ public abstract class NettyRemotingAbstract {
}
}
} catch (Throwable e) {
- if (!"com.aliyun.openservices.ons.api.impl.authority.exception.AuthenticationException"
- .equals(e.getClass().getCanonicalName())) {
- PLOG.error("process request exception", e);
- PLOG.error(cmd.toString());
- }
+ PLOG.error("process request exception", e);
+ PLOG.error(cmd.toString());
if (!cmd.isOnewayRPC()) {
final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR, //
From 55b55706dd8ddba335d9909a89232fe83142e840 Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Wed, 12 Apr 2017 21:46:45 +0800
Subject: [PATCH 27/74] Guard MQVesion methods.
---
.../org/apache/rocketmq/common/MQVersion.java | 16 +++++--
.../apache/rocketmq/common/MQVersionTest.java | 47 +++++++++++++++++++
2 files changed, 58 insertions(+), 5 deletions(-)
create mode 100644 common/src/test/java/org/apache/rocketmq/common/MQVersionTest.java
diff --git a/common/src/main/java/org/apache/rocketmq/common/MQVersion.java b/common/src/main/java/org/apache/rocketmq/common/MQVersion.java
index 60fb7239bf..23f64dc401 100644
--- a/common/src/main/java/org/apache/rocketmq/common/MQVersion.java
+++ b/common/src/main/java/org/apache/rocketmq/common/MQVersion.java
@@ -21,16 +21,20 @@ public class MQVersion {
public static final int CURRENT_VERSION = Version.V4_1_0_SNAPSHOT.ordinal();
public static String getVersionDesc(int value) {
- try {
- Version v = Version.values()[value];
- return v.name();
- } catch (Exception e) {
+ int length = Version.values().length;
+ if (value >= length) {
+ return Version.values()[length - 1].name();
}
- return "HigherVersion";
+ return Version.values()[value].name();
}
public static Version value2Version(int value) {
+ int length = Version.values().length;
+ if (value >= length) {
+ return Version.values()[length - 1];
+ }
+
return Version.values()[value];
}
@@ -935,5 +939,7 @@ public class MQVersion {
V5_9_9_SNAPSHOT,
V5_9_9,
+
+ HIGHER_VERSION
}
}
diff --git a/common/src/test/java/org/apache/rocketmq/common/MQVersionTest.java b/common/src/test/java/org/apache/rocketmq/common/MQVersionTest.java
new file mode 100644
index 0000000000..ac6269f3b8
--- /dev/null
+++ b/common/src/test/java/org/apache/rocketmq/common/MQVersionTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import org.junit.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MQVersionTest {
+
+ @Test
+ public void testGetVersionDesc() throws Exception {
+ String desc = "V3_0_0_SNAPSHOT";
+ assertThat(MQVersion.getVersionDesc(0)).isEqualTo(desc);
+ }
+
+ @Test
+ public void testGetVersionDesc_higherVersion() throws Exception {
+ String desc = "HIGHER_VERSION";
+ assertThat(MQVersion.getVersionDesc(Integer.MAX_VALUE)).isEqualTo(desc);
+ }
+
+ @Test
+ public void testValue2Version() throws Exception {
+ assertThat(MQVersion.value2Version(0)).isEqualTo(MQVersion.Version.V3_0_0_SNAPSHOT);
+ }
+
+
+ @Test
+ public void testValue2Version_HigherVersion() throws Exception {
+ assertThat(MQVersion.value2Version(Integer.MAX_VALUE)).isEqualTo(MQVersion.Version.HIGHER_VERSION);
+ }
+}
\ No newline at end of file
From cf723ad4e4dc6f2d57e123e5fe1485a191f27597 Mon Sep 17 00:00:00 2001
From: Li Zhanhui
Date: Thu, 6 Apr 2017 16:43:52 +0800
Subject: [PATCH 28/74] Fix issue 165
---
.../java/org/apache/rocketmq/store/DefaultMessageStore.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
index 5c2d27f1f3..0edfeec47b 100644
--- a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
+++ b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
@@ -462,7 +462,7 @@ public class DefaultMessageStore implements MessageStore {
long maxPhyOffsetPulling = 0;
int i = 0;
- final int maxFilterMessageCount = 16000;
+ final int maxFilterMessageCount = Math.max(16000, maxMsgNums * ConsumeQueue.CQ_STORE_UNIT_SIZE);
final boolean diskFallRecorded = this.messageStoreConfig.isDiskFallRecorded();
for (; i < bufferConsumeQueue.getSize() && i < maxFilterMessageCount; i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = bufferConsumeQueue.getByteBuffer().getLong();
From 051527d44a758ba25b497dd30ba800f9b06ca823 Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Wed, 12 Apr 2017 22:25:11 +0800
Subject: [PATCH 29/74] BugFix: WS_DOMAIN_NAME, SUBGROUP default values
override custom values passed by java options options
---
broker/pom.xml | 13 ++++++
.../apache/rocketmq/broker/BrokerStartup.java | 6 +--
.../rocketmq/broker/BrokerStartupTest.java | 40 +++++++++++++++++++
3 files changed, 55 insertions(+), 4 deletions(-)
create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/BrokerStartupTest.java
diff --git a/broker/pom.xml b/broker/pom.xml
index 4d0aae65a1..8cdafea349 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -65,4 +65,17 @@
javassist
+
+
+
+
+ maven-surefire-plugin
+ 2.19.1
+
+ 1
+ false
+
+
+
+
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
index 39ee8dd2e5..98ff136574 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
@@ -232,12 +232,10 @@ public class BrokerStartup {
private static void properties2SystemEnv(Properties properties) {
if (properties == null) {
- log.info("No properties to set system environment");
return;
}
-
- String rmqAddressServerDomain = properties.getProperty("rmqAddressServerDomain", MixAll.DEFAULT_NAMESRV_ADDR_LOOKUP);
- String rmqAddressServerSubGroup = properties.getProperty("rmqAddressServerSubGroup", "nsaddr");
+ String rmqAddressServerDomain = properties.getProperty("rmqAddressServerDomain", MixAll.WS_DOMAIN_NAME);
+ String rmqAddressServerSubGroup = properties.getProperty("rmqAddressServerSubGroup", MixAll.WS_DOMAIN_SUBGROUP);
System.setProperty("rocketmq.namesrv.domain", rmqAddressServerDomain);
System.setProperty("rocketmq.namesrv.domain.subgroup", rmqAddressServerSubGroup);
}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/BrokerStartupTest.java b/broker/src/test/java/org/apache/rocketmq/broker/BrokerStartupTest.java
new file mode 100644
index 0000000000..a5ad3ac39e
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/BrokerStartupTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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 java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Properties;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BrokerStartupTest {
+
+ @Test
+ public void testProperties2SystemEnv() throws NoSuchMethodException, InvocationTargetException,
+ IllegalAccessException {
+ Properties properties = new Properties();
+ Class clazz = BrokerStartup.class;
+ Method method = clazz.getDeclaredMethod("properties2SystemEnv", Properties.class);
+ method.setAccessible(true);
+ System.setProperty("rocketmq.namesrv.domain", "value");
+ method.invoke(null, properties);
+ Assert.assertEquals("value", System.getProperty("rocketmq.namesrv.domain"));
+ }
+
+}
\ No newline at end of file
From 15c2b55ace26de6c98e0c5c26477556922a29451 Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Mon, 17 Apr 2017 19:28:26 +0800
Subject: [PATCH 30/74] [ROCKETMQ-172]log improvement for rocketmq client
closes apache/incubator-rocketmq#90
---
.../org/apache/rocketmq/client/impl/MQClientAPIImpl.java | 6 +++---
.../rocketmq/client/impl/factory/MQClientInstance.java | 2 +-
.../apache/rocketmq/remoting/netty/NettyRemotingClient.java | 2 ++
3 files changed, 6 insertions(+), 4 deletions(-)
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 6119e2483e..ff25334fa9 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
@@ -593,12 +593,12 @@ public class MQClientAPIImpl {
}
} else {
if (!responseFuture.isSendRequestOK()) {
- pullCallback.onException(new MQClientException("send request failed", responseFuture.getCause()));
+ pullCallback.onException(new MQClientException("send request failed to " + addr + ". Request: " + request, responseFuture.getCause()));
} else if (responseFuture.isTimeout()) {
- pullCallback.onException(new MQClientException("wait response timeout " + responseFuture.getTimeoutMillis() + "ms",
+ pullCallback.onException(new MQClientException("wait response from " + addr + " timeout :" + responseFuture.getTimeoutMillis() + "ms" + ". Request: " + request,
responseFuture.getCause()));
} else {
- pullCallback.onException(new MQClientException("unknow reseaon", responseFuture.getCause()));
+ pullCallback.onException(new MQClientException("unknown reason. addr: " + addr + ", timeoutMillis: " + timeoutMillis + ". Request: " + request, responseFuture.getCause()));
}
}
}
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 11266c4d15..d7e02fe3dd 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
@@ -596,7 +596,7 @@ public class MQClientInstance {
}
}
}
- log.info("topicRouteTable.put TopicRouteData[{}]", cloneTopicRouteData);
+ log.info("topicRouteTable.put. Topic = {}, TopicRouteData[{}]", topic, cloneTopicRouteData);
this.topicRouteTable.put(topic, cloneTopicRouteData);
return true;
}
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 85f9244d2d..26088aa5a5 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
@@ -321,6 +321,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
if (update) {
Collections.shuffle(addrs);
+ log.info("name server address updated. NEW : {} , OLD: {}",addrs,old);
this.namesrvAddrList.set(addrs);
}
}
@@ -398,6 +399,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
String newAddr = addrList.get(index);
this.namesrvAddrChoosed.set(newAddr);
+ log.info("new name server is chosen. OLD: {} , NEW: {}. namesrvIndex = {}", addr, newAddr, namesrvIndex);
Channel channelNew = this.createChannel(newAddr);
if (channelNew != null)
return channelNew;
From 3c6260a6eba09bb0322b4f883f28be8ac5527a54 Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Wed, 19 Apr 2017 11:58:48 +0800
Subject: [PATCH 31/74] [ROCKETMQ-107] Fix possible concurrency problem on
ServiceState when consumer start/shutdown, closes
apache/incubator-rocketmq#68
---
.../consumer/DefaultMQPullConsumerImpl.java | 21 ++++++++++++-------
.../consumer/DefaultMQPushConsumerImpl.java | 15 ++++++-------
2 files changed, 22 insertions(+), 14 deletions(-)
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java
index b26d062bf4..7d43b372e4 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java
@@ -70,7 +70,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
private final RPCHook rpcHook;
private final ArrayList consumeMessageHookList = new ArrayList();
private final ArrayList filterMessageHookList = new ArrayList();
- private ServiceState serviceState = ServiceState.CREATE_JUST;
+ private volatile ServiceState serviceState = ServiceState.CREATE_JUST;
private MQClientInstance mQClientFactory;
private PullAPIWrapper pullAPIWrapper;
private OffsetStore offsetStore;
@@ -161,7 +161,8 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
return this.pullSyncImpl(mq, subExpression, offset, maxNums, false, timeout);
}
- private PullResult pullSyncImpl(MessageQueue mq, String subExpression, long offset, int maxNums, boolean block, long timeout)
+ private PullResult pullSyncImpl(MessageQueue mq, String subExpression, long offset, int maxNums, boolean block,
+ long timeout)
throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
this.makeSureStateOK();
@@ -365,7 +366,8 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
pull(mq, subExpression, offset, maxNums, pullCallback, this.defaultMQPullConsumer.getConsumerPullTimeoutMillis());
}
- public void pull(MessageQueue mq, String subExpression, long offset, int maxNums, PullCallback pullCallback, long timeout)
+ public void pull(MessageQueue mq, String subExpression, long offset, int maxNums, PullCallback pullCallback,
+ long timeout)
throws MQClientException, RemotingException, InterruptedException {
this.pullAsyncImpl(mq, subExpression, offset, maxNums, pullCallback, false, timeout);
}
@@ -449,7 +451,8 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
return defaultMQPullConsumer;
}
- public void pullBlockIfNotFound(MessageQueue mq, String subExpression, long offset, int maxNums, PullCallback pullCallback)
+ public void pullBlockIfNotFound(MessageQueue mq, String subExpression, long offset, int maxNums,
+ PullCallback pullCallback)
throws MQClientException, RemotingException, InterruptedException {
this.pullAsyncImpl(mq, subExpression, offset, maxNums, pullCallback, true,
this.getDefaultMQPullConsumer().getConsumerPullTimeoutMillis());
@@ -510,7 +513,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
}
}
- public void shutdown() {
+ public synchronized void shutdown() {
switch (this.serviceState) {
case CREATE_JUST:
break;
@@ -528,7 +531,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
}
}
- public void start() throws MQClientException {
+ public synchronized void start() throws MQClientException {
switch (this.serviceState) {
case CREATE_JUST:
this.serviceState = ServiceState.START_FAILED;
@@ -593,6 +596,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
default:
break;
}
+
}
private void checkConfig() throws MQClientException {
@@ -662,7 +666,8 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
this.offsetStore.updateOffset(mq, offset, false);
}
- public MessageExt viewMessage(String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
+ public MessageExt viewMessage(String msgId)
+ throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().viewMessage(msgId);
}
@@ -692,6 +697,8 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner {
return serviceState;
}
+ //Don't use this deprecated setter, which will be removed soon.
+ @Deprecated
public void setServiceState(ServiceState serviceState) {
this.serviceState = serviceState;
}
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
index 4f33732ddd..67f3ebe2fb 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
@@ -97,7 +97,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
private final long consumerStartTimestamp = System.currentTimeMillis();
private final ArrayList consumeMessageHookList = new ArrayList();
private final RPCHook rpcHook;
- private ServiceState serviceState = ServiceState.CREATE_JUST;
+ private volatile ServiceState serviceState = ServiceState.CREATE_JUST;
private MQClientInstance mQClientFactory;
private PullAPIWrapper pullAPIWrapper;
private volatile boolean pause = false;
@@ -515,7 +515,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
}
}
- public void shutdown() {
+ public synchronized void shutdown() {
switch (this.serviceState) {
case CREATE_JUST:
break;
@@ -535,7 +535,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
}
}
- public void start() throws MQClientException {
+ public synchronized void start() throws MQClientException {
switch (this.serviceState) {
case CREATE_JUST:
log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
@@ -615,9 +615,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
}
this.updateTopicSubscribeInfoWhenSubscriptionChanged();
-
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
-
this.mQClientFactory.rebalanceImmediately();
}
@@ -855,7 +853,8 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
this.consumeMessageService.updateCorePoolSize(corePoolSize);
}
- public MessageExt viewMessage(String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
+ public MessageExt viewMessage(String msgId)
+ throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
return this.mQClientFactory.getMQAdminImpl().viewMessage(msgId);
}
@@ -1014,7 +1013,9 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
return serviceState;
}
- public void setServiceState(ServiceState serviceState) {
+ //Don't use this deprecated setter, which will be removed soon.
+ @Deprecated
+ public synchronized void setServiceState(ServiceState serviceState) {
this.serviceState = serviceState;
}
From 361d5f1d73b125c64444c4ece761ddfd6b6b2949 Mon Sep 17 00:00:00 2001
From: yukon
Date: Thu, 20 Apr 2017 11:36:46 +0800
Subject: [PATCH 32/74] [ROCKETMQ-176] Use new maven central badge with the
newest release version info in README.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 81eded4f55..7a9abb113b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
## Apache RocketMQ [](https://travis-ci.org/apache/incubator-rocketmq) [](https://coveralls.io/github/apache/incubator-rocketmq?branch=master)
-[](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.rocketmq)
+[](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.rocketmq)
[](https://github.org/apache/rocketmqreleases)
[](https://www.apache.org/licenses/LICENSE-2.0.html)
From 2093b656739ee24e6fd2a252d3c98b0e6610c83a Mon Sep 17 00:00:00 2001
From: yukon
Date: Thu, 20 Apr 2017 14:11:37 +0800
Subject: [PATCH 33/74] [ROCKETMQ-168] Polish the BUILDING guide.
---
BUILDING | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/BUILDING b/BUILDING
index 8a304952b7..c6e23f594e 100644
--- a/BUILDING
+++ b/BUILDING
@@ -34,4 +34,4 @@ Build Instructions for Apache RocketMQ
Execute the following command in order to build the tar.gz packages and install JAR into local repository:
- $ mvn -Prelease-all -DskipTests clean package install -U
\ No newline at end of file
+ $ mvn -Prelease-all -DskipTests clean install -U
\ No newline at end of file
From 9eeb2f7e4e321c2f4688a3d3470731c0314c7cf3 Mon Sep 17 00:00:00 2001
From: vsair
Date: Fri, 21 Apr 2017 18:17:58 +0800
Subject: [PATCH 34/74] [ROCKETMQ-121]Support message filtering based on SQL92
closes apache/incubator-rocketmq#82
---
broker/pom.xml | 4 +
.../rocketmq/broker/BrokerController.java | 27 +
.../broker/BrokerPathConfigHelper.java | 3 +
.../broker/client/ConsumerGroupEvent.java | 33 +
.../client/ConsumerIdsChangeListener.java | 6 +-
.../broker/client/ConsumerManager.java | 11 +-
.../DefaultConsumerIdsChangeListener.java | 37 +-
.../filter/CommitLogDispatcherCalcBitMap.java | 110 ++
.../broker/filter/ConsumerFilterData.java | 151 ++
.../broker/filter/ConsumerFilterManager.java | 471 ++++++
.../ExpressionForRetryMessageFilter.java | 97 ++
.../filter/ExpressionMessageFilter.java | 162 ++
.../filter/MessageEvaluationContext.java | 58 +
.../NotifyMessageArrivingListener.java | 8 +-
.../broker/longpolling/PullRequest.java | 10 +-
.../longpolling/PullRequestHoldService.java | 19 +-
.../rocketmq/broker/out/BrokerOuterAPI.java | 2 +-
.../plugin/AbstractPluginMessageStore.java | 18 +-
.../processor/AdminBrokerProcessor.java | 91 ++
.../processor/ClientManageProcessor.java | 44 +
.../processor/PullMessageProcessor.java | 59 +-
.../CommitLogDispatcherCalcBitMapTest.java | 192 +++
.../filter/ConsumerFilterManagerTest.java | 291 ++++
.../filter/MessageStoreWithFilterTest.java | 392 +++++
.../processor/PullMessageProcessorTest.java | 9 +-
.../consumer/DefaultMQPushConsumer.java | 15 +
.../client/consumer/MQPushConsumer.java | 21 +
.../client/consumer/MessageSelector.java | 77 +
.../client/impl/FindBrokerResult.java | 12 +
.../rocketmq/client/impl/MQClientAPIImpl.java | 57 +-
.../consumer/DefaultMQPushConsumerImpl.java | 40 +-
.../client/impl/consumer/PullAPIWrapper.java | 40 +
.../client/impl/factory/MQClientInstance.java | 60 +-
.../apache/rocketmq/common/BrokerConfig.java | 67 +
.../org/apache/rocketmq/common/MixAll.java | 14 +-
.../rocketmq/common/constant/LoggerName.java | 1 +
.../common/filter/ExpressionType.java | 67 +
.../rocketmq/common/filter/FilterAPI.java | 18 +
.../rocketmq/common/message/Message.java | 6 +
.../common/message/MessageDecoder.java | 39 +
.../common/namesrv/TopAddressing.java | 2 +-
.../rocketmq/common/protocol/RequestCode.java | 4 +
.../common/protocol/ResponseCode.java | 4 +
.../protocol/body/CheckClientRequestBody.java | 52 +
.../protocol/body/ConsumeQueueData.java | 98 ++
.../body/QueryConsumeQueueResponseBody.java | 72 +
.../header/PullMessageRequestHeader.java | 9 +
.../QueryConsumeQueueRequestHeader.java | 75 +
.../protocol/heartbeat/SubscriptionData.java | 17 +-
.../rocketmq/common/filter/FilterAPITest.java | 49 +
.../common/message/MessageDecoderTest.java | 80 +
distribution/conf/logback_broker.xml | 28 +
distribution/release.xml | 1 +
.../rocketmq/example/benchmark/Consumer.java | 31 +-
.../rocketmq/example/benchmark/Producer.java | 34 +-
.../rocketmq/example/filter/SqlConsumer.java | 62 +
.../rocketmq/example/filter/SqlProducer.java | 67 +
filter/pom.xml | 43 +
.../apache/rocketmq/filter/FilterFactory.java | 72 +
.../org/apache/rocketmq/filter/FilterSpi.java | 43 +
.../org/apache/rocketmq/filter/SqlFilter.java | 43 +
.../rocketmq/filter/constant/UnaryType.java | 26 +
.../filter/expression/BinaryExpression.java | 91 ++
.../filter/expression/BooleanExpression.java | 39 +
.../expression/ComparisonExpression.java | 413 +++++
.../filter/expression/ConstantExpression.java | 156 ++
.../expression/EmptyEvaluationContext.java | 35 +
.../filter/expression/EvaluationContext.java | 43 +
.../filter/expression/Expression.java | 38 +
.../filter/expression/LogicExpression.java | 94 ++
.../filter/expression/MQFilterException.java | 46 +
.../filter/expression/NowExpression.java | 36 +
.../filter/expression/PropertyExpression.java | 70 +
.../filter/expression/UnaryExpression.java | 267 ++++
.../filter/expression/UnaryInExpression.java | 61 +
.../filter/parser/ParseException.java | 204 +++
.../filter/parser/SelectorParser.java | 1354 +++++++++++++++++
.../rocketmq/filter/parser/SelectorParser.jj | 524 +++++++
.../parser/SelectorParserConstants.java | 140 ++
.../parser/SelectorParserTokenManager.java | 919 +++++++++++
.../filter/parser/SimpleCharStream.java | 502 ++++++
.../apache/rocketmq/filter/parser/Token.java | 152 ++
.../rocketmq/filter/parser/TokenMgrError.java | 174 +++
.../rocketmq/filter/util/BitsArray.java | 260 ++++
.../rocketmq/filter/util/BloomFilter.java | 338 ++++
.../rocketmq/filter/util/BloomFilterData.java | 83 +
.../apache/rocketmq/filter/BitsArrayTest.java | 123 ++
.../rocketmq/filter/BloomFilterTest.java | 172 +++
.../rocketmq/filter/ExpressionTest.java | 594 ++++++++
.../apache/rocketmq/filter/FilterSpiTest.java | 84 +
.../apache/rocketmq/filter/ParserTest.java | 129 ++
pom.xml | 11 +
srvutil/pom.xml | 4 +
.../org/apache/rocketmq/store/CommitLog.java | 8 +-
.../rocketmq/store/CommitLogDispatcher.java | 26 +
.../apache/rocketmq/store/ConsumeQueue.java | 122 +-
.../rocketmq/store/ConsumeQueueExt.java | 638 ++++++++
.../rocketmq/store/DefaultMessageFilter.java | 41 +-
.../rocketmq/store/DefaultMessageStore.java | 134 +-
.../rocketmq/store/DispatchRequest.java | 21 +-
.../org/apache/rocketmq/store/MappedFile.java | 25 +
.../rocketmq/store/MappedFileQueue.java | 2 +-
.../store/MessageArrivingListener.java | 5 +-
.../apache/rocketmq/store/MessageFilter.java | 26 +-
.../apache/rocketmq/store/MessageStore.java | 8 +-
.../store/config/MessageStoreConfig.java | 31 +
.../store/config/StorePathConfigHelper.java | 4 +
.../schedule/ScheduleMessageService.java | 14 +
.../rocketmq/store/ConsumeQueueExtTest.java | 251 +++
.../rocketmq/store/ConsumeQueueTest.java | 226 +++
.../store/DefaultMessageStoreTest.java | 4 +-
.../tools/admin/DefaultMQAdminExt.java | 9 +
.../tools/admin/DefaultMQAdminExtImpl.java | 8 +
.../rocketmq/tools/admin/MQAdminExt.java | 22 +
.../tools/command/MQAdminStartup.java | 3 +
.../queue/QueryConsumeQueueCommand.java | 159 ++
116 files changed, 12559 insertions(+), 135 deletions(-)
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupEvent.java
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMap.java
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterData.java
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterManager.java
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionForRetryMessageFilter.java
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionMessageFilter.java
create mode 100644 broker/src/main/java/org/apache/rocketmq/broker/filter/MessageEvaluationContext.java
create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMapTest.java
create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/filter/ConsumerFilterManagerTest.java
create mode 100644 broker/src/test/java/org/apache/rocketmq/broker/filter/MessageStoreWithFilterTest.java
create mode 100644 client/src/main/java/org/apache/rocketmq/client/consumer/MessageSelector.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/filter/ExpressionType.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumeQueueData.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/protocol/body/QueryConsumeQueueResponseBody.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryConsumeQueueRequestHeader.java
create mode 100644 common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java
create mode 100644 example/src/main/java/org/apache/rocketmq/example/filter/SqlConsumer.java
create mode 100644 example/src/main/java/org/apache/rocketmq/example/filter/SqlProducer.java
create mode 100644 filter/pom.xml
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/FilterFactory.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/FilterSpi.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/SqlFilter.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/constant/UnaryType.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/BinaryExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/BooleanExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/ComparisonExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/ConstantExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/EmptyEvaluationContext.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/EvaluationContext.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserConstants.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserTokenManager.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/Token.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/parser/TokenMgrError.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/util/BitsArray.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilter.java
create mode 100644 filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilterData.java
create mode 100644 filter/src/test/java/org/apache/rocketmq/filter/BitsArrayTest.java
create mode 100644 filter/src/test/java/org/apache/rocketmq/filter/BloomFilterTest.java
create mode 100644 filter/src/test/java/org/apache/rocketmq/filter/ExpressionTest.java
create mode 100644 filter/src/test/java/org/apache/rocketmq/filter/FilterSpiTest.java
create mode 100644 filter/src/test/java/org/apache/rocketmq/filter/ParserTest.java
create mode 100644 store/src/main/java/org/apache/rocketmq/store/CommitLogDispatcher.java
create mode 100644 store/src/main/java/org/apache/rocketmq/store/ConsumeQueueExt.java
create mode 100644 store/src/test/java/org/apache/rocketmq/store/ConsumeQueueExtTest.java
create mode 100644 store/src/test/java/org/apache/rocketmq/store/ConsumeQueueTest.java
create mode 100644 tools/src/main/java/org/apache/rocketmq/tools/command/queue/QueryConsumeQueueCommand.java
diff --git a/broker/pom.xml b/broker/pom.xml
index 8cdafea349..0f8ad0a135 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -48,6 +48,10 @@
${project.groupId}rocketmq-srvutil
+
+ ${project.groupId}
+ rocketmq-filter
+ ch.qos.logbacklogback-classic
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
index 6acd40ca77..bacd25ccf5 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
@@ -37,6 +37,8 @@ import org.apache.rocketmq.broker.client.DefaultConsumerIdsChangeListener;
import org.apache.rocketmq.broker.client.ProducerManager;
import org.apache.rocketmq.broker.client.net.Broker2Client;
import org.apache.rocketmq.broker.client.rebalance.RebalanceLockManager;
+import org.apache.rocketmq.broker.filter.CommitLogDispatcherCalcBitMap;
+import org.apache.rocketmq.broker.filter.ConsumerFilterManager;
import org.apache.rocketmq.broker.filtersrv.FilterServerManager;
import org.apache.rocketmq.broker.latency.BrokerFastFailure;
import org.apache.rocketmq.broker.latency.BrokerFixedThreadPoolExecutor;
@@ -96,6 +98,7 @@ public class BrokerController {
private final MessageStoreConfig messageStoreConfig;
private final ConsumerOffsetManager consumerOffsetManager;
private final ConsumerManager consumerManager;
+ private final ConsumerFilterManager consumerFilterManager;
private final ProducerManager producerManager;
private final ClientHousekeepingService clientHousekeepingService;
private final PullMessageProcessor pullMessageProcessor;
@@ -149,6 +152,7 @@ public class BrokerController {
this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService);
this.consumerIdsChangeListener = new DefaultConsumerIdsChangeListener(this);
this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener);
+ this.consumerFilterManager = new ConsumerFilterManager(this);
this.producerManager = new ProducerManager();
this.clientHousekeepingService = new ClientHousekeepingService(this);
this.broker2Client = new Broker2Client(this);
@@ -192,6 +196,7 @@ public class BrokerController {
result = result && this.consumerOffsetManager.load();
result = result && this.subscriptionGroupManager.load();
+ result = result && this.consumerFilterManager.load();
if (result) {
try {
@@ -202,6 +207,7 @@ public class BrokerController {
//load plugin
MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig);
this.messageStore = MessageStoreFactory.build(context, this.messageStore);
+ this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));
} catch (IOException e) {
result = false;
e.printStackTrace();
@@ -274,6 +280,17 @@ public class BrokerController {
}
}, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
+ this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ BrokerController.this.consumerFilterManager.persist();
+ } catch (Throwable e) {
+ log.error("schedule persist consumer filter error.", e);
+ }
+ }
+ }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
+
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
@@ -400,9 +417,11 @@ public class BrokerController {
ClientManageProcessor clientProcessor = new ClientManageProcessor(this);
this.remotingServer.registerProcessor(RequestCode.HEART_BEAT, clientProcessor, this.clientManageExecutor);
this.remotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientProcessor, this.clientManageExecutor);
+ this.remotingServer.registerProcessor(RequestCode.CHECK_CLIENT_CONFIG, clientProcessor, this.clientManageExecutor);
this.fastRemotingServer.registerProcessor(RequestCode.HEART_BEAT, clientProcessor, this.clientManageExecutor);
this.fastRemotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientProcessor, this.clientManageExecutor);
+ this.remotingServer.registerProcessor(RequestCode.CHECK_CLIENT_CONFIG, clientProcessor, this.clientManageExecutor);
/**
* ConsumerManageProcessor
@@ -504,6 +523,10 @@ public class BrokerController {
return consumerManager;
}
+ public ConsumerFilterManager getConsumerFilterManager() {
+ return consumerFilterManager;
+ }
+
public ConsumerOffsetManager getConsumerOffsetManager() {
return consumerOffsetManager;
}
@@ -590,6 +613,10 @@ public class BrokerController {
if (this.brokerFastFailure != null) {
this.brokerFastFailure.shutdown();
}
+
+ if (this.consumerFilterManager != null) {
+ this.consumerFilterManager.persist();
+ }
}
private void unregisterBrokerAll() {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerPathConfigHelper.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerPathConfigHelper.java
index 24876df48e..0a323ee80c 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerPathConfigHelper.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerPathConfigHelper.java
@@ -44,4 +44,7 @@ public class BrokerPathConfigHelper {
return rootDir + File.separator + "config" + File.separator + "subscriptionGroup.json";
}
+ public static String getConsumerFilterPath(final String rootDir) {
+ return rootDir + File.separator + "config" + File.separator + "consumerFilter.json";
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupEvent.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupEvent.java
new file mode 100644
index 0000000000..717fb7085e
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.client;
+
+public enum ConsumerGroupEvent {
+
+ /**
+ * Some consumers in the group are changed.
+ */
+ CHANGE,
+ /**
+ * The group of consumer is unregistered.
+ */
+ UNREGISTER,
+ /**
+ * The group of consumer is registered.
+ */
+ REGISTER
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
index 07d28dca4b..831e293273 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
@@ -16,9 +16,7 @@
*/
package org.apache.rocketmq.broker.client;
-import io.netty.channel.Channel;
-import java.util.List;
-
public interface ConsumerIdsChangeListener {
- void consumerIdsChanged(final String group, final List channels);
+
+ void handle(ConsumerGroupEvent event, String group, Object... args);
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
index a2d88d5d4a..a5ddec8cc2 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
@@ -85,10 +85,11 @@ public class ConsumerManager {
if (remove != null) {
log.info("unregister consumer ok, no any connection, and remove consumer group, {}",
next.getKey());
+ this.consumerIdsChangeListener.handle(ConsumerGroupEvent.UNREGISTER, next.getKey());
}
}
- this.consumerIdsChangeListener.consumerIdsChanged(next.getKey(), info.getAllChannel());
+ this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, next.getKey(), info.getAllChannel());
}
}
}
@@ -111,10 +112,12 @@ public class ConsumerManager {
if (r1 || r2) {
if (isNotifyConsumerIdsChangedEnable) {
- this.consumerIdsChangeListener.consumerIdsChanged(group, consumerGroupInfo.getAllChannel());
+ this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
}
+ this.consumerIdsChangeListener.handle(ConsumerGroupEvent.REGISTER, group, subList);
+
return r1 || r2;
}
@@ -126,10 +129,12 @@ public class ConsumerManager {
ConsumerGroupInfo remove = this.consumerTable.remove(group);
if (remove != null) {
log.info("unregister consumer ok, no any connection, and remove consumer group, {}", group);
+
+ this.consumerIdsChangeListener.handle(ConsumerGroupEvent.UNREGISTER, group);
}
}
if (isNotifyConsumerIdsChangedEnable) {
- this.consumerIdsChangeListener.consumerIdsChanged(group, consumerGroupInfo.getAllChannel());
+ this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
}
}
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java b/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java
index a1b2d8ac69..d716a339fa 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java
@@ -17,8 +17,12 @@
package org.apache.rocketmq.broker.client;
import io.netty.channel.Channel;
+
+import java.util.Collection;
import java.util.List;
+
import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
public class DefaultConsumerIdsChangeListener implements ConsumerIdsChangeListener {
private final BrokerController brokerController;
@@ -28,11 +32,34 @@ public class DefaultConsumerIdsChangeListener implements ConsumerIdsChangeListen
}
@Override
- public void consumerIdsChanged(String group, List channels) {
- if (channels != null && brokerController.getBrokerConfig().isNotifyConsumerIdsChangedEnable()) {
- for (Channel chl : channels) {
- this.brokerController.getBroker2Client().notifyConsumerIdsChanged(chl, group);
- }
+ public void handle(ConsumerGroupEvent event, String group, Object... args) {
+ if (event == null) {
+ return;
+ }
+ switch (event) {
+ case CHANGE:
+ if (args == null || args.length < 1) {
+ return;
+ }
+ List channels = (List) args[0];
+ if (channels != null && brokerController.getBrokerConfig().isNotifyConsumerIdsChangedEnable()) {
+ for (Channel chl : channels) {
+ this.brokerController.getBroker2Client().notifyConsumerIdsChanged(chl, group);
+ }
+ }
+ break;
+ case UNREGISTER:
+ this.brokerController.getConsumerFilterManager().unRegister(group);
+ break;
+ case REGISTER:
+ if (args == null || args.length < 1) {
+ return;
+ }
+ Collection subscriptionDataList = (Collection) args[0];
+ this.brokerController.getConsumerFilterManager().register(group, subscriptionDataList);
+ break;
+ default:
+ throw new RuntimeException("Unknown event " + event);
}
}
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMap.java b/broker/src/main/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMap.java
new file mode 100644
index 0000000000..85415d6297
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMap.java
@@ -0,0 +1,110 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.filter.util.BitsArray;
+import org.apache.rocketmq.store.CommitLogDispatcher;
+import org.apache.rocketmq.store.DispatchRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Calculate bit map of filter.
+ */
+public class CommitLogDispatcherCalcBitMap implements CommitLogDispatcher {
+
+ private static final Logger log = LoggerFactory.getLogger(LoggerName.FILTER_LOGGER_NAME);
+
+ protected final BrokerConfig brokerConfig;
+ protected final ConsumerFilterManager consumerFilterManager;
+
+ public CommitLogDispatcherCalcBitMap(BrokerConfig brokerConfig, ConsumerFilterManager consumerFilterManager) {
+ this.brokerConfig = brokerConfig;
+ this.consumerFilterManager = consumerFilterManager;
+ }
+
+ @Override
+ public void dispatch(DispatchRequest request) {
+ if (!this.brokerConfig.isEnableCalcFilterBitMap()) {
+ return;
+ }
+
+ try {
+
+ Collection filterDatas = consumerFilterManager.get(request.getTopic());
+
+ if (filterDatas == null || filterDatas.isEmpty()) {
+ return;
+ }
+
+ Iterator iterator = filterDatas.iterator();
+ BitsArray filterBitMap = BitsArray.create(
+ this.consumerFilterManager.getBloomFilter().getM()
+ );
+
+ long startTime = System.currentTimeMillis();
+ while (iterator.hasNext()) {
+ ConsumerFilterData filterData = iterator.next();
+
+ if (filterData.getCompiledExpression() == null) {
+ log.error("[BUG] Consumer in filter manager has no compiled expression! {}", filterData);
+ continue;
+ }
+
+ if (filterData.getBloomFilterData() == null) {
+ log.error("[BUG] Consumer in filter manager has no bloom data! {}", filterData);
+ continue;
+ }
+
+ Object ret = null;
+ try {
+ MessageEvaluationContext context = new MessageEvaluationContext(request.getPropertiesMap());
+
+ ret = filterData.getCompiledExpression().evaluate(context);
+ } catch (Throwable e) {
+ log.error("Calc filter bit map error!commitLogOffset={}, consumer={}, {}", request.getCommitLogOffset(), filterData, e);
+ }
+
+ log.debug("Result of Calc bit map:ret={}, data={}, props={}, offset={}", ret, filterData, request.getPropertiesMap(), request.getCommitLogOffset());
+
+ // eval true
+ if (ret != null && ret instanceof Boolean && (Boolean) ret) {
+ consumerFilterManager.getBloomFilter().hashTo(
+ filterData.getBloomFilterData(),
+ filterBitMap
+ );
+ }
+ }
+
+ request.setBitMap(filterBitMap.bytes());
+
+ long eclipseTime = System.currentTimeMillis() - startTime;
+ // 1ms
+ if (eclipseTime >= 1) {
+ log.warn("Spend {} ms to calc bit map, consumerNum={}, topic={}", eclipseTime, filterDatas.size(), request.getTopic());
+ }
+ } catch (Throwable e) {
+ log.error("Calc bit map error! topic={}, offset={}, queueId={}, {}", request.getTopic(), request.getCommitLogOffset(), request.getQueueId(), e);
+ }
+ }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterData.java b/broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterData.java
new file mode 100644
index 0000000000..4db02e26f8
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterData.java
@@ -0,0 +1,151 @@
+/*
+ * 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.filter;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.util.BloomFilterData;
+
+import java.util.Collections;
+
+/**
+ * Filter data of consumer.
+ */
+public class ConsumerFilterData {
+
+ private String consumerGroup;
+ private String topic;
+ private String expression;
+ private String expressionType;
+ private transient Expression compiledExpression;
+ private long bornTime;
+ private long deadTime = 0;
+ private BloomFilterData bloomFilterData;
+ private long clientVersion;
+
+ public boolean isDead() {
+ return this.deadTime >= this.bornTime;
+ }
+
+ public long howLongAfterDeath() {
+ if (isDead()) {
+ return System.currentTimeMillis() - getDeadTime();
+ }
+ return -1;
+ }
+
+ /**
+ * Check this filter data has been used to calculate bit map when msg was stored in server.
+ *
+ * @param msgStoreTime
+ * @return
+ */
+ public boolean isMsgInLive(long msgStoreTime) {
+ return msgStoreTime > getBornTime();
+ }
+
+ public String getConsumerGroup() {
+ return consumerGroup;
+ }
+
+ public void setConsumerGroup(final String consumerGroup) {
+ this.consumerGroup = consumerGroup;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public void setTopic(final String topic) {
+ this.topic = topic;
+ }
+
+ public String getExpression() {
+ return expression;
+ }
+
+ public void setExpression(final String expression) {
+ this.expression = expression;
+ }
+
+ public String getExpressionType() {
+ return expressionType;
+ }
+
+ public void setExpressionType(final String expressionType) {
+ this.expressionType = expressionType;
+ }
+
+ public Expression getCompiledExpression() {
+ return compiledExpression;
+ }
+
+ public void setCompiledExpression(final Expression compiledExpression) {
+ this.compiledExpression = compiledExpression;
+ }
+
+ public long getBornTime() {
+ return bornTime;
+ }
+
+ public void setBornTime(final long bornTime) {
+ this.bornTime = bornTime;
+ }
+
+ public long getDeadTime() {
+ return deadTime;
+ }
+
+ public void setDeadTime(final long deadTime) {
+ this.deadTime = deadTime;
+ }
+
+ public BloomFilterData getBloomFilterData() {
+ return bloomFilterData;
+ }
+
+ public void setBloomFilterData(final BloomFilterData bloomFilterData) {
+ this.bloomFilterData = bloomFilterData;
+ }
+
+ public long getClientVersion() {
+ return clientVersion;
+ }
+
+ public void setClientVersion(long clientVersion) {
+ this.clientVersion = clientVersion;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return EqualsBuilder.reflectionEquals(this, o, Collections.emptyList());
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this, Collections.emptyList());
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+ }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterManager.java b/broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterManager.java
new file mode 100644
index 0000000000..7f790af624
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filter/ConsumerFilterManager.java
@@ -0,0 +1,471 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.BrokerPathConfigHelper;
+import org.apache.rocketmq.common.ConfigManager;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.filter.FilterFactory;
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.filter.util.BloomFilter;
+import org.apache.rocketmq.filter.util.BloomFilterData;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Consumer filter data manager.Just manage the consumers use expression filter.
+ */
+public class ConsumerFilterManager extends ConfigManager {
+
+ private static final Logger log = LoggerFactory.getLogger(LoggerName.FILTER_LOGGER_NAME);
+
+ private static final long MS_24_HOUR = 24 * 3600 * 1000;
+
+ private ConcurrentHashMap
+ filterDataByTopic = new ConcurrentHashMap(256);
+
+ private transient BrokerController brokerController;
+ private transient BloomFilter bloomFilter;
+
+ public ConsumerFilterManager() {
+ // just for test
+ this.bloomFilter = BloomFilter.createByFn(20, 64);
+ }
+
+ public ConsumerFilterManager(BrokerController brokerController) {
+ this.brokerController = brokerController;
+ this.bloomFilter = BloomFilter.createByFn(
+ brokerController.getBrokerConfig().getMaxErrorRateOfBloomFilter(),
+ brokerController.getBrokerConfig().getExpectConsumerNumUseFilter()
+ );
+ // then set bit map length of store config.
+ brokerController.getMessageStoreConfig().setBitMapLengthConsumeQueueExt(
+ this.bloomFilter.getM()
+ );
+ }
+
+ /**
+ * Build consumer filter data.Be care, bloom filter data is not included.
+ *
+ * @param topic
+ * @param consumerGroup
+ * @param expression
+ * @param type
+ * @param clientVersion
+ * @return maybe null
+ */
+ public static ConsumerFilterData build(final String topic, final String consumerGroup,
+ final String expression, final String type,
+ final long clientVersion) {
+ if (ExpressionType.isTagType(type)) {
+ return null;
+ }
+
+ ConsumerFilterData consumerFilterData = new ConsumerFilterData();
+ consumerFilterData.setTopic(topic);
+ consumerFilterData.setConsumerGroup(consumerGroup);
+ consumerFilterData.setBornTime(System.currentTimeMillis());
+ consumerFilterData.setDeadTime(0);
+ consumerFilterData.setExpression(expression);
+ consumerFilterData.setExpressionType(type);
+ consumerFilterData.setClientVersion(clientVersion);
+ try {
+ consumerFilterData.setCompiledExpression(
+ FilterFactory.INSTANCE.get(type).compile(expression)
+ );
+ } catch (Throwable e) {
+ log.error("parse error: expr={}, topic={}, group={}, error={}", expression, topic, consumerGroup, e.getMessage());
+ return null;
+ }
+
+ return consumerFilterData;
+ }
+
+ public void register(final String consumerGroup, final Collection subList) {
+ for (SubscriptionData subscriptionData : subList) {
+ register(
+ subscriptionData.getTopic(),
+ consumerGroup,
+ subscriptionData.getSubString(),
+ subscriptionData.getExpressionType(),
+ subscriptionData.getSubVersion()
+ );
+ }
+
+ // make illegal topic dead.
+ Collection groupFilterData = getByGroup(consumerGroup);
+
+ Iterator iterator = groupFilterData.iterator();
+ while (iterator.hasNext()) {
+ ConsumerFilterData filterData = iterator.next();
+
+ boolean exist = false;
+ for (SubscriptionData subscriptionData : subList) {
+ if (subscriptionData.getTopic().equals(filterData.getTopic())) {
+ exist = true;
+ break;
+ }
+ }
+
+ if (!exist && !filterData.isDead()) {
+ filterData.setDeadTime(System.currentTimeMillis());
+ log.info("Consumer filter changed: {}, make illegal topic dead:{}", consumerGroup, filterData);
+ }
+ }
+ }
+
+ public boolean register(final String topic, final String consumerGroup, final String expression,
+ final String type, final long clientVersion) {
+ if (ExpressionType.isTagType(type)) {
+ return false;
+ }
+
+ if (expression == null || expression.length() == 0) {
+ return false;
+ }
+
+ FilterDataMapByTopic filterDataMapByTopic = this.filterDataByTopic.get(topic);
+
+ if (filterDataMapByTopic == null) {
+ FilterDataMapByTopic temp = new FilterDataMapByTopic(topic);
+ FilterDataMapByTopic prev = this.filterDataByTopic.putIfAbsent(topic, temp);
+ filterDataMapByTopic = prev != null ? prev : temp;
+ }
+
+ BloomFilterData bloomFilterData = bloomFilter.generate(consumerGroup + "#" + topic);
+
+ return filterDataMapByTopic.register(consumerGroup, expression, type, bloomFilterData, clientVersion);
+ }
+
+ public void unRegister(final String consumerGroup) {
+ for (String topic : filterDataByTopic.keySet()) {
+ this.filterDataByTopic.get(topic).unRegister(consumerGroup);
+ }
+ }
+
+ public ConsumerFilterData get(final String topic, final String consumerGroup) {
+ if (!this.filterDataByTopic.containsKey(topic)) {
+ return null;
+ }
+ if (this.filterDataByTopic.get(topic).getGroupFilterData().isEmpty()) {
+ return null;
+ }
+
+ return this.filterDataByTopic.get(topic).getGroupFilterData().get(consumerGroup);
+ }
+
+ public Collection getByGroup(final String consumerGroup) {
+ Collection ret = new HashSet();
+
+ Iterator topicIterator = this.filterDataByTopic.values().iterator();
+ while (topicIterator.hasNext()) {
+ FilterDataMapByTopic filterDataMapByTopic = topicIterator.next();
+
+ Iterator filterDataIterator = filterDataMapByTopic.getGroupFilterData().values().iterator();
+
+ while (filterDataIterator.hasNext()) {
+ ConsumerFilterData filterData = filterDataIterator.next();
+
+ if (filterData.getConsumerGroup().equals(consumerGroup)) {
+ ret.add(filterData);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public final Collection get(final String topic) {
+ if (!this.filterDataByTopic.containsKey(topic)) {
+ return null;
+ }
+ if (this.filterDataByTopic.get(topic).getGroupFilterData().isEmpty()) {
+ return null;
+ }
+
+ return this.filterDataByTopic.get(topic).getGroupFilterData().values();
+ }
+
+ public BloomFilter getBloomFilter() {
+ return bloomFilter;
+ }
+
+ @Override
+ public String encode() {
+ return encode(false);
+ }
+
+ @Override
+ public String configFilePath() {
+ if (this.brokerController != null) {
+ return BrokerPathConfigHelper.getConsumerFilterPath(
+ this.brokerController.getMessageStoreConfig().getStorePathRootDir()
+ );
+ }
+ return BrokerPathConfigHelper.getConsumerFilterPath("./unit_test");
+ }
+
+ @Override
+ public void decode(final String jsonString) {
+ ConsumerFilterManager load = RemotingSerializable.fromJson(jsonString, ConsumerFilterManager.class);
+ if (load != null && load.filterDataByTopic != null) {
+ boolean bloomChanged = false;
+ for (String topic : load.filterDataByTopic.keySet()) {
+ FilterDataMapByTopic dataMapByTopic = load.filterDataByTopic.get(topic);
+ if (dataMapByTopic == null) {
+ continue;
+ }
+
+ for (String group : dataMapByTopic.getGroupFilterData().keySet()) {
+
+ ConsumerFilterData filterData = dataMapByTopic.getGroupFilterData().get(group);
+
+ if (filterData == null) {
+ continue;
+ }
+
+ try {
+ filterData.setCompiledExpression(
+ FilterFactory.INSTANCE.get(filterData.getExpressionType()).compile(filterData.getExpression())
+ );
+ } catch (Exception e) {
+ log.error("load filter data error, " + filterData, e);
+ }
+
+ // check whether bloom filter is changed
+ // if changed, ignore the bit map calculated before.
+ if (!this.bloomFilter.isValid(filterData.getBloomFilterData())) {
+ bloomChanged = true;
+ log.info("Bloom filter is changed!So ignore all filter data persisted! {}, {}", this.bloomFilter, filterData.getBloomFilterData());
+ break;
+ }
+
+ log.info("load exist consumer filter data: {}", filterData);
+
+ if (filterData.getDeadTime() == 0) {
+ // we think all consumers are dead when load
+ long deadTime = System.currentTimeMillis() - 30 * 1000;
+ filterData.setDeadTime(
+ deadTime <= filterData.getBornTime() ? filterData.getBornTime() : deadTime
+ );
+ }
+ }
+ }
+
+ if (!bloomChanged) {
+ this.filterDataByTopic = load.filterDataByTopic;
+ }
+ }
+ }
+
+ @Override
+ public String encode(final boolean prettyFormat) {
+ // clean
+ {
+ clean();
+ }
+ return RemotingSerializable.toJson(this, prettyFormat);
+ }
+
+ public void clean() {
+ Iterator> topicIterator = this.filterDataByTopic.entrySet().iterator();
+ while (topicIterator.hasNext()) {
+ Map.Entry filterDataMapByTopic = topicIterator.next();
+
+ Iterator> filterDataIterator
+ = filterDataMapByTopic.getValue().getGroupFilterData().entrySet().iterator();
+
+ while (filterDataIterator.hasNext()) {
+ Map.Entry filterDataByGroup = filterDataIterator.next();
+
+ ConsumerFilterData filterData = filterDataByGroup.getValue();
+ if (filterData.howLongAfterDeath() >= (this.brokerController == null ? MS_24_HOUR : this.brokerController.getBrokerConfig().getFilterDataCleanTimeSpan())) {
+ log.info("Remove filter consumer {}, died too long!", filterDataByGroup.getValue());
+ filterDataIterator.remove();
+ }
+ }
+
+ if (filterDataMapByTopic.getValue().getGroupFilterData().isEmpty()) {
+ log.info("Topic has no consumer, remove it! {}", filterDataMapByTopic.getKey());
+ topicIterator.remove();
+ }
+ }
+ }
+
+ public ConcurrentHashMap getFilterDataByTopic() {
+ return filterDataByTopic;
+ }
+
+ public void setFilterDataByTopic(final ConcurrentHashMap filterDataByTopic) {
+ this.filterDataByTopic = filterDataByTopic;
+ }
+
+ public static class FilterDataMapByTopic {
+
+ private ConcurrentHashMap
+ groupFilterData = new ConcurrentHashMap();
+
+ private String topic;
+
+ public FilterDataMapByTopic() {
+ }
+
+ public FilterDataMapByTopic(String topic) {
+ this.topic = topic;
+ }
+
+ public void unRegister(String consumerGroup) {
+ if (!this.groupFilterData.containsKey(consumerGroup)) {
+ return;
+ }
+
+ ConsumerFilterData data = this.groupFilterData.get(consumerGroup);
+
+ if (data == null || data.isDead()) {
+ return;
+ }
+
+ long now = System.currentTimeMillis();
+
+ log.info("Unregister consumer filter: {}, deadTime: {}", data, now);
+
+ data.setDeadTime(now);
+ }
+
+ public boolean register(String consumerGroup, String expression, String type, BloomFilterData bloomFilterData, long clientVersion) {
+ ConsumerFilterData old = this.groupFilterData.get(consumerGroup);
+
+ if (old == null) {
+ ConsumerFilterData consumerFilterData = build(topic, consumerGroup, expression, type, clientVersion);
+ if (consumerFilterData == null) {
+ return false;
+ }
+ consumerFilterData.setBloomFilterData(bloomFilterData);
+
+ old = this.groupFilterData.putIfAbsent(consumerGroup, consumerFilterData);
+ if (old == null) {
+ log.info("New consumer filter registered: {}", consumerFilterData);
+ return true;
+ } else {
+ if (clientVersion <= old.getClientVersion()) {
+ if (!type.equals(old.getExpressionType()) || !expression.equals(old.getExpression())) {
+ log.warn("Ignore consumer({} : {}) filter(concurrent), because of version {} <= {}, but maybe info changed!old={}:{}, ignored={}:{}",
+ consumerGroup, topic,
+ clientVersion, old.getClientVersion(),
+ old.getExpressionType(), old.getExpression(),
+ type, expression);
+ }
+ if (clientVersion == old.getClientVersion() && old.isDead()) {
+ reAlive(old);
+ return true;
+ }
+
+ return false;
+ } else {
+ this.groupFilterData.put(consumerGroup, consumerFilterData);
+ log.info("New consumer filter registered(concurrent): {}, old: {}", consumerFilterData, old);
+ return true;
+ }
+ }
+ } else {
+ if (clientVersion <= old.getClientVersion()) {
+ if (!type.equals(old.getExpressionType()) || !expression.equals(old.getExpression())) {
+ log.info("Ignore consumer({}:{}) filter, because of version {} <= {}, but maybe info changed!old={}:{}, ignored={}:{}",
+ consumerGroup, topic,
+ clientVersion, old.getClientVersion(),
+ old.getExpressionType(), old.getExpression(),
+ type, expression);
+ }
+ if (clientVersion == old.getClientVersion() && old.isDead()) {
+ reAlive(old);
+ return true;
+ }
+
+ return false;
+ }
+
+ boolean change = !old.getExpression().equals(expression) || !old.getExpressionType().equals(type);
+ if (old.getBloomFilterData() == null && bloomFilterData != null) {
+ change = true;
+ }
+ if (old.getBloomFilterData() != null && !old.getBloomFilterData().equals(bloomFilterData)) {
+ change = true;
+ }
+
+ // if subscribe data is changed, or consumer is died too long.
+ if (change) {
+ ConsumerFilterData consumerFilterData = build(topic, consumerGroup, expression, type, clientVersion);
+ if (consumerFilterData == null) {
+ // new expression compile error, remove old, let client report error.
+ this.groupFilterData.remove(consumerGroup);
+ return false;
+ }
+ consumerFilterData.setBloomFilterData(bloomFilterData);
+
+ this.groupFilterData.put(consumerGroup, consumerFilterData);
+
+ log.info("Consumer filter info change, old: {}, new: {}, change: {}",
+ old, consumerFilterData, change);
+
+ return true;
+ } else {
+ old.setClientVersion(clientVersion);
+ if (old.isDead()) {
+ reAlive(old);
+ }
+ return true;
+ }
+ }
+ }
+
+ protected void reAlive(ConsumerFilterData filterData) {
+ long oldDeadTime = filterData.getDeadTime();
+ filterData.setDeadTime(0);
+ log.info("Re alive consumer filter: {}, oldDeadTime: {}", filterData, oldDeadTime);
+ }
+
+ public final ConsumerFilterData get(String consumerGroup) {
+ return this.groupFilterData.get(consumerGroup);
+ }
+
+ public final ConcurrentHashMap getGroupFilterData() {
+ return this.groupFilterData;
+ }
+
+ public void setGroupFilterData(final ConcurrentHashMap groupFilterData) {
+ this.groupFilterData = groupFilterData;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public void setTopic(final String topic) {
+ this.topic = topic;
+ }
+ }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionForRetryMessageFilter.java b/broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionForRetryMessageFilter.java
new file mode 100644
index 0000000000..9518178951
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionForRetryMessageFilter.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.filter;
+
+
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * Support filter to retry topic.
+ * It will decode properties first in order to get real topic.
+ */
+public class ExpressionForRetryMessageFilter extends ExpressionMessageFilter {
+ public ExpressionForRetryMessageFilter(SubscriptionData subscriptionData, ConsumerFilterData consumerFilterData, ConsumerFilterManager consumerFilterManager) {
+ super(subscriptionData, consumerFilterData, consumerFilterManager);
+ }
+
+ @Override
+ public boolean isMatchedByCommitLog(ByteBuffer msgBuffer, Map properties) {
+ if (subscriptionData == null) {
+ return true;
+ }
+
+ if (subscriptionData.isClassFilterMode()) {
+ return true;
+ }
+
+ boolean isRetryTopic = subscriptionData.getTopic().startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX);
+
+ if (!isRetryTopic && ExpressionType.isTagType(subscriptionData.getExpressionType())) {
+ return true;
+ }
+
+ ConsumerFilterData realFilterData = this.consumerFilterData;
+ Map tempProperties = properties;
+ boolean decoded = false;
+ if (isRetryTopic) {
+ // retry topic, use original filter data.
+ // poor performance to support retry filter.
+ if (tempProperties == null && msgBuffer != null) {
+ decoded = true;
+ tempProperties = MessageDecoder.decodeProperties(msgBuffer);
+ }
+ String realTopic = tempProperties.get(MessageConst.PROPERTY_RETRY_TOPIC);
+ String group = subscriptionData.getTopic().substring(MixAll.RETRY_GROUP_TOPIC_PREFIX.length());
+ realFilterData = this.consumerFilterManager.get(realTopic, group);
+ }
+
+ // no expression
+ if (realFilterData == null || realFilterData.getExpression() == null
+ || realFilterData.getCompiledExpression() == null) {
+ return true;
+ }
+
+ if (!decoded && tempProperties == null && msgBuffer != null) {
+ tempProperties = MessageDecoder.decodeProperties(msgBuffer);
+ }
+
+ Object ret = null;
+ try {
+ MessageEvaluationContext context = new MessageEvaluationContext(tempProperties);
+
+ ret = realFilterData.getCompiledExpression().evaluate(context);
+ } catch (Throwable e) {
+ log.error("Message Filter error, " + realFilterData + ", " + tempProperties, e);
+ }
+
+ log.debug("Pull eval result: {}, {}, {}", ret, realFilterData, tempProperties);
+
+ if (ret == null || !(ret instanceof Boolean)) {
+ return false;
+ }
+
+ return (Boolean) ret;
+ }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionMessageFilter.java b/broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionMessageFilter.java
new file mode 100644
index 0000000000..893df0d7c6
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filter/ExpressionMessageFilter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.filter.util.BitsArray;
+import org.apache.rocketmq.filter.util.BloomFilter;
+import org.apache.rocketmq.store.ConsumeQueueExt;
+import org.apache.rocketmq.store.MessageFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+public class ExpressionMessageFilter implements MessageFilter {
+
+ protected static final Logger log = LoggerFactory.getLogger(LoggerName.FILTER_LOGGER_NAME);
+
+ protected final SubscriptionData subscriptionData;
+ protected final ConsumerFilterData consumerFilterData;
+ protected final ConsumerFilterManager consumerFilterManager;
+ protected final boolean bloomDataValid;
+
+ public ExpressionMessageFilter(SubscriptionData subscriptionData, ConsumerFilterData consumerFilterData,
+ ConsumerFilterManager consumerFilterManager) {
+ this.subscriptionData = subscriptionData;
+ this.consumerFilterData = consumerFilterData;
+ this.consumerFilterManager = consumerFilterManager;
+ if (consumerFilterData == null) {
+ bloomDataValid = false;
+ return;
+ }
+ BloomFilter bloomFilter = this.consumerFilterManager.getBloomFilter();
+ if (bloomFilter != null && bloomFilter.isValid(consumerFilterData.getBloomFilterData())) {
+ bloomDataValid = true;
+ } else {
+ bloomDataValid = false;
+ }
+ }
+
+ @Override
+ public boolean isMatchedByConsumeQueue(Long tagsCode, ConsumeQueueExt.CqExtUnit cqExtUnit) {
+ if (null == subscriptionData) {
+ return true;
+ }
+
+ if (subscriptionData.isClassFilterMode()) {
+ return true;
+ }
+
+ // by tags code.
+ if (ExpressionType.isTagType(subscriptionData.getExpressionType())) {
+
+ if (tagsCode == null || tagsCode < 0L) {
+ return true;
+ }
+
+ if (subscriptionData.getSubString().equals(SubscriptionData.SUB_ALL)) {
+ return true;
+ }
+
+ return subscriptionData.getCodeSet().contains(tagsCode.intValue());
+ } else {
+ // no expression or no bloom
+ if (consumerFilterData == null || consumerFilterData.getExpression() == null
+ || consumerFilterData.getCompiledExpression() == null || consumerFilterData.getBloomFilterData() == null) {
+ return true;
+ }
+
+ // message is before consumer
+ if (cqExtUnit == null || !consumerFilterData.isMsgInLive(cqExtUnit.getMsgStoreTime())) {
+ log.debug("Pull matched because not in live: {}, {}", consumerFilterData, cqExtUnit);
+ return true;
+ }
+
+ byte[] filterBitMap = cqExtUnit.getFilterBitMap();
+ BloomFilter bloomFilter = this.consumerFilterManager.getBloomFilter();
+ if (filterBitMap == null || !this.bloomDataValid
+ || filterBitMap.length * Byte.SIZE != consumerFilterData.getBloomFilterData().getBitNum()) {
+ return true;
+ }
+
+ BitsArray bitsArray = null;
+ try {
+ bitsArray = BitsArray.create(filterBitMap);
+ boolean ret = bloomFilter.isHit(consumerFilterData.getBloomFilterData(), bitsArray);
+ log.debug("Pull {} by bit map:{}, {}, {}", ret, consumerFilterData, bitsArray, cqExtUnit);
+ return ret;
+ } catch (Throwable e) {
+ log.error("bloom filter error, sub=" + subscriptionData
+ + ", filter=" + consumerFilterData + ", bitMap=" + bitsArray, e);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isMatchedByCommitLog(ByteBuffer msgBuffer, Map properties) {
+ if (subscriptionData == null) {
+ return true;
+ }
+
+ if (subscriptionData.isClassFilterMode()) {
+ return true;
+ }
+
+ if (ExpressionType.isTagType(subscriptionData.getExpressionType())) {
+ return true;
+ }
+
+ ConsumerFilterData realFilterData = this.consumerFilterData;
+ Map tempProperties = properties;
+
+ // no expression
+ if (realFilterData == null || realFilterData.getExpression() == null
+ || realFilterData.getCompiledExpression() == null) {
+ return true;
+ }
+
+ if (tempProperties == null && msgBuffer != null) {
+ tempProperties = MessageDecoder.decodeProperties(msgBuffer);
+ }
+
+ Object ret = null;
+ try {
+ MessageEvaluationContext context = new MessageEvaluationContext(tempProperties);
+
+ ret = realFilterData.getCompiledExpression().evaluate(context);
+ } catch (Throwable e) {
+ log.error("Message Filter error, " + realFilterData + ", " + tempProperties, e);
+ }
+
+ log.debug("Pull eval result: {}, {}, {}", ret, realFilterData, tempProperties);
+
+ if (ret == null || !(ret instanceof Boolean)) {
+ return false;
+ }
+
+ return (Boolean) ret;
+ }
+
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filter/MessageEvaluationContext.java b/broker/src/main/java/org/apache/rocketmq/broker/filter/MessageEvaluationContext.java
new file mode 100644
index 0000000000..879d17908f
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filter/MessageEvaluationContext.java
@@ -0,0 +1,58 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.filter.expression.EvaluationContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Evaluation context from message.
+ */
+public class MessageEvaluationContext implements EvaluationContext {
+
+ private Map properties;
+
+ public MessageEvaluationContext(Map properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public Object get(final String name) {
+ if (this.properties == null) {
+ return null;
+ }
+ return this.properties.get(name);
+ }
+
+ @Override
+ public Map keyValues() {
+ if (properties == null) {
+ return null;
+ }
+
+ Map copy = new HashMap(properties.size(), 1);
+
+ for (String key : properties.keySet()) {
+ copy.put(key, properties.get(key));
+ }
+
+ return copy;
+ }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java
index 2dec9f746b..fd38c4fb76 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java
@@ -19,6 +19,8 @@ package org.apache.rocketmq.broker.longpolling;
import org.apache.rocketmq.store.MessageArrivingListener;
+import java.util.Map;
+
public class NotifyMessageArrivingListener implements MessageArrivingListener {
private final PullRequestHoldService pullRequestHoldService;
@@ -27,7 +29,9 @@ public class NotifyMessageArrivingListener implements MessageArrivingListener {
}
@Override
- public void arriving(String topic, int queueId, long logicOffset, long tagsCode) {
- this.pullRequestHoldService.notifyMessageArriving(topic, queueId, logicOffset, tagsCode);
+ public void arriving(String topic, int queueId, long logicOffset, long tagsCode,
+ long msgStoreTime, byte[] filterBitMap, Map properties) {
+ this.pullRequestHoldService.notifyMessageArriving(topic, queueId, logicOffset, tagsCode,
+ msgStoreTime, filterBitMap, properties);
}
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequest.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequest.java
index b66344f3be..045ab9b16a 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequest.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequest.java
@@ -19,6 +19,7 @@ package org.apache.rocketmq.broker.longpolling;
import io.netty.channel.Channel;
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.store.MessageFilter;
public class PullRequest {
private final RemotingCommand requestCommand;
@@ -27,15 +28,18 @@ public class PullRequest {
private final long suspendTimestamp;
private final long pullFromThisOffset;
private final SubscriptionData subscriptionData;
+ private final MessageFilter messageFilter;
public PullRequest(RemotingCommand requestCommand, Channel clientChannel, long timeoutMillis, long suspendTimestamp,
- long pullFromThisOffset, SubscriptionData subscriptionData) {
+ long pullFromThisOffset, SubscriptionData subscriptionData,
+ MessageFilter messageFilter) {
this.requestCommand = requestCommand;
this.clientChannel = clientChannel;
this.timeoutMillis = timeoutMillis;
this.suspendTimestamp = suspendTimestamp;
this.pullFromThisOffset = pullFromThisOffset;
this.subscriptionData = subscriptionData;
+ this.messageFilter = messageFilter;
}
public RemotingCommand getRequestCommand() {
@@ -61,4 +65,8 @@ public class PullRequest {
public SubscriptionData getSubscriptionData() {
return subscriptionData;
}
+
+ public MessageFilter getMessageFilter() {
+ return messageFilter;
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
index fdba50daaf..1a53db198b 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
@@ -18,13 +18,13 @@ package org.apache.rocketmq.broker.longpolling;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.common.ServiceThread;
import org.apache.rocketmq.common.SystemClock;
import org.apache.rocketmq.common.constant.LoggerName;
-import org.apache.rocketmq.store.DefaultMessageFilter;
-import org.apache.rocketmq.store.MessageFilter;
+import org.apache.rocketmq.store.ConsumeQueueExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,7 +33,6 @@ public class PullRequestHoldService extends ServiceThread {
private static final String TOPIC_QUEUEID_SEPARATOR = "@";
private final BrokerController brokerController;
private final SystemClock systemClock = new SystemClock();
- private final MessageFilter messageFilter = new DefaultMessageFilter();
private ConcurrentHashMap pullRequestTable =
new ConcurrentHashMap(1024);
@@ -110,10 +109,11 @@ public class PullRequestHoldService extends ServiceThread {
}
public void notifyMessageArriving(final String topic, final int queueId, final long maxOffset) {
- notifyMessageArriving(topic, queueId, maxOffset, null);
+ notifyMessageArriving(topic, queueId, maxOffset, null, 0, null, null);
}
- public void notifyMessageArriving(final String topic, final int queueId, final long maxOffset, final Long tagsCode) {
+ public void notifyMessageArriving(final String topic, final int queueId, final long maxOffset, final Long tagsCode,
+ long msgStoreTime, byte[] filterBitMap, Map properties) {
String key = this.buildKey(topic, queueId);
ManyPullRequest mpr = this.pullRequestTable.get(key);
if (mpr != null) {
@@ -128,7 +128,14 @@ public class PullRequestHoldService extends ServiceThread {
}
if (newestOffset > request.getPullFromThisOffset()) {
- if (this.messageFilter.isMessageMatched(request.getSubscriptionData(), tagsCode)) {
+ boolean match = request.getMessageFilter().isMatchedByConsumeQueue(tagsCode,
+ new ConsumeQueueExt.CqExtUnit(tagsCode, msgStoreTime, filterBitMap));
+ // match by bit map, need eval again when properties is not null.
+ if (match && properties != null) {
+ match = request.getMessageFilter().isMatchedByCommitLog(null, properties);
+ }
+
+ if (match) {
try {
this.brokerController.getPullMessageProcessor().executeRequestWhenWakeup(request.getClientChannel(),
request.getRequestCommand());
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
index 039c942984..6c2a987d29 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
@@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory;
public class BrokerOuterAPI {
private static final Logger log = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
private final RemotingClient remotingClient;
- private final TopAddressing topAddressing = new TopAddressing(MixAll.WS_ADDR);
+ private final TopAddressing topAddressing = new TopAddressing(MixAll.getWSAddr());
private String nameSrvAddr = null;
public BrokerOuterAPI(final NettyClientConfig nettyClientConfig) {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
index 00257fd680..8ded973e0e 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
@@ -18,11 +18,14 @@
package org.apache.rocketmq.broker.plugin;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.Set;
import org.apache.rocketmq.common.message.MessageExt;
-import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.store.CommitLogDispatcher;
+import org.apache.rocketmq.store.ConsumeQueue;
import org.apache.rocketmq.store.GetMessageResult;
import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.store.MessageFilter;
import org.apache.rocketmq.store.MessageStore;
import org.apache.rocketmq.store.PutMessageResult;
import org.apache.rocketmq.store.QueryMessageResult;
@@ -84,8 +87,8 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
@Override
public GetMessageResult getMessage(String group, String topic, int queueId, long offset,
- int maxMsgNums, SubscriptionData subscriptionData) {
- return next.getMessage(group, topic, queueId, offset, maxMsgNums, subscriptionData);
+ int maxMsgNums, final MessageFilter messageFilter) {
+ return next.getMessage(group, topic, queueId, offset, maxMsgNums, messageFilter);
}
@Override
@@ -234,4 +237,13 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
next.setConfirmOffset(phyOffset);
}
+ @Override
+ public LinkedList getDispatcherList() {
+ return next.getDispatcherList();
+ }
+
+ @Override
+ public ConsumeQueue getConsumeQueue(String topic, int queueId) {
+ return next.getConsumeQueue(topic, queueId);
+ }
}
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 e35316d603..daea53c7de 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,6 +16,7 @@
*/
package org.apache.rocketmq.broker.processor;
+import com.alibaba.fastjson.JSON;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import java.io.UnsupportedEncodingException;
@@ -32,6 +33,8 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.ClientChannelInfo;
import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
+import org.apache.rocketmq.broker.filter.ConsumerFilterData;
+import org.apache.rocketmq.broker.filter.ExpressionMessageFilter;
import org.apache.rocketmq.common.MQVersion;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.TopicConfig;
@@ -49,6 +52,7 @@ import org.apache.rocketmq.common.protocol.ResponseCode;
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
import org.apache.rocketmq.common.protocol.body.BrokerStatsItem;
import org.apache.rocketmq.common.protocol.body.Connection;
+import org.apache.rocketmq.common.protocol.body.ConsumeQueueData;
import org.apache.rocketmq.common.protocol.body.ConsumeStatsList;
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
import org.apache.rocketmq.common.protocol.body.GroupList;
@@ -56,6 +60,7 @@ import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.LockBatchRequestBody;
import org.apache.rocketmq.common.protocol.body.LockBatchResponseBody;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
+import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
import org.apache.rocketmq.common.protocol.body.QueryConsumeTimeSpanBody;
import org.apache.rocketmq.common.protocol.body.QueryCorrectionOffsetBody;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
@@ -81,6 +86,7 @@ import org.apache.rocketmq.common.protocol.header.GetMinOffsetRequestHeader;
import org.apache.rocketmq.common.protocol.header.GetMinOffsetResponseHeader;
import org.apache.rocketmq.common.protocol.header.GetProducerConnectionListRequestHeader;
import org.apache.rocketmq.common.protocol.header.GetTopicStatsInfoRequestHeader;
+import org.apache.rocketmq.common.protocol.header.QueryConsumeQueueRequestHeader;
import org.apache.rocketmq.common.protocol.header.QueryConsumeTimeSpanRequestHeader;
import org.apache.rocketmq.common.protocol.header.QueryCorrectionOffsetHeader;
import org.apache.rocketmq.common.protocol.header.QueryTopicConsumeByWhoRequestHeader;
@@ -94,6 +100,7 @@ import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.common.stats.StatsItem;
import org.apache.rocketmq.common.stats.StatsSnapshot;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.filter.util.BitsArray;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
@@ -101,7 +108,10 @@ import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
import org.apache.rocketmq.remoting.protocol.LanguageCode;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+import org.apache.rocketmq.store.ConsumeQueue;
+import org.apache.rocketmq.store.ConsumeQueueExt;
import org.apache.rocketmq.store.DefaultMessageStore;
+import org.apache.rocketmq.store.MessageFilter;
import org.apache.rocketmq.store.SelectMappedBufferResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -187,6 +197,8 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
return ViewBrokerStatsData(ctx, request);
case RequestCode.GET_BROKER_CONSUME_STATS:
return fetchAllConsumeStatsInBroker(ctx, request);
+ case RequestCode.QUERY_CONSUME_QUEUE:
+ return queryConsumeQueue(ctx, request);
default:
break;
}
@@ -1244,4 +1256,83 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
}
}
+ private RemotingCommand queryConsumeQueue(ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
+ QueryConsumeQueueRequestHeader requestHeader =
+ (QueryConsumeQueueRequestHeader) request.decodeCommandCustomHeader(QueryConsumeQueueRequestHeader.class);
+
+ RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+ ConsumeQueue consumeQueue = this.brokerController.getMessageStore().getConsumeQueue(requestHeader.getTopic(),
+ requestHeader.getQueueId());
+ if (consumeQueue == null) {
+ response.setCode(ResponseCode.SYSTEM_ERROR);
+ response.setRemark(String.format("%d@%s is not exist!", requestHeader.getQueueId(), requestHeader.getTopic()));
+ return response;
+ }
+
+ QueryConsumeQueueResponseBody body = new QueryConsumeQueueResponseBody();
+ response.setCode(ResponseCode.SUCCESS);
+ response.setBody(body.encode());
+
+ body.setMaxQueueIndex(consumeQueue.getMaxOffsetInQueue());
+ body.setMinQueueIndex(consumeQueue.getMinOffsetInQueue());
+
+ MessageFilter messageFilter = null;
+ if (requestHeader.getConsumerGroup() != null) {
+ SubscriptionData subscriptionData = this.brokerController.getConsumerManager().findSubscriptionData(
+ requestHeader.getConsumerGroup(), requestHeader.getTopic()
+ );
+ body.setSubscriptionData(subscriptionData);
+ if (subscriptionData == null) {
+ body.setFilterData(String.format("%s@%s is not online!", requestHeader.getConsumerGroup(), requestHeader.getTopic()));
+ } else {
+ ConsumerFilterData filterData = this.brokerController.getConsumerFilterManager()
+ .get(requestHeader.getTopic(), requestHeader.getConsumerGroup());
+ body.setFilterData(JSON.toJSONString(filterData, true));
+
+ messageFilter = new ExpressionMessageFilter(subscriptionData, filterData,
+ this.brokerController.getConsumerFilterManager());
+ }
+ }
+
+ SelectMappedBufferResult result = consumeQueue.getIndexBuffer(requestHeader.getIndex());
+ if (result == null) {
+ response.setRemark(String.format("Index %d of %d@%s is not exist!", requestHeader.getIndex(), requestHeader.getQueueId(), requestHeader.getTopic()));
+ return response;
+ }
+ try {
+ List queues = new ArrayList<>();
+ for (int i = 0; i < result.getSize() && i < requestHeader.getCount() * ConsumeQueue.CQ_STORE_UNIT_SIZE; i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
+ ConsumeQueueData one = new ConsumeQueueData();
+ one.setPhysicOffset(result.getByteBuffer().getLong());
+ one.setPhysicSize(result.getByteBuffer().getInt());
+ one.setTagsCode(result.getByteBuffer().getLong());
+
+ if (!consumeQueue.isExtAddr(one.getTagsCode())) {
+ queues.add(one);
+ continue;
+ }
+
+ ConsumeQueueExt.CqExtUnit cqExtUnit = consumeQueue.getExt(one.getTagsCode());
+ if (cqExtUnit != null) {
+ one.setExtendDataJson(JSON.toJSONString(cqExtUnit));
+ if (cqExtUnit.getFilterBitMap() != null) {
+ one.setBitMap(BitsArray.create(cqExtUnit.getFilterBitMap()).toString());
+ }
+ if (messageFilter != null) {
+ one.setEval(messageFilter.isMatchedByConsumeQueue(cqExtUnit.getTagsCode(), cqExtUnit));
+ }
+ } else {
+ one.setMsg("Cq extend not exist!addr: " + one.getTagsCode());
+ }
+
+ queues.add(one);
+ }
+ body.setQueueData(queues);
+ } finally {
+ result.release();
+ }
+
+ return response;
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java
index 6349ffc9eb..67807a863d 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java
@@ -22,15 +22,19 @@ import org.apache.rocketmq.broker.client.ClientChannelInfo;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.filter.ExpressionType;
import org.apache.rocketmq.common.protocol.RequestCode;
import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.body.CheckClientRequestBody;
import org.apache.rocketmq.common.protocol.header.UnregisterClientRequestHeader;
import org.apache.rocketmq.common.protocol.header.UnregisterClientResponseHeader;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumerData;
import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
import org.apache.rocketmq.common.protocol.heartbeat.ProducerData;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
import org.apache.rocketmq.common.sysflag.TopicSysFlag;
+import org.apache.rocketmq.filter.FilterFactory;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
@@ -54,6 +58,8 @@ public class ClientManageProcessor implements NettyRequestProcessor {
return this.heartBeat(ctx, request);
case RequestCode.UNREGISTER_CLIENT:
return this.unregisterClient(ctx, request);
+ case RequestCode.CHECK_CLIENT_CONFIG:
+ return this.checkClientConfig(ctx, request);
default:
break;
}
@@ -157,4 +163,42 @@ public class ClientManageProcessor implements NettyRequestProcessor {
response.setRemark(null);
return response;
}
+
+ public RemotingCommand checkClientConfig(ChannelHandlerContext ctx, RemotingCommand request)
+ throws RemotingCommandException {
+ final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+ CheckClientRequestBody requestBody = CheckClientRequestBody.decode(request.getBody(),
+ CheckClientRequestBody.class);
+
+ if (requestBody != null && requestBody.getSubscriptionData() != null) {
+ SubscriptionData subscriptionData = requestBody.getSubscriptionData();
+
+ if (ExpressionType.isTagType(subscriptionData.getExpressionType())) {
+ response.setCode(ResponseCode.SUCCESS);
+ response.setRemark(null);
+ return response;
+ }
+
+ if (!this.brokerController.getBrokerConfig().isEnablePropertyFilter()) {
+ response.setCode(ResponseCode.SYSTEM_ERROR);
+ response.setRemark("The broker does not support consumer to filter message by " + subscriptionData.getExpressionType());
+ return response;
+ }
+
+ try {
+ FilterFactory.INSTANCE.get(subscriptionData.getExpressionType()).compile(subscriptionData.getSubString());
+ } catch (Exception e) {
+ log.warn("Client {}@{} filter message, but failed to compile expression! sub={}, error={}",
+ requestBody.getClientId(), requestBody.getGroup(), requestBody.getSubscriptionData(), e.getMessage());
+ response.setCode(ResponseCode.SUBSCRIPTION_PARSE_FAILED);
+ response.setRemark(e.getMessage());
+ return response;
+ }
+ }
+
+ response.setCode(ResponseCode.SUCCESS);
+ response.setRemark(null);
+ return response;
+ }
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java
index 89967d8f6a..10945da97c 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java
@@ -25,6 +25,10 @@ import java.nio.ByteBuffer;
import java.util.List;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
+import org.apache.rocketmq.broker.filter.ConsumerFilterData;
+import org.apache.rocketmq.broker.filter.ConsumerFilterManager;
+import org.apache.rocketmq.broker.filter.ExpressionForRetryMessageFilter;
+import org.apache.rocketmq.broker.filter.ExpressionMessageFilter;
import org.apache.rocketmq.broker.longpolling.PullRequest;
import org.apache.rocketmq.broker.mqtrace.ConsumeMessageContext;
import org.apache.rocketmq.broker.mqtrace.ConsumeMessageHook;
@@ -34,6 +38,7 @@ import org.apache.rocketmq.common.TopicConfig;
import org.apache.rocketmq.common.TopicFilterType;
import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.filter.ExpressionType;
import org.apache.rocketmq.common.filter.FilterAPI;
import org.apache.rocketmq.common.help.FAQUrl;
import org.apache.rocketmq.common.message.MessageDecoder;
@@ -54,6 +59,7 @@ import org.apache.rocketmq.remoting.netty.RequestTask;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.store.GetMessageResult;
import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.store.MessageFilter;
import org.apache.rocketmq.store.PutMessageResult;
import org.apache.rocketmq.store.config.BrokerRole;
import org.apache.rocketmq.store.stats.BrokerStatsManager;
@@ -142,13 +148,22 @@ public class PullMessageProcessor implements NettyRequestProcessor {
}
SubscriptionData subscriptionData = null;
+ ConsumerFilterData consumerFilterData = null;
if (hasSubscriptionFlag) {
try {
- subscriptionData = FilterAPI.buildSubscriptionData(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
- requestHeader.getSubscription());
+ subscriptionData = FilterAPI.build(
+ requestHeader.getTopic(), requestHeader.getSubscription(), requestHeader.getExpressionType()
+ );
+ if (!ExpressionType.isTagType(subscriptionData.getExpressionType())) {
+ consumerFilterData = ConsumerFilterManager.build(
+ requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getSubscription(),
+ requestHeader.getExpressionType(), requestHeader.getSubVersion()
+ );
+ assert consumerFilterData != null;
+ }
} catch (Exception e) {
LOG.warn("Parse the consumer's subscription[{}] failed, group: {}", requestHeader.getSubscription(), //
- requestHeader.getConsumerGroup());
+ requestHeader.getConsumerGroup());
response.setCode(ResponseCode.SUBSCRIPTION_PARSE_FAILED);
response.setRemark("parse the consumer's subscription failed");
return response;
@@ -180,16 +195,48 @@ public class PullMessageProcessor implements NettyRequestProcessor {
if (subscriptionData.getSubVersion() < requestHeader.getSubVersion()) {
LOG.warn("The broker's subscription is not latest, group: {} {}", requestHeader.getConsumerGroup(),
- subscriptionData.getSubString());
+ subscriptionData.getSubString());
response.setCode(ResponseCode.SUBSCRIPTION_NOT_LATEST);
response.setRemark("the consumer's subscription not latest");
return response;
}
+ if (!ExpressionType.isTagType(subscriptionData.getExpressionType())) {
+ consumerFilterData = this.brokerController.getConsumerFilterManager().get(requestHeader.getTopic(),
+ requestHeader.getConsumerGroup());
+ if (consumerFilterData == null) {
+ response.setCode(ResponseCode.FILTER_DATA_NOT_EXIST);
+ response.setRemark("The broker's consumer filter data is not exist!Your expression may be wrong!");
+ return response;
+ }
+ if (consumerFilterData.getClientVersion() < requestHeader.getSubVersion()) {
+ LOG.warn("The broker's consumer filter data is not latest, group: {}, topic: {}, serverV: {}, clientV: {}",
+ requestHeader.getConsumerGroup(), requestHeader.getTopic(), consumerFilterData.getClientVersion(), requestHeader.getSubVersion());
+ response.setCode(ResponseCode.FILTER_DATA_NOT_LATEST);
+ response.setRemark("the consumer's consumer filter data not latest");
+ return response;
+ }
+ }
+ }
+
+ if (!ExpressionType.isTagType(subscriptionData.getExpressionType())
+ && !this.brokerController.getBrokerConfig().isEnablePropertyFilter()) {
+ response.setCode(ResponseCode.SYSTEM_ERROR);
+ response.setRemark("The broker does not support consumer to filter message by " + subscriptionData.getExpressionType());
+ return response;
+ }
+
+ MessageFilter messageFilter;
+ if (this.brokerController.getBrokerConfig().isFilterSupportRetry()) {
+ messageFilter = new ExpressionForRetryMessageFilter(subscriptionData, consumerFilterData,
+ this.brokerController.getConsumerFilterManager());
+ } else {
+ messageFilter = new ExpressionMessageFilter(subscriptionData, consumerFilterData,
+ this.brokerController.getConsumerFilterManager());
}
final GetMessageResult getMessageResult =
this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
- requestHeader.getQueueId(), requestHeader.getQueueOffset(), requestHeader.getMaxMsgNums(), subscriptionData);
+ requestHeader.getQueueId(), requestHeader.getQueueOffset(), requestHeader.getMaxMsgNums(), messageFilter);
if (getMessageResult != null) {
response.setRemark(getMessageResult.getStatus().name());
responseHeader.setNextBeginOffset(getMessageResult.getNextBeginOffset());
@@ -368,7 +415,7 @@ public class PullMessageProcessor implements NettyRequestProcessor {
long offset = requestHeader.getQueueOffset();
int queueId = requestHeader.getQueueId();
PullRequest pullRequest = new PullRequest(request, channel, pollingTimeMills,
- this.brokerController.getMessageStore().now(), offset, subscriptionData);
+ this.brokerController.getMessageStore().now(), offset, subscriptionData, messageFilter);
this.brokerController.getPullRequestHoldService().suspendPullRequest(topic, queueId, pullRequest);
response = null;
break;
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMapTest.java b/broker/src/test/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMapTest.java
new file mode 100644
index 0000000000..87f625616c
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/filter/CommitLogDispatcherCalcBitMapTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.filter.util.BitsArray;
+import org.apache.rocketmq.store.DispatchRequest;
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CommitLogDispatcherCalcBitMapTest {
+
+ @Test
+ public void testDispatch_filterDataIllegal() {
+ BrokerConfig brokerConfig = new BrokerConfig();
+ brokerConfig.setEnableCalcFilterBitMap(true);
+
+ ConsumerFilterManager filterManager = new ConsumerFilterManager();
+
+ filterManager.register("topic0", "CID_0", "a is not null and a >= 5",
+ ExpressionType.SQL92, System.currentTimeMillis());
+
+ filterManager.register("topic0", "CID_1", "a is not null and a >= 15",
+ ExpressionType.SQL92, System.currentTimeMillis());
+
+ ConsumerFilterData nullExpression = filterManager.get("topic0", "CID_0");
+ nullExpression.setExpression(null);
+ nullExpression.setCompiledExpression(null);
+ ConsumerFilterData nullBloomData = filterManager.get("topic0", "CID_1");
+ nullBloomData.setBloomFilterData(null);
+
+
+ CommitLogDispatcherCalcBitMap calcBitMap = new CommitLogDispatcherCalcBitMap(brokerConfig,
+ filterManager);
+
+ for (int i = 0; i < 1; i++) {
+ Map properties = new HashMap(4);
+ properties.put("a", String.valueOf(i * 10 + 5));
+
+ String topic = "topic" + i;
+
+ DispatchRequest dispatchRequest = new DispatchRequest(
+ topic,
+ 0,
+ i * 100 + 123,
+ 100,
+ (long) ("tags" + i).hashCode(),
+ System.currentTimeMillis(),
+ i,
+ null,
+ UUID.randomUUID().toString(),
+ 0,
+ 0,
+ properties
+ );
+
+ calcBitMap.dispatch(dispatchRequest);
+
+ assertThat(dispatchRequest.getBitMap()).isNotNull();
+
+ BitsArray bitsArray = BitsArray.create(dispatchRequest.getBitMap(),
+ filterManager.getBloomFilter().getM());
+
+ for (int j = 0; j < bitsArray.bitLength(); j++) {
+ assertThat(bitsArray.getBit(j)).isFalse();
+ }
+ }
+ }
+
+ @Test
+ public void testDispatch_blankFilterData() {
+ BrokerConfig brokerConfig = new BrokerConfig();
+ brokerConfig.setEnableCalcFilterBitMap(true);
+
+ ConsumerFilterManager filterManager = new ConsumerFilterManager();
+
+ CommitLogDispatcherCalcBitMap calcBitMap = new CommitLogDispatcherCalcBitMap(brokerConfig,
+ filterManager);
+
+ for (int i = 0; i < 10; i++) {
+ Map properties = new HashMap(4);
+ properties.put("a", String.valueOf(i * 10 + 5));
+
+ String topic = "topic" + i;
+
+ DispatchRequest dispatchRequest = new DispatchRequest(
+ topic,
+ 0,
+ i * 100 + 123,
+ 100,
+ (long) ("tags" + i).hashCode(),
+ System.currentTimeMillis(),
+ i,
+ null,
+ UUID.randomUUID().toString(),
+ 0,
+ 0,
+ properties
+ );
+
+ calcBitMap.dispatch(dispatchRequest);
+
+ assertThat(dispatchRequest.getBitMap()).isNull();
+ }
+ }
+
+ @Test
+ public void testDispatch() {
+ BrokerConfig brokerConfig = new BrokerConfig();
+ brokerConfig.setEnableCalcFilterBitMap(true);
+
+ ConsumerFilterManager filterManager = ConsumerFilterManagerTest.gen(10, 10);
+
+ CommitLogDispatcherCalcBitMap calcBitMap = new CommitLogDispatcherCalcBitMap(brokerConfig,
+ filterManager);
+
+ for (int i = 0; i < 10; i++) {
+ Map properties = new HashMap(4);
+ properties.put("a", String.valueOf(i * 10 + 5));
+
+ String topic = "topic" + i;
+
+ DispatchRequest dispatchRequest = new DispatchRequest(
+ topic,
+ 0,
+ i * 100 + 123,
+ 100,
+ (long) ("tags" + i).hashCode(),
+ System.currentTimeMillis(),
+ i,
+ null,
+ UUID.randomUUID().toString(),
+ 0,
+ 0,
+ properties
+ );
+
+ calcBitMap.dispatch(dispatchRequest);
+
+ assertThat(dispatchRequest.getBitMap()).isNotNull();
+
+ BitsArray bits = BitsArray.create(dispatchRequest.getBitMap());
+
+ Collection filterDatas = filterManager.get(topic);
+
+ for (ConsumerFilterData filterData : filterDatas) {
+
+ if (filterManager.getBloomFilter().isHit(filterData.getBloomFilterData(), bits)) {
+ try {
+ assertThat((Boolean) filterData.getCompiledExpression().evaluate(
+ new MessageEvaluationContext(properties)
+ )).isTrue();
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+ } else {
+ try {
+ assertThat((Boolean) filterData.getCompiledExpression().evaluate(
+ new MessageEvaluationContext(properties)
+ )).isFalse();
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/filter/ConsumerFilterManagerTest.java b/broker/src/test/java/org/apache/rocketmq/broker/filter/ConsumerFilterManagerTest.java
new file mode 100644
index 0000000000..c8412a8bff
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/filter/ConsumerFilterManagerTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.common.filter.FilterAPI;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ConsumerFilterManagerTest {
+
+ public static ConsumerFilterManager gen(int topicCount, int consumerCount) {
+ ConsumerFilterManager filterManager = new ConsumerFilterManager();
+
+ for (int i = 0; i < topicCount; i++) {
+ String topic = "topic" + i;
+
+ for (int j = 0; j < consumerCount; j++) {
+
+ String consumer = "CID_" + j;
+
+ filterManager.register(topic, consumer, expr(j), ExpressionType.SQL92, System.currentTimeMillis());
+ }
+ }
+
+ return filterManager;
+ }
+
+ public static String expr(int i) {
+ return "a is not null and a > " + ((i - 1) * 10) + " and a < " + ((i + 1) * 10);
+ }
+
+ @Test
+ public void testRegister_newExpressionCompileErrorAndRemoveOld() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ assertThat(filterManager.get("topic9", "CID_9")).isNotNull();
+
+ String newExpr = "a between 10,20";
+
+ assertThat(filterManager.register("topic9", "CID_9", newExpr, ExpressionType.SQL92, System.currentTimeMillis() + 1))
+ .isFalse();
+ assertThat(filterManager.get("topic9", "CID_9")).isNull();
+
+ newExpr = "a between 10 AND 20";
+
+ assertThat(filterManager.register("topic9", "CID_9", newExpr, ExpressionType.SQL92, System.currentTimeMillis() + 1))
+ .isTrue();
+
+ ConsumerFilterData filterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(filterData).isNotNull();
+ assertThat(newExpr).isEqualTo(filterData.getExpression());
+ }
+
+ @Test
+ public void testRegister_change() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ ConsumerFilterData filterData = filterManager.get("topic9", "CID_9");
+
+ System.out.println(filterData.getCompiledExpression());
+
+ String newExpr = "a > 0 and a < 10";
+
+ filterManager.register("topic9", "CID_9", newExpr, ExpressionType.SQL92, System.currentTimeMillis() + 1);
+
+ filterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(newExpr).isEqualTo(filterData.getExpression());
+
+ System.out.println(filterData.toString());
+ }
+
+ @Test
+ public void testRegister() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ ConsumerFilterData filterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterData.isDead()).isFalse();
+
+ // new version
+ assertThat(filterManager.register(
+ "topic9", "CID_9", "a is not null", ExpressionType.SQL92, System.currentTimeMillis() + 1000
+ )).isTrue();
+
+ ConsumerFilterData newFilter = filterManager.get("topic9", "CID_9");
+
+ assertThat(newFilter).isNotEqualTo(filterData);
+
+ // same version
+ assertThat(filterManager.register(
+ "topic9", "CID_9", "a is null", ExpressionType.SQL92, newFilter.getClientVersion()
+ )).isFalse();
+
+ ConsumerFilterData filterData1 = filterManager.get("topic9", "CID_9");
+
+ assertThat(newFilter).isEqualTo(filterData1);
+ }
+
+ @Test
+ public void testRegister_reAlive() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ ConsumerFilterData filterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterData.isDead()).isFalse();
+
+ //make dead
+ filterManager.unRegister("CID_9");
+
+ //reAlive
+ filterManager.register(
+ filterData.getTopic(),
+ filterData.getConsumerGroup(),
+ filterData.getExpression(),
+ filterData.getExpressionType(),
+ System.currentTimeMillis()
+ );
+
+ ConsumerFilterData newFilterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(newFilterData).isNotNull();
+ assertThat(newFilterData.isDead()).isFalse();
+ }
+
+ @Test
+ public void testRegister_bySubscriptionData() {
+ ConsumerFilterManager filterManager = new ConsumerFilterManager();
+ List subscriptionDatas = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ try {
+ subscriptionDatas.add(
+ FilterAPI.build(
+ "topic" + i,
+ "a is not null and a > " + i,
+ ExpressionType.SQL92
+ )
+ );
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+ }
+
+ filterManager.register("CID_0", subscriptionDatas);
+
+ Collection filterDatas = filterManager.getByGroup("CID_0");
+
+ assertThat(filterDatas).isNotNull();
+ assertThat(filterDatas.size()).isEqualTo(10);
+
+ Iterator iterator = filterDatas.iterator();
+ while (iterator.hasNext()) {
+ ConsumerFilterData filterData = iterator.next();
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterManager.getBloomFilter().isValid(filterData.getBloomFilterData())).isTrue();
+ }
+ }
+
+ @Test
+ public void testRegister_tag() {
+ ConsumerFilterManager filterManager = new ConsumerFilterManager();
+
+ assertThat(filterManager.register("topic0", "CID_0", "*", null, System.currentTimeMillis())).isFalse();
+
+ Collection filterDatas = filterManager.getByGroup("CID_0");
+
+ assertThat(filterDatas).isNullOrEmpty();
+ }
+
+ @Test
+ public void testUnregister() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ ConsumerFilterData filterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterData.isDead()).isFalse();
+
+ filterManager.unRegister("CID_9");
+
+ assertThat(filterData.isDead()).isTrue();
+ }
+
+ @Test
+ public void testPersist() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ try {
+ filterManager.persist();
+
+ ConsumerFilterData filterData = filterManager.get("topic9", "CID_9");
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterData.isDead()).isFalse();
+
+ ConsumerFilterManager loadFilter = new ConsumerFilterManager();
+
+ assertThat(loadFilter.load()).isTrue();
+
+ filterData = loadFilter.get("topic9", "CID_9");
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterData.isDead()).isTrue();
+ assertThat(filterData.getCompiledExpression()).isNotNull();
+ } finally {
+ deleteDirectory("./unit_test");
+ }
+ }
+
+ @Test
+ public void testPersist_clean() {
+ ConsumerFilterManager filterManager = gen(10, 10);
+
+ String topic = "topic9";
+ for (int i = 0; i < 10; i++) {
+ String cid = "CID_" + i;
+
+ ConsumerFilterData filterData = filterManager.get(topic, cid);
+
+ assertThat(filterData).isNotNull();
+ assertThat(filterData.isDead()).isFalse();
+
+ //make dead more than 24h
+ filterData.setBornTime(System.currentTimeMillis() - 26 * 60 * 60 * 1000);
+ filterData.setDeadTime(System.currentTimeMillis() - 25 * 60 * 60 * 1000);
+ }
+
+ try {
+ filterManager.persist();
+
+ ConsumerFilterManager loadFilter = new ConsumerFilterManager();
+
+ assertThat(loadFilter.load()).isTrue();
+
+ ConsumerFilterData filterData = loadFilter.get(topic, "CID_9");
+
+ assertThat(filterData).isNull();
+
+ Collection topicData = loadFilter.get(topic);
+
+ assertThat(topicData).isNullOrEmpty();
+ } finally {
+ deleteDirectory("./unit_test");
+ }
+ }
+
+ protected void deleteDirectory(String rootPath) {
+ File file = new File(rootPath);
+ deleteFile(file);
+ }
+
+ protected void deleteFile(File file) {
+ File[] subFiles = file.listFiles();
+ if (subFiles != null) {
+ for (File sub : subFiles) {
+ deleteFile(sub);
+ }
+ }
+
+ file.delete();
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/filter/MessageStoreWithFilterTest.java b/broker/src/test/java/org/apache/rocketmq/broker/filter/MessageStoreWithFilterTest.java
new file mode 100644
index 0000000000..53e563edc2
--- /dev/null
+++ b/broker/src/test/java/org/apache/rocketmq/broker/filter/MessageStoreWithFilterTest.java
@@ -0,0 +1,392 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.store.CommitLogDispatcher;
+import org.apache.rocketmq.store.DefaultMessageStore;
+import org.apache.rocketmq.store.DispatchRequest;
+import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.store.GetMessageStatus;
+import org.apache.rocketmq.store.MessageArrivingListener;
+import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.store.PutMessageResult;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.apache.rocketmq.store.stats.BrokerStatsManager;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MessageStoreWithFilterTest {
+
+ private static final String msg = "Once, there was a chance for me!";
+ private static final byte[] msgBody = msg.getBytes();
+
+ private static final String topic = "topic";
+ private static final int queueId = 0;
+ private static final String storePath = "." + File.separator + "unit_test_store";
+ private static final int commitLogFileSize = 1024 * 1024 * 256;
+ private static final int cqFileSize = 300000 * 20;
+ private static final int cqExtFileSize = 300000 * 128;
+
+ private static SocketAddress BornHost;
+
+ private static SocketAddress StoreHost;
+
+ static {
+ try {
+ StoreHost = new InetSocketAddress(InetAddress.getLocalHost(), 8123);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ try {
+ BornHost = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public MessageExtBrokerInner buildMessage() {
+ MessageExtBrokerInner msg = new MessageExtBrokerInner();
+ msg.setTopic(topic);
+ msg.setTags("TAG1");
+ msg.setKeys("Hello");
+ msg.setBody(msgBody);
+ msg.setKeys(String.valueOf(System.currentTimeMillis()));
+ msg.setQueueId(queueId);
+ msg.setSysFlag(0);
+ msg.setBornTimestamp(System.currentTimeMillis());
+ msg.setStoreHost(StoreHost);
+ msg.setBornHost(BornHost);
+ for (int i = 1; i < 3; i++) {
+ msg.putUserProperty(String.valueOf(i), "imagoodperson" + i);
+ }
+ msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));
+
+ return msg;
+ }
+
+ public MessageStoreConfig buildStoreConfig(int commitLogFileSize, int cqFileSize,
+ boolean enableCqExt, int cqExtFileSize) {
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setMapedFileSizeCommitLog(commitLogFileSize);
+ messageStoreConfig.setMapedFileSizeConsumeQueue(cqFileSize);
+ messageStoreConfig.setMappedFileSizeConsumeQueueExt(cqExtFileSize);
+ messageStoreConfig.setMessageIndexEnable(false);
+ messageStoreConfig.setEnableConsumeQueueExt(enableCqExt);
+
+ messageStoreConfig.setStorePathRootDir(storePath);
+ messageStoreConfig.setStorePathCommitLog(storePath + File.separator + "commitlog");
+
+ return messageStoreConfig;
+ }
+
+ protected DefaultMessageStore gen(ConsumerFilterManager filterManager) throws Exception {
+ MessageStoreConfig messageStoreConfig = buildStoreConfig(
+ commitLogFileSize, cqFileSize, true, cqExtFileSize
+ );
+
+ BrokerConfig brokerConfig = new BrokerConfig();
+ brokerConfig.setEnableCalcFilterBitMap(true);
+ brokerConfig.setMaxErrorRateOfBloomFilter(20);
+ brokerConfig.setExpectConsumerNumUseFilter(64);
+
+ DefaultMessageStore master = new DefaultMessageStore(
+ messageStoreConfig,
+ new BrokerStatsManager(brokerConfig.getBrokerClusterName()),
+ new MessageArrivingListener() {
+ @Override
+ public void arriving(String topic, int queueId, long logicOffset, long tagsCode,
+ long msgStoreTime, byte[] filterBitMap, Map properties) {
+// System.out.println(String.format("Msg coming: %s, %d, %d, %d",
+// topic, queueId, logicOffset, tagsCode));
+ }
+ }
+ , brokerConfig);
+
+ master.getDispatcherList().addFirst(new CommitLogDispatcher() {
+ @Override
+ public void dispatch(DispatchRequest request) {
+ try {
+// System.out.println(String.format("offset:%d, bitMap:%s", request.getCommitLogOffset(),
+// BitsArray.create(request.getBitMap()).toString()));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ master.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(brokerConfig, filterManager));
+
+ assertThat(master.load()).isTrue();
+
+ master.start();
+
+ return master;
+ }
+
+ protected List putMsg(DefaultMessageStore master, int topicCount, int msgCountPerTopic) throws Exception {
+ List msgs = new ArrayList();
+ for (int i = 0; i < topicCount; i++) {
+ String realTopic = topic + i;
+ for (int j = 0; j < msgCountPerTopic; j++) {
+ MessageExtBrokerInner msg = buildMessage();
+ msg.setTopic(realTopic);
+ msg.putUserProperty("a", String.valueOf(j * 10 + 5));
+ msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));
+
+ PutMessageResult result = master.putMessage(msg);
+
+ msg.setMsgId(result.getAppendMessageResult().getMsgId());
+
+ msgs.add(msg);
+ }
+ }
+
+ return msgs;
+ }
+
+ protected void deleteDirectory(String rootPath) {
+ File file = new File(rootPath);
+ deleteFile(file);
+ }
+
+ protected void deleteFile(File file) {
+ File[] subFiles = file.listFiles();
+ if (subFiles != null) {
+ for (File sub : subFiles) {
+ deleteFile(sub);
+ }
+ }
+
+ file.delete();
+ }
+
+ protected List filtered(List msgs, ConsumerFilterData filterData) {
+ List filteredMsgs = new ArrayList();
+
+ for (MessageExtBrokerInner messageExtBrokerInner : msgs) {
+
+ if (!messageExtBrokerInner.getTopic().equals(filterData.getTopic())) {
+ continue;
+ }
+
+ try {
+ Object evlRet = filterData.getCompiledExpression().evaluate(new MessageEvaluationContext(messageExtBrokerInner.getProperties()));
+
+ if (evlRet == null || !(evlRet instanceof Boolean) || (Boolean) evlRet) {
+ filteredMsgs.add(messageExtBrokerInner);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+ }
+
+ return filteredMsgs;
+ }
+
+ @Test
+ public void testGetMessage_withFilterBitMapAndConsumerChanged() {
+ int topicCount = 10, msgPerTopic = 10;
+ ConsumerFilterManager filterManager = ConsumerFilterManagerTest.gen(topicCount, msgPerTopic);
+
+ DefaultMessageStore master = null;
+ try {
+ master = gen(filterManager);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+
+ try {
+ List msgs = null;
+ try {
+ msgs = putMsg(master, topicCount, msgPerTopic);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+
+ // sleep to wait for consume queue has been constructed.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+
+ // reset consumer;
+ String topic = "topic" + 0;
+ String resetGroup = "CID_" + 2;
+ String normalGroup = "CID_" + 3;
+
+ {
+ // reset CID_2@topic0 to get all messages.
+ SubscriptionData resetSubData = new SubscriptionData();
+ resetSubData.setExpressionType(ExpressionType.SQL92);
+ resetSubData.setTopic(topic);
+ resetSubData.setClassFilterMode(false);
+ resetSubData.setSubString("a is not null OR a is null");
+
+ ConsumerFilterData resetFilterData = ConsumerFilterManager.build(topic,
+ resetGroup, resetSubData.getSubString(), resetSubData.getExpressionType(),
+ System.currentTimeMillis());
+
+ GetMessageResult resetGetResult = master.getMessage(resetGroup, topic, queueId, 0, 1000,
+ new ExpressionMessageFilter(resetSubData, resetFilterData, filterManager));
+
+ try {
+ assertThat(resetGetResult).isNotNull();
+
+ List filteredMsgs = filtered(msgs, resetFilterData);
+
+ assertThat(resetGetResult.getMessageBufferList().size()).isEqualTo(filteredMsgs.size());
+ } finally {
+ resetGetResult.release();
+ }
+ }
+
+ {
+ ConsumerFilterData normalFilterData = filterManager.get(topic, normalGroup);
+ assertThat(normalFilterData).isNotNull();
+ assertThat(normalFilterData.getBornTime()).isLessThan(System.currentTimeMillis());
+
+ SubscriptionData normalSubData = new SubscriptionData();
+ normalSubData.setExpressionType(normalFilterData.getExpressionType());
+ normalSubData.setTopic(topic);
+ normalSubData.setClassFilterMode(false);
+ normalSubData.setSubString(normalFilterData.getExpression());
+
+ List filteredMsgs = filtered(msgs, normalFilterData);
+
+ GetMessageResult normalGetResult = master.getMessage(normalGroup, topic, queueId, 0, 1000,
+ new ExpressionMessageFilter(normalSubData, normalFilterData, filterManager));
+
+ try {
+ assertThat(normalGetResult).isNotNull();
+ assertThat(normalGetResult.getMessageBufferList().size()).isEqualTo(filteredMsgs.size());
+ } finally {
+ normalGetResult.release();
+ }
+ }
+ } finally {
+ master.shutdown();
+ master.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+
+ @Test
+ public void testGetMessage_withFilterBitMap() {
+ int topicCount = 10, msgPerTopic = 500;
+ ConsumerFilterManager filterManager = ConsumerFilterManagerTest.gen(topicCount, msgPerTopic);
+
+ DefaultMessageStore master = null;
+ try {
+ master = gen(filterManager);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+
+ try {
+ List msgs = null;
+ try {
+ msgs = putMsg(master, topicCount, msgPerTopic);
+ // sleep to wait for consume queue has been constructed.
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+
+ for (int i = 0; i < topicCount; i++) {
+ String realTopic = topic + i;
+
+ for (int j = 0; j < msgPerTopic; j++) {
+ String group = "CID_" + j;
+
+ ConsumerFilterData filterData = filterManager.get(realTopic, group);
+ assertThat(filterData).isNotNull();
+
+ List filteredMsgs = filtered(msgs, filterData);
+
+ SubscriptionData subscriptionData = new SubscriptionData();
+ subscriptionData.setExpressionType(filterData.getExpressionType());
+ subscriptionData.setTopic(filterData.getTopic());
+ subscriptionData.setClassFilterMode(false);
+ subscriptionData.setSubString(filterData.getExpression());
+
+ GetMessageResult getMessageResult = master.getMessage(group, realTopic, queueId, 0, 10000,
+ new ExpressionMessageFilter(subscriptionData, filterData, filterManager));
+ String assertMsg = group + "-" + realTopic;
+ try {
+ assertThat(getMessageResult).isNotNull();
+ assertThat(GetMessageStatus.FOUND).isEqualTo(getMessageResult.getStatus());
+ assertThat(getMessageResult.getMessageBufferList()).isNotNull().isNotEmpty();
+ assertThat(getMessageResult.getMessageBufferList().size()).isEqualTo(filteredMsgs.size());
+
+ for (ByteBuffer buffer : getMessageResult.getMessageBufferList()) {
+ MessageExt messageExt = MessageDecoder.decode(buffer.slice(), false);
+ assertThat(messageExt).isNotNull();
+
+ Object evlRet = null;
+ try {
+ evlRet = filterData.getCompiledExpression().evaluate(new MessageEvaluationContext(messageExt.getProperties()));
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(true).isFalse();
+ }
+
+ assertThat(evlRet).isNotNull().isEqualTo(Boolean.TRUE);
+
+ // check
+ boolean find = false;
+ for (MessageExtBrokerInner messageExtBrokerInner : filteredMsgs) {
+ if (messageExtBrokerInner.getMsgId().equals(messageExt.getMsgId())) {
+ find = true;
+ }
+ }
+ assertThat(find).isTrue();
+ }
+ } finally {
+ getMessageResult.release();
+ }
+ }
+ }
+ } finally {
+ master.shutdown();
+ master.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+}
diff --git a/broker/src/test/java/org/apache/rocketmq/broker/processor/PullMessageProcessorTest.java b/broker/src/test/java/org/apache/rocketmq/broker/processor/PullMessageProcessorTest.java
index d3d9812054..941d4a77fe 100644
--- a/broker/src/test/java/org/apache/rocketmq/broker/processor/PullMessageProcessorTest.java
+++ b/broker/src/test/java/org/apache/rocketmq/broker/processor/PullMessageProcessorTest.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Set;
import org.apache.rocketmq.broker.BrokerController;
import org.apache.rocketmq.broker.client.ClientChannelInfo;
+import org.apache.rocketmq.broker.filter.ExpressionMessageFilter;
import org.apache.rocketmq.broker.mqtrace.ConsumeMessageContext;
import org.apache.rocketmq.broker.mqtrace.ConsumeMessageHook;
import org.apache.rocketmq.common.BrokerConfig;
@@ -126,7 +127,7 @@ public class PullMessageProcessorTest {
@Test
public void testProcessRequest_Found() throws RemotingCommandException {
GetMessageResult getMessageResult = createGetMessageResult();
- when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(SubscriptionData.class))).thenReturn(getMessageResult);
+ when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(ExpressionMessageFilter.class))).thenReturn(getMessageResult);
final RemotingCommand request = createPullMsgCommand(RequestCode.PULL_MESSAGE);
RemotingCommand response = pullMessageProcessor.processRequest(handlerContext, request);
@@ -137,7 +138,7 @@ public class PullMessageProcessorTest {
@Test
public void testProcessRequest_FoundWithHook() throws RemotingCommandException {
GetMessageResult getMessageResult = createGetMessageResult();
- when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(SubscriptionData.class))).thenReturn(getMessageResult);
+ when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(ExpressionMessageFilter.class))).thenReturn(getMessageResult);
List consumeMessageHookList = new ArrayList<>();
final ConsumeMessageContext[] messageContext = new ConsumeMessageContext[1];
ConsumeMessageHook consumeMessageHook = new ConsumeMessageHook() {
@@ -168,7 +169,7 @@ public class PullMessageProcessorTest {
public void testProcessRequest_MsgWasRemoving() throws RemotingCommandException {
GetMessageResult getMessageResult = createGetMessageResult();
getMessageResult.setStatus(GetMessageStatus.MESSAGE_WAS_REMOVING);
- when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(SubscriptionData.class))).thenReturn(getMessageResult);
+ when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(ExpressionMessageFilter.class))).thenReturn(getMessageResult);
final RemotingCommand request = createPullMsgCommand(RequestCode.PULL_MESSAGE);
RemotingCommand response = pullMessageProcessor.processRequest(handlerContext, request);
@@ -180,7 +181,7 @@ public class PullMessageProcessorTest {
public void testProcessRequest_NoMsgInQueue() throws RemotingCommandException {
GetMessageResult getMessageResult = createGetMessageResult();
getMessageResult.setStatus(GetMessageStatus.NO_MESSAGE_IN_QUEUE);
- when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(SubscriptionData.class))).thenReturn(getMessageResult);
+ when(messageStore.getMessage(anyString(), anyString(), anyInt(), anyLong(), anyInt(), any(ExpressionMessageFilter.class))).thenReturn(getMessageResult);
final RemotingCommand request = createPullMsgCommand(RequestCode.PULL_MESSAGE);
RemotingCommand response = pullMessageProcessor.processRequest(handlerContext, request);
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
index 3903fe2b4f..9c9b59ef07 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java
@@ -518,6 +518,21 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
this.defaultMQPushConsumerImpl.subscribe(topic, fullClassName, filterClassSource);
}
+ /**
+ * Subscribe a topic by message selector.
+ *
+ * @see org.apache.rocketmq.client.consumer.MessageSelector#bySql
+ * @see org.apache.rocketmq.client.consumer.MessageSelector#byTag
+ *
+ * @param topic topic to consume.
+ * @param messageSelector {@link org.apache.rocketmq.client.consumer.MessageSelector}
+ * @throws MQClientException
+ */
+ @Override
+ public void subscribe(final String topic, final MessageSelector messageSelector) throws MQClientException {
+ this.defaultMQPushConsumerImpl.subscribe(topic, messageSelector);
+ }
+
/**
* Un-subscribe the specified topic from subscription.
* @param topic message topic
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/MQPushConsumer.java b/client/src/main/java/org/apache/rocketmq/client/consumer/MQPushConsumer.java
index 9255281523..9c6c1f1780 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/MQPushConsumer.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/MQPushConsumer.java
@@ -69,6 +69,27 @@ public interface MQPushConsumer extends MQConsumer {
*/
void subscribe(final String topic, final String fullClassName, final String filterClassSource) throws MQClientException;
+ /**
+ * Subscribe some topic with selector.
+ *
+ * This interface also has the ability of {@link #subscribe(String, String)},
+ * and, support other message selection, such as {@link org.apache.rocketmq.common.filter.ExpressionType#SQL92}.
+ *
+ *
+ * @param topic
+ * @param selector message selector({@link MessageSelector}), can be null.
+ * @throws MQClientException
+ */
+ void subscribe(final String topic, final MessageSelector selector) throws MQClientException;
+
/**
* Unsubscribe consumption some topic
*
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/MessageSelector.java b/client/src/main/java/org/apache/rocketmq/client/consumer/MessageSelector.java
new file mode 100644
index 0000000000..35a5181fd9
--- /dev/null
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/MessageSelector.java
@@ -0,0 +1,77 @@
+/*
+ * 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.consumer;
+
+import org.apache.rocketmq.common.filter.ExpressionType;
+
+/**
+ *
+ * Message selector: select message at server.
+ *
+ * Example:
+ * (a > 10 AND a < 100) OR (b IS NOT NULL AND b=TRUE)
+ *
+ */
+ public static final String SQL92 = "SQL92";
+
+ /**
+ * Only support or operation such as
+ * "tag1 || tag2 || tag3",
+ * If null or * expression,meaning subscribe all.
+ */
+ public static final String TAG = "TAG";
+
+ public static boolean isTagType(String type) {
+ if (type == null || TAG.equals(type)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java b/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java
index e9bf3fa290..fc8525c18e 100644
--- a/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java
+++ b/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java
@@ -63,4 +63,22 @@ public class FilterAPI {
return subscriptionData;
}
+
+ public static SubscriptionData build(final String topic, final String subString,
+ final String type) throws Exception {
+ if (ExpressionType.TAG.equals(type) || type == null) {
+ return buildSubscriptionData(null, topic, subString);
+ }
+
+ if (subString == null || subString.length() < 1) {
+ throw new IllegalArgumentException("Expression can't be null! " + type);
+ }
+
+ SubscriptionData subscriptionData = new SubscriptionData();
+ subscriptionData.setTopic(topic);
+ subscriptionData.setSubString(subString);
+ subscriptionData.setExpressionType(type);
+
+ return subscriptionData;
+ }
}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/Message.java b/common/src/main/java/org/apache/rocketmq/common/message/Message.java
index f3bff83292..2c81f5cd5e 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/Message.java
+++ b/common/src/main/java/org/apache/rocketmq/common/message/Message.java
@@ -81,6 +81,12 @@ public class Message implements Serializable {
throw new RuntimeException(String.format(
"The Property<%s> is used by system, input another please", name));
}
+ if (value == null || value == "" || value.trim() == ""
+ || name == null || name == "" || name.trim() == "") {
+ throw new IllegalArgumentException(
+ "The name or value of property can not be null or blank string!"
+ );
+ }
this.putProperty(name, value);
}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java
index 90b837a335..e41ec9d206 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java
@@ -41,6 +41,20 @@ public class MessageDecoder {
public final static int MESSAGE_MAGIC_CODE = 0xAABBCCDD ^ 1880681586 + 8;
public static final char NAME_VALUE_SEPARATOR = 1;
public static final char PROPERTY_SEPARATOR = 2;
+ public static final int BODY_SIZE_POSITION = 4 // 1 TOTALSIZE
+ + 4 // 2 MAGICCODE
+ + 4 // 3 BODYCRC
+ + 4 // 4 QUEUEID
+ + 4 // 5 FLAG
+ + 8 // 6 QUEUEOFFSET
+ + 8 // 7 PHYSICALOFFSET
+ + 4 // 8 SYSFLAG
+ + 8 // 9 BORNTIMESTAMP
+ + 8 // 10 BORNHOST
+ + 8 // 11 STORETIMESTAMP
+ + 8 // 12 STOREHOSTADDRESS
+ + 4 // 13 RECONSUMETIMES
+ + 8; // 14 Prepared Transaction Offset
public static String createMessageId(final ByteBuffer input, final ByteBuffer addr, final long offset) {
input.flip();
@@ -80,6 +94,31 @@ public class MessageDecoder {
return new MessageId(address, offset);
}
+ /**
+ * Just decode properties from msg buffer.
+ *
+ * @param byteBuffer msg commit log buffer.
+ * @return
+ */
+ public static Map decodeProperties(java.nio.ByteBuffer byteBuffer) {
+ int topicLengthPosition = BODY_SIZE_POSITION + 4 + byteBuffer.getInt(BODY_SIZE_POSITION);
+
+ byte topicLength = byteBuffer.get(topicLengthPosition);
+
+ short propertiesLength = byteBuffer.getShort(topicLengthPosition + 1 + topicLength);
+
+ byteBuffer.position(topicLengthPosition + 1 + topicLength + 2);
+
+ if (propertiesLength > 0) {
+ byte[] properties = new byte[propertiesLength];
+ byteBuffer.get(properties);
+ String propertiesString = new String(properties, CHARSET_UTF8);
+ Map map = string2messageProperties(propertiesString);
+ return map;
+ }
+ return null;
+ }
+
public static MessageExt decode(java.nio.ByteBuffer byteBuffer) {
return decode(byteBuffer, true, true, false);
}
diff --git a/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java b/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java
index 74fd9654c9..990e7486d7 100644
--- a/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java
+++ b/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java
@@ -88,7 +88,7 @@ public class TopAddressing {
if (verbose) {
String errorMsg =
- "connect to " + url + " failed, maybe the domain name " + MixAll.WS_DOMAIN_NAME + " not bind in /etc/hosts";
+ "connect to " + url + " failed, maybe the domain name " + MixAll.getWSAddr() + " not bind in /etc/hosts";
errorMsg += FAQUrl.suggestTodo(FAQUrl.NAME_SERVER_ADDR_NOT_EXIST_URL);
log.warn(errorMsg);
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java b/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
index c6b0925535..6f132f7cdf 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
@@ -68,6 +68,8 @@ public class RequestCode {
public static final int GET_ALL_DELAY_OFFSET = 45;
+ public static final int CHECK_CLIENT_CONFIG = 46;
+
public static final int PUT_KV_CONFIG = 100;
public static final int GET_KV_CONFIG = 101;
@@ -162,4 +164,6 @@ public class RequestCode {
public static final int SEND_BATCH_MESSAGE = 320;
+
+ public static final int QUERY_CONSUME_QUEUE = 321;
}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java b/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java
index 90b182b1e5..f62c4ea618 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java
@@ -53,6 +53,10 @@ public class ResponseCode extends RemotingSysResponseCode {
public static final int SUBSCRIPTION_GROUP_NOT_EXIST = 26;
+ public static final int FILTER_DATA_NOT_EXIST = 27;
+
+ public static final int FILTER_DATA_NOT_LATEST = 28;
+
public static final int TRANSACTION_SHOULD_COMMIT = 200;
public static final int TRANSACTION_SHOULD_ROLLBACK = 201;
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
new file mode 100644
index 0000000000..a78ce55456
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
@@ -0,0 +1,52 @@
+/*
+ * 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.protocol.body;
+
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+
+public class CheckClientRequestBody extends RemotingSerializable {
+
+ private String clientId;
+ private String group;
+ private SubscriptionData subscriptionData;
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public SubscriptionData getSubscriptionData() {
+ return subscriptionData;
+ }
+
+ public void setSubscriptionData(SubscriptionData subscriptionData) {
+ this.subscriptionData = subscriptionData;
+ }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumeQueueData.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumeQueueData.java
new file mode 100644
index 0000000000..7268dcda56
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumeQueueData.java
@@ -0,0 +1,98 @@
+/*
+ * 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.protocol.body;
+
+public class ConsumeQueueData {
+
+ private long physicOffset;
+ private int physicSize;
+ private long tagsCode;
+ private String extendDataJson;
+ private String bitMap;
+ private boolean eval;
+ private String msg;
+
+ public long getPhysicOffset() {
+ return physicOffset;
+ }
+
+ public void setPhysicOffset(long physicOffset) {
+ this.physicOffset = physicOffset;
+ }
+
+ public int getPhysicSize() {
+ return physicSize;
+ }
+
+ public void setPhysicSize(int physicSize) {
+ this.physicSize = physicSize;
+ }
+
+ public long getTagsCode() {
+ return tagsCode;
+ }
+
+ public void setTagsCode(long tagsCode) {
+ this.tagsCode = tagsCode;
+ }
+
+ public String getExtendDataJson() {
+ return extendDataJson;
+ }
+
+ public void setExtendDataJson(String extendDataJson) {
+ this.extendDataJson = extendDataJson;
+ }
+
+ public String getBitMap() {
+ return bitMap;
+ }
+
+ public void setBitMap(String bitMap) {
+ this.bitMap = bitMap;
+ }
+
+ public boolean isEval() {
+ return eval;
+ }
+
+ public void setEval(boolean eval) {
+ this.eval = eval;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ @Override
+ public String toString() {
+ return "ConsumeQueueData{" +
+ "physicOffset=" + physicOffset +
+ ", physicSize=" + physicSize +
+ ", tagsCode=" + tagsCode +
+ ", extendDataJson='" + extendDataJson + '\'' +
+ ", bitMap='" + bitMap + '\'' +
+ ", eval=" + eval +
+ ", msg='" + msg + '\'' +
+ '}';
+ }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/QueryConsumeQueueResponseBody.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/QueryConsumeQueueResponseBody.java
new file mode 100644
index 0000000000..be93da993d
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/QueryConsumeQueueResponseBody.java
@@ -0,0 +1,72 @@
+/*
+ * 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.protocol.body;
+
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+
+import java.util.List;
+
+public class QueryConsumeQueueResponseBody extends RemotingSerializable {
+
+ private SubscriptionData subscriptionData;
+ private String filterData;
+ private List queueData;
+ private long maxQueueIndex;
+ private long minQueueIndex;
+
+ public SubscriptionData getSubscriptionData() {
+ return subscriptionData;
+ }
+
+ public void setSubscriptionData(SubscriptionData subscriptionData) {
+ this.subscriptionData = subscriptionData;
+ }
+
+ public String getFilterData() {
+ return filterData;
+ }
+
+ public void setFilterData(String filterData) {
+ this.filterData = filterData;
+ }
+
+ public List getQueueData() {
+ return queueData;
+ }
+
+ public void setQueueData(List queueData) {
+ this.queueData = queueData;
+ }
+
+ public long getMaxQueueIndex() {
+ return maxQueueIndex;
+ }
+
+ public void setMaxQueueIndex(long maxQueueIndex) {
+ this.maxQueueIndex = maxQueueIndex;
+ }
+
+ public long getMinQueueIndex() {
+ return minQueueIndex;
+ }
+
+ public void setMinQueueIndex(long minQueueIndex) {
+ this.minQueueIndex = minQueueIndex;
+ }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageRequestHeader.java
index 8a592132e4..106e89e511 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageRequestHeader.java
@@ -46,6 +46,7 @@ public class PullMessageRequestHeader implements CommandCustomHeader {
private String subscription;
@CFNotNull
private Long subVersion;
+ private String expressionType;
@Override
public void checkFields() throws RemotingCommandException {
@@ -130,4 +131,12 @@ public class PullMessageRequestHeader implements CommandCustomHeader {
public void setSubVersion(Long subVersion) {
this.subVersion = subVersion;
}
+
+ public String getExpressionType() {
+ return expressionType;
+ }
+
+ public void setExpressionType(String expressionType) {
+ this.expressionType = expressionType;
+ }
}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryConsumeQueueRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryConsumeQueueRequestHeader.java
new file mode 100644
index 0000000000..642fe17cb6
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryConsumeQueueRequestHeader.java
@@ -0,0 +1,75 @@
+/*
+ * 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.protocol.header;
+
+import org.apache.rocketmq.remoting.CommandCustomHeader;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+
+public class QueryConsumeQueueRequestHeader implements CommandCustomHeader {
+
+ private String topic;
+ private int queueId;
+ private long index;
+ private int count;
+ private String consumerGroup;
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public void setTopic(String topic) {
+ this.topic = topic;
+ }
+
+ public int getQueueId() {
+ return queueId;
+ }
+
+ public void setQueueId(int queueId) {
+ this.queueId = queueId;
+ }
+
+ public long getIndex() {
+ return index;
+ }
+
+ public void setIndex(long index) {
+ this.index = index;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public String getConsumerGroup() {
+ return consumerGroup;
+ }
+
+ public void setConsumerGroup(String consumerGroup) {
+ this.consumerGroup = consumerGroup;
+ }
+
+ @Override
+ public void checkFields() throws RemotingCommandException {
+
+ }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/heartbeat/SubscriptionData.java b/common/src/main/java/org/apache/rocketmq/common/protocol/heartbeat/SubscriptionData.java
index 81f59544c2..e456b7e606 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/heartbeat/SubscriptionData.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/heartbeat/SubscriptionData.java
@@ -32,6 +32,7 @@ public class SubscriptionData implements Comparable {
private Set tagsSet = new HashSet();
private Set codeSet = new HashSet();
private long subVersion = System.currentTimeMillis();
+ private String expressionType;
@JSONField(serialize = false)
private String filterClassSource;
@@ -102,6 +103,14 @@ public class SubscriptionData implements Comparable {
this.classFilterMode = classFilterMode;
}
+ public String getExpressionType() {
+ return expressionType;
+ }
+
+ public void setExpressionType(String expressionType) {
+ this.expressionType = expressionType;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
@@ -111,6 +120,7 @@ public class SubscriptionData implements Comparable {
result = prime * result + ((subString == null) ? 0 : subString.hashCode());
result = prime * result + ((tagsSet == null) ? 0 : tagsSet.hashCode());
result = prime * result + ((topic == null) ? 0 : topic.hashCode());
+ result = prime * result + ((expressionType == null) ? 0 : expressionType.hashCode());
return result;
}
@@ -147,6 +157,11 @@ public class SubscriptionData implements Comparable {
return false;
} else if (!topic.equals(other.topic))
return false;
+ if (expressionType == null) {
+ if (other.expressionType != null)
+ return false;
+ } else if (!expressionType.equals(other.expressionType))
+ return false;
return true;
}
@@ -154,7 +169,7 @@ public class SubscriptionData implements Comparable {
public String toString() {
return "SubscriptionData [classFilterMode=" + classFilterMode + ", topic=" + topic + ", subString="
+ subString + ", tagsSet=" + tagsSet + ", codeSet=" + codeSet + ", subVersion=" + subVersion
- + "]";
+ + ", expressionType=" + expressionType + "]";
}
@Override
diff --git a/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java b/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java
index 5137f3282e..c5f8460eeb 100644
--- a/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java
@@ -42,4 +42,53 @@ public class FilterAPITest {
}
assertThat(subscriptionData.getTagsSet()).isEqualTo(tagSet);
}
+
+ @Test
+ public void testBuildTagSome() {
+ try {
+ SubscriptionData subscriptionData = FilterAPI.build(
+ "TOPIC", "A || B", ExpressionType.TAG
+ );
+
+ assertThat(subscriptionData).isNotNull();
+ assertThat(subscriptionData.getTopic()).isEqualTo("TOPIC");
+ assertThat(subscriptionData.getSubString()).isEqualTo("A || B");
+ assertThat(ExpressionType.isTagType(subscriptionData.getExpressionType())).isTrue();
+
+ assertThat(subscriptionData.getTagsSet()).isNotNull();
+ assertThat(subscriptionData.getTagsSet()).containsExactly("A", "B");
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+ }
+
+ @Test
+ public void testBuildSQL() {
+ try {
+ SubscriptionData subscriptionData = FilterAPI.build(
+ "TOPIC", "a is not null", ExpressionType.SQL92
+ );
+
+ assertThat(subscriptionData).isNotNull();
+ assertThat(subscriptionData.getTopic()).isEqualTo("TOPIC");
+ assertThat(subscriptionData.getExpressionType()).isEqualTo(ExpressionType.SQL92);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+ }
+
+ @Test
+ public void testBuildSQLWithNullSubString() {
+ try {
+ FilterAPI.build(
+ "TOPIC", null, ExpressionType.SQL92
+ );
+
+ assertThat(Boolean.FALSE).isTrue();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java b/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java
new file mode 100644
index 0000000000..d14d6b060c
--- /dev/null
+++ b/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.message;
+
+import org.junit.Test;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MessageDecoderTest {
+
+ @Test
+ public void testDecodeProperties() {
+ MessageExt messageExt = new MessageExt();
+
+ messageExt.setMsgId("645100FA00002A9F000000489A3AA09E");
+ messageExt.setTopic("abc");
+ messageExt.setBody("hello!q!".getBytes());
+ try {
+ messageExt.setBornHost(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0));
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+ messageExt.setBornTimestamp(System.currentTimeMillis());
+ messageExt.setCommitLogOffset(123456);
+ messageExt.setPreparedTransactionOffset(0);
+ messageExt.setQueueId(0);
+ messageExt.setQueueOffset(123);
+ messageExt.setReconsumeTimes(0);
+ try {
+ messageExt.setStoreHost(new InetSocketAddress(InetAddress.getLocalHost(), 0));
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ messageExt.putUserProperty("a", "123");
+ messageExt.putUserProperty("b", "hello");
+ messageExt.putUserProperty("c", "3.14");
+
+ byte[] msgBytes = new byte[0];
+ try {
+ msgBytes = MessageDecoder.encode(messageExt, false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(msgBytes.length);
+ byteBuffer.put(msgBytes);
+
+ Map properties = MessageDecoder.decodeProperties(byteBuffer);
+
+ assertThat(properties).isNotNull();
+ assertThat("123").isEqualTo(properties.get("a"));
+ assertThat("hello").isEqualTo(properties.get("b"));
+ assertThat("3.14").isEqualTo(properties.get("c"));
+ }
+}
diff --git a/distribution/conf/logback_broker.xml b/distribution/conf/logback_broker.xml
index 05c0ee4fb0..dd5c63f5de 100644
--- a/distribution/conf/logback_broker.xml
+++ b/distribution/conf/logback_broker.xml
@@ -222,6 +222,29 @@
+
+ ${user.home}/logs/rocketmqlogs/filter.log
+ true
+
+ ${user.home}/logs/rocketmqlogs/otherdays/filter.%i.log
+
+ 1
+ 10
+
+
+ 100MB
+
+
+ %d{yyy-MM-dd HH:mm:ss,GMT+8} %p %t - %m%n
+ UTF-8
+
+
+
+
+
+
${user.home}/logs/rocketmqlogs/stats.log
@@ -321,6 +344,11 @@
+
+
+
+
+
diff --git a/distribution/release.xml b/distribution/release.xml
index 2d3ec1ecc2..9e4ef2a017 100644
--- a/distribution/release.xml
+++ b/distribution/release.xml
@@ -67,6 +67,7 @@
org.apache.rocketmq:rocketmq-namesrvorg.apache.rocketmq:rocketmq-filtersrvorg.apache.rocketmq:rocketmq-example
+ org.apache.rocketmq:rocketmq-filterlib/
diff --git a/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java b/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java
index 473e4c7700..3e1b79b0cd 100644
--- a/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java
+++ b/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java
@@ -27,10 +27,13 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
+import org.apache.rocketmq.client.consumer.MessageSelector;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.filter.ExpressionType;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.srvutil.ServerUtil;
@@ -46,12 +49,14 @@ public class Consumer {
final String topic = commandLine.hasOption('t') ? commandLine.getOptionValue('t').trim() : "BenchmarkTest";
final String groupPrefix = commandLine.hasOption('g') ? commandLine.getOptionValue('g').trim() : "benchmark_consumer";
final String isPrefixEnable = commandLine.hasOption('p') ? commandLine.getOptionValue('p').trim() : "true";
+ final String filterType = commandLine.hasOption('f') ? commandLine.getOptionValue('f').trim() : null;
+ final String expression = commandLine.hasOption('e') ? commandLine.getOptionValue('e').trim() : null;
String group = groupPrefix;
if (Boolean.parseBoolean(isPrefixEnable)) {
group = groupPrefix + "_" + Long.toString(System.currentTimeMillis() % 100);
}
- System.out.printf("topic %s group %s prefix %s%n", topic, group, isPrefixEnable);
+ System.out.printf("topic: %s, group: %s, prefix: %s, filterType: %s, expression: %s%n", topic, group, isPrefixEnable, filterType, expression);
final StatsBenchmarkConsumer statsBenchmarkConsumer = new StatsBenchmarkConsumer();
@@ -99,7 +104,21 @@ public class Consumer {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group);
consumer.setInstanceName(Long.toString(System.currentTimeMillis()));
- consumer.subscribe(topic, "*");
+ if (filterType == null || expression == null) {
+ consumer.subscribe(topic, "*");
+ } else {
+ if (ExpressionType.TAG.equals(filterType)) {
+ String expr = MixAll.file2String(expression);
+ System.out.printf("Expression: %s%n", expr);
+ consumer.subscribe(topic, MessageSelector.byTag(expr));
+ } else if (ExpressionType.SQL92.equals(filterType)) {
+ String expr = MixAll.file2String(expression);
+ System.out.printf("Expression: %s%n", expr);
+ consumer.subscribe(topic, MessageSelector.bySql(expr));
+ } else {
+ throw new IllegalArgumentException("Not support filter type! " + filterType);
+ }
+ }
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
@@ -142,6 +161,14 @@ public class Consumer {
opt.setRequired(false);
options.addOption(opt);
+ opt = new Option("f", "filterType", true, "TAG, SQL92");
+ opt.setRequired(false);
+ options.addOption(opt);
+
+ opt = new Option("e", "expression", true, "filter expression content file path.ie: ./test/expr");
+ opt.setRequired(false);
+ options.addOption(opt);
+
return options;
}
diff --git a/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java b/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java
index 50d750df4d..2d8d0f6fa1 100644
--- a/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java
+++ b/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java
@@ -18,11 +18,13 @@ package org.apache.rocketmq.example.benchmark;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
+import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
+
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
@@ -50,13 +52,12 @@ public class Producer {
final int threadCount = commandLine.hasOption('w') ? Integer.parseInt(commandLine.getOptionValue('w')) : 64;
final int messageSize = commandLine.hasOption('s') ? Integer.parseInt(commandLine.getOptionValue('s')) : 128;
final boolean keyEnable = commandLine.hasOption('k') && Boolean.parseBoolean(commandLine.getOptionValue('k'));
+ final int propertySize = commandLine.hasOption('p') ? Integer.parseInt(commandLine.getOptionValue('p')) : 0;
System.out.printf("topic %s threadCount %d messageSize %d keyEnable %s%n", topic, threadCount, messageSize, keyEnable);
final Logger log = ClientLogger.getLog();
- final Message msg = buildMessage(messageSize, topic);
-
final ExecutorService sendThreadPool = Executors.newFixedThreadPool(threadCount);
final StatsBenchmarkProducer statsBenchmark = new StatsBenchmarkProducer();
@@ -117,10 +118,37 @@ public class Producer {
public void run() {
while (true) {
try {
+ final Message msg;
+ try {
+ msg = buildMessage(messageSize, topic);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ return;
+ }
final long beginTimestamp = System.currentTimeMillis();
if (keyEnable) {
msg.setKeys(String.valueOf(beginTimestamp / 1000));
}
+ if (propertySize > 0) {
+ if (msg.getProperties() != null) {
+ msg.getProperties().clear();
+ }
+ int i = 0;
+ int startValue = (new Random(System.currentTimeMillis())).nextInt(100);
+ int size = 0;
+ while (true) {
+ String prop1 = "prop" + i, prop1V = "hello" + startValue;
+ String prop2 = "prop" + (i + 1), prop2V = String.valueOf(startValue);
+ msg.putUserProperty(prop1, prop1V);
+ msg.putUserProperty(prop2, prop2V);
+ size += prop1.length() + prop2.length() + prop1V.length() + prop2V.length();
+ if (size > propertySize) {
+ break;
+ }
+ i += 2;
+ startValue += 2;
+ }
+ }
producer.send(msg);
statsBenchmark.getSendRequestSuccessCount().incrementAndGet();
statsBenchmark.getReceiveResponseSuccessCount().incrementAndGet();
@@ -214,7 +242,7 @@ class StatsBenchmarkProducer {
private final AtomicLong sendMessageMaxRT = new AtomicLong(0L);
public Long[] createSnapshot() {
- Long[] snap = new Long[] {
+ Long[] snap = new Long[]{
System.currentTimeMillis(),
this.sendRequestSuccessCount.get(),
this.sendRequestFailedCount.get(),
diff --git a/example/src/main/java/org/apache/rocketmq/example/filter/SqlConsumer.java b/example/src/main/java/org/apache/rocketmq/example/filter/SqlConsumer.java
new file mode 100644
index 0000000000..9a3b813158
--- /dev/null
+++ b/example/src/main/java/org/apache/rocketmq/example/filter/SqlConsumer.java
@@ -0,0 +1,62 @@
+/*
+ * 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.example.filter;
+
+import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
+import org.apache.rocketmq.client.consumer.MessageSelector;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
+import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.message.MessageExt;
+
+import java.util.List;
+
+public class SqlConsumer {
+
+ public static void main(String[] args) {
+ DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
+
+ try {
+ consumer.subscribe("TopicTest",
+ MessageSelector.bySql("(TAGS is not null and TAGS in ('TagA', 'TagB'))" +
+ "and (a is not null and a between 0 3)"));
+ } catch (MQClientException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ consumer.registerMessageListener(new MessageListenerConcurrently() {
+
+ @Override
+ public ConsumeConcurrentlyStatus consumeMessage(List msgs,
+ ConsumeConcurrentlyContext context) {
+ System.out.printf(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
+ return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
+ }
+ });
+
+ try {
+ consumer.start();
+ } catch (MQClientException e) {
+ e.printStackTrace();
+ return;
+ }
+ System.out.printf("Consumer Started.%n");
+ }
+}
diff --git a/example/src/main/java/org/apache/rocketmq/example/filter/SqlProducer.java b/example/src/main/java/org/apache/rocketmq/example/filter/SqlProducer.java
new file mode 100644
index 0000000000..3f3a0e6521
--- /dev/null
+++ b/example/src/main/java/org/apache/rocketmq/example/filter/SqlProducer.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.example.filter;
+
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.remoting.common.RemotingHelper;
+
+public class SqlProducer {
+
+ public static void main(String[] args) {
+ DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
+ try {
+ producer.start();
+ } catch (MQClientException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ for (int i = 0; i < 10; i++) {
+ try {
+ String tag;
+ int div = i % 3;
+ if (div == 0) {
+ tag = "TagA";
+ } else if (div == 1) {
+ tag = "TagB";
+ } else {
+ tag = "TagC";
+ }
+ Message msg = new Message("TopicTest",
+ tag,
+ ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
+ );
+ msg.putUserProperty("a", String.valueOf(i));
+
+ SendResult sendResult = producer.send(msg);
+ System.out.printf("%s%n", sendResult);
+ } catch (Exception e) {
+ e.printStackTrace();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ producer.shutdown();
+ }
+}
diff --git a/filter/pom.xml b/filter/pom.xml
new file mode 100644
index 0000000000..7978f0552e
--- /dev/null
+++ b/filter/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+ rocketmq-all
+ org.apache.rocketmq
+ 4.1.0-incubating-SNAPSHOT
+
+
+ 4.0.0
+ jar
+ rocketmq-filter
+ rocketmq-filter ${project.version}
+
+
+
+ ${project.groupId}
+ rocketmq-common
+
+
+ ${project.groupId}
+ rocketmq-srvutil
+
+
+
\ No newline at end of file
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/FilterFactory.java b/filter/src/main/java/org/apache/rocketmq/filter/FilterFactory.java
new file mode 100644
index 0000000000..a31854840c
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/FilterFactory.java
@@ -0,0 +1,72 @@
+/*
+ * 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.filter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Filter factory: support other filter to register.
+ */
+public class FilterFactory {
+
+ public static final FilterFactory INSTANCE = new FilterFactory();
+
+ protected static final Map FILTER_SPI_HOLDER = new HashMap(4);
+
+ static {
+ FilterFactory.INSTANCE.register(new SqlFilter());
+ }
+
+ /**
+ * Register a filter.
+ *
+ * Note:
+ *
1. Filter registered will be used in broker server, so take care of it's reliability and performance.
+ *
+ * @param filterSpi
+ */
+ public void register(FilterSpi filterSpi) {
+ if (FILTER_SPI_HOLDER.containsKey(filterSpi.ofType())) {
+ throw new IllegalArgumentException(String.format("Filter spi type(%s) already exist!", filterSpi.ofType()));
+ }
+
+ FILTER_SPI_HOLDER.put(filterSpi.ofType(), filterSpi);
+ }
+
+ /**
+ * Un register a filter.
+ *
+ * @param type
+ * @return
+ */
+ public FilterSpi unRegister(String type) {
+ return FILTER_SPI_HOLDER.remove(type);
+ }
+
+ /**
+ * Get a filter registered, null if none exist.
+ *
+ * @param type
+ * @return
+ */
+ public FilterSpi get(String type) {
+ return FILTER_SPI_HOLDER.get(type);
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/FilterSpi.java b/filter/src/main/java/org/apache/rocketmq/filter/FilterSpi.java
new file mode 100644
index 0000000000..fcc39fac60
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/FilterSpi.java
@@ -0,0 +1,43 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+
+/**
+ * Filter spi interface.
+ */
+public interface FilterSpi {
+
+ /**
+ * Compile.
+ *
+ * @param expr
+ * @return
+ * @throws org.apache.rocketmq.filter.expression.MQFilterException
+ */
+ Expression compile(final String expr) throws MQFilterException;
+
+ /**
+ * Which type.
+ *
+ * @return
+ */
+ String ofType();
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/SqlFilter.java b/filter/src/main/java/org/apache/rocketmq/filter/SqlFilter.java
new file mode 100644
index 0000000000..0c1ffb8db8
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/SqlFilter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.parser.SelectorParser;
+
+/**
+ * SQL92 Filter, just a wrapper of {@link org.apache.rocketmq.filter.parser.SelectorParser}.
+ *
+ *
+ * Do not use this filter directly.Use {@link FilterFactory#get} to select a filter.
+ *
+ */
+public class SqlFilter implements FilterSpi {
+
+ @Override
+ public Expression compile(final String expr) throws MQFilterException {
+ return SelectorParser.parse(expr);
+ }
+
+ @Override
+ public String ofType() {
+ return ExpressionType.SQL92;
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/constant/UnaryType.java b/filter/src/main/java/org/apache/rocketmq/filter/constant/UnaryType.java
new file mode 100644
index 0000000000..d2d04cdb7b
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/constant/UnaryType.java
@@ -0,0 +1,26 @@
+/*
+ * 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.filter.constant;
+
+public enum UnaryType {
+ NEGATE,
+ IN,
+ NOT,
+ BOOLEANCAST,
+ LIKE
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/BinaryExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/BinaryExpression.java
new file mode 100644
index 0000000000..0f172e305f
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/BinaryExpression.java
@@ -0,0 +1,91 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * An expression which performs an operation on two expression values.
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.BinaryExpression,
+ *
+ */
+public abstract class BinaryExpression implements Expression {
+ protected Expression left;
+ protected Expression right;
+
+ public BinaryExpression(Expression left, Expression right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public Expression getLeft() {
+ return left;
+ }
+
+ public Expression getRight() {
+ return right;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString() {
+ return "(" + left.toString() + " " + getExpressionSymbol() + " " + right.toString() + ")";
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object o) {
+
+ if (o == null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+ return toString().equals(o.toString());
+
+ }
+
+ /**
+ * Returns the symbol that represents this binary expression. For example, addition is
+ * represented by "+"
+ *
+ * @return
+ */
+ public abstract String getExpressionSymbol();
+
+ /**
+ * @param expression
+ */
+ public void setRight(Expression expression) {
+ right = expression;
+ }
+
+ /**
+ * @param expression
+ */
+ public void setLeft(Expression expression) {
+ left = expression;
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/BooleanExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/BooleanExpression.java
new file mode 100644
index 0000000000..bb54632e64
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/BooleanExpression.java
@@ -0,0 +1,39 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * A BooleanExpression is an expression that always
+ * produces a Boolean result.
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.BooleanExpression,
+ * but the parameter is changed to an interface.
+ *
+ *
+ * @see org.apache.rocketmq.filter.expression.EvaluationContext
+ */
+public interface BooleanExpression extends Expression {
+
+ /**
+ * @param context
+ * @return true if the expression evaluates to Boolean.TRUE.
+ * @throws Exception
+ */
+ boolean matches(EvaluationContext context) throws Exception;
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/ComparisonExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/ComparisonExpression.java
new file mode 100644
index 0000000000..8b82e57397
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/ComparisonExpression.java
@@ -0,0 +1,413 @@
+/*
+ * 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.filter.expression;
+
+import java.util.List;
+
+/**
+ * A filter performing a comparison of two objects
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.ComparisonExpression,
+ * but:
+ * 1. Remove LIKE expression, and related methods;
+ * 2. Extract a new method __compare which has int return value;
+ * 3. When create between expression, check whether left value is less or equal than right value;
+ * 4. For string type value(can not convert to number), only equal or unequal comparison are supported.
+ *
+ */
+public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression {
+
+ public static final ThreadLocal CONVERT_STRING_EXPRESSIONS = new ThreadLocal();
+
+ boolean convertStringExpressions = false;
+
+ /**
+ * @param left
+ * @param right
+ */
+ public ComparisonExpression(Expression left, Expression right) {
+ super(left, right);
+ convertStringExpressions = CONVERT_STRING_EXPRESSIONS.get() != null;
+ }
+
+ public static BooleanExpression createBetween(Expression value, Expression left, Expression right) {
+ // check
+ if (left instanceof ConstantExpression && right instanceof ConstantExpression) {
+ Object lv = ((ConstantExpression) left).getValue();
+ Object rv = ((ConstantExpression) right).getValue();
+ if (lv == null || rv == null) {
+ throw new RuntimeException("Illegal values of between, values can not be null!");
+ }
+ if (lv instanceof Comparable && rv instanceof Comparable) {
+ int ret = __compare((Comparable) rv, (Comparable) lv, true);
+ if (ret < 0)
+ throw new RuntimeException(
+ String.format("Illegal values of between, left value(%s) must less than or equal to right value(%s)", lv, rv)
+ );
+ }
+ }
+
+ return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
+ }
+
+ public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) {
+ return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static BooleanExpression createInFilter(Expression left, List elements) {
+
+ if (!(left instanceof PropertyExpression)) {
+ throw new RuntimeException("Expected a property for In expression, got: " + left);
+ }
+ return UnaryExpression.createInExpression((PropertyExpression) left, elements, false);
+
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static BooleanExpression createNotInFilter(Expression left, List elements) {
+
+ if (!(left instanceof PropertyExpression)) {
+ throw new RuntimeException("Expected a property for In expression, got: " + left);
+ }
+ return UnaryExpression.createInExpression((PropertyExpression) left, elements, true);
+
+ }
+
+ public static BooleanExpression createIsNull(Expression left) {
+ return doCreateEqual(left, ConstantExpression.NULL);
+ }
+
+ public static BooleanExpression createIsNotNull(Expression left) {
+ return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL));
+ }
+
+ public static BooleanExpression createNotEqual(Expression left, Expression right) {
+ return UnaryExpression.createNOT(createEqual(left, right));
+ }
+
+ public static BooleanExpression createEqual(Expression left, Expression right) {
+ checkEqualOperand(left);
+ checkEqualOperand(right);
+ checkEqualOperandCompatability(left, right);
+ return doCreateEqual(left, right);
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ private static BooleanExpression doCreateEqual(Expression left, Expression right) {
+ return new ComparisonExpression(left, right) {
+
+ public Object evaluate(EvaluationContext context) throws Exception {
+ Object lv = left.evaluate(context);
+ Object rv = right.evaluate(context);
+
+ // If one of the values is null
+ if (lv == null ^ rv == null) {
+ if (lv == null) {
+ return null;
+ }
+ return Boolean.FALSE;
+ }
+ if (lv == rv || lv.equals(rv)) {
+ return Boolean.TRUE;
+ }
+ if (lv instanceof Comparable && rv instanceof Comparable) {
+ return compare((Comparable) lv, (Comparable) rv);
+ }
+ return Boolean.FALSE;
+ }
+
+ protected boolean asBoolean(int answer) {
+ return answer == 0;
+ }
+
+ public String getExpressionSymbol() {
+ return "==";
+ }
+ };
+ }
+
+ public static BooleanExpression createGreaterThan(final Expression left, final Expression right) {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+ return new ComparisonExpression(left, right) {
+ protected boolean asBoolean(int answer) {
+ return answer > 0;
+ }
+
+ public String getExpressionSymbol() {
+ return ">";
+ }
+ };
+ }
+
+ public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+ return new ComparisonExpression(left, right) {
+ protected boolean asBoolean(int answer) {
+ return answer >= 0;
+ }
+
+ public String getExpressionSymbol() {
+ return ">=";
+ }
+ };
+ }
+
+ public static BooleanExpression createLessThan(final Expression left, final Expression right) {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+ return new ComparisonExpression(left, right) {
+
+ protected boolean asBoolean(int answer) {
+ return answer < 0;
+ }
+
+ public String getExpressionSymbol() {
+ return "<";
+ }
+
+ };
+ }
+
+ public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+ return new ComparisonExpression(left, right) {
+
+ protected boolean asBoolean(int answer) {
+ return answer <= 0;
+ }
+
+ public String getExpressionSymbol() {
+ return "<=";
+ }
+ };
+ }
+
+ /**
+ * Only Numeric expressions can be used in >, >=, < or <= expressions.s
+ *
+ * @param expr
+ */
+ public static void checkLessThanOperand(Expression expr) {
+ if (expr instanceof ConstantExpression) {
+ Object value = ((ConstantExpression) expr).getValue();
+ if (value instanceof Number) {
+ return;
+ }
+
+ // Else it's boolean or a String..
+ throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+ }
+ if (expr instanceof BooleanExpression) {
+ throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+ }
+ }
+
+ /**
+ * Validates that the expression can be used in == or <> expression. Cannot
+ * not be NULL TRUE or FALSE litterals.
+ *
+ * @param expr
+ */
+ public static void checkEqualOperand(Expression expr) {
+ if (expr instanceof ConstantExpression) {
+ Object value = ((ConstantExpression) expr).getValue();
+ if (value == null) {
+ throw new RuntimeException("'" + expr + "' cannot be compared.");
+ }
+ }
+ }
+
+ /**
+ * @param left
+ * @param right
+ */
+ private static void checkEqualOperandCompatability(Expression left, Expression right) {
+ if (left instanceof ConstantExpression && right instanceof ConstantExpression) {
+ if (left instanceof BooleanExpression && !(right instanceof BooleanExpression)) {
+ throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'");
+ }
+ }
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public Object evaluate(EvaluationContext context) throws Exception {
+ Comparable lv = (Comparable) left.evaluate(context);
+ if (lv == null) {
+ return null;
+ }
+ Comparable rv = (Comparable) right.evaluate(context);
+ if (rv == null) {
+ return null;
+ }
+ if (getExpressionSymbol().equals(">=") || getExpressionSymbol().equals(">")
+ || getExpressionSymbol().equals("<") || getExpressionSymbol().equals("<=")) {
+ Class extends Comparable> lc = lv.getClass();
+ Class extends Comparable> rc = rv.getClass();
+ if (lc == rc && lc == String.class) {
+ // Compare String is illegal
+ // first try to convert to double
+ try {
+ Comparable lvC = Double.valueOf((String) (Comparable) lv);
+ Comparable rvC = Double.valueOf((String) rv);
+
+ return compare(lvC, rvC);
+ } catch (Exception e) {
+ throw new RuntimeException("It's illegal to compare string by '>=', '>', '<', '<='. lv=" + lv + ", rv=" + rv, e);
+ }
+ }
+ }
+ return compare(lv, rv);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected static int __compare(Comparable lv, Comparable rv, boolean convertStringExpressions) {
+ Class extends Comparable> lc = lv.getClass();
+ Class extends Comparable> rc = rv.getClass();
+ // If the the objects are not of the same type,
+ // try to convert up to allow the comparison.
+ if (lc != rc) {
+ try {
+ if (lc == Boolean.class) {
+ if (convertStringExpressions && rc == String.class) {
+ lv = Boolean.valueOf((String) lv).booleanValue();
+ } else {
+ return -1;
+ }
+ } else if (lc == Byte.class) {
+ if (rc == Short.class) {
+ lv = Short.valueOf(((Number) lv).shortValue());
+ } else if (rc == Integer.class) {
+ lv = Integer.valueOf(((Number) lv).intValue());
+ } else if (rc == Long.class) {
+ lv = Long.valueOf(((Number) lv).longValue());
+ } else if (rc == Float.class) {
+ lv = new Float(((Number) lv).floatValue());
+ } else if (rc == Double.class) {
+ lv = new Double(((Number) lv).doubleValue());
+ } else if (convertStringExpressions && rc == String.class) {
+ rv = Byte.valueOf((String) rv);
+ } else {
+ return -1;
+ }
+ } else if (lc == Short.class) {
+ if (rc == Integer.class) {
+ lv = Integer.valueOf(((Number) lv).intValue());
+ } else if (rc == Long.class) {
+ lv = Long.valueOf(((Number) lv).longValue());
+ } else if (rc == Float.class) {
+ lv = new Float(((Number) lv).floatValue());
+ } else if (rc == Double.class) {
+ lv = new Double(((Number) lv).doubleValue());
+ } else if (convertStringExpressions && rc == String.class) {
+ rv = Short.valueOf((String) rv);
+ } else {
+ return -1;
+ }
+ } else if (lc == Integer.class) {
+ if (rc == Long.class) {
+ lv = Long.valueOf(((Number) lv).longValue());
+ } else if (rc == Float.class) {
+ lv = new Float(((Number) lv).floatValue());
+ } else if (rc == Double.class) {
+ lv = new Double(((Number) lv).doubleValue());
+ } else if (convertStringExpressions && rc == String.class) {
+ rv = Integer.valueOf((String) rv);
+ } else {
+ return -1;
+ }
+ } else if (lc == Long.class) {
+ if (rc == Integer.class) {
+ rv = Long.valueOf(((Number) rv).longValue());
+ } else if (rc == Float.class) {
+ lv = new Float(((Number) lv).floatValue());
+ } else if (rc == Double.class) {
+ lv = new Double(((Number) lv).doubleValue());
+ } else if (convertStringExpressions && rc == String.class) {
+ rv = Long.valueOf((String) rv);
+ } else {
+ return -1;
+ }
+ } else if (lc == Float.class) {
+ if (rc == Integer.class) {
+ rv = new Float(((Number) rv).floatValue());
+ } else if (rc == Long.class) {
+ rv = new Float(((Number) rv).floatValue());
+ } else if (rc == Double.class) {
+ lv = new Double(((Number) lv).doubleValue());
+ } else if (convertStringExpressions && rc == String.class) {
+ rv = Float.valueOf((String) rv);
+ } else {
+ return -1;
+ }
+ } else if (lc == Double.class) {
+ if (rc == Integer.class) {
+ rv = new Double(((Number) rv).doubleValue());
+ } else if (rc == Long.class) {
+ rv = new Double(((Number) rv).doubleValue());
+ } else if (rc == Float.class) {
+ rv = new Float(((Number) rv).doubleValue());
+ } else if (convertStringExpressions && rc == String.class) {
+ rv = Double.valueOf((String) rv);
+ } else {
+ return -1;
+ }
+ } else if (convertStringExpressions && lc == String.class) {
+ if (rc == Boolean.class) {
+ lv = Boolean.valueOf((String) lv);
+ } else if (rc == Byte.class) {
+ lv = Byte.valueOf((String) lv);
+ } else if (rc == Short.class) {
+ lv = Short.valueOf((String) lv);
+ } else if (rc == Integer.class) {
+ lv = Integer.valueOf((String) lv);
+ } else if (rc == Long.class) {
+ lv = Long.valueOf((String) lv);
+ } else if (rc == Float.class) {
+ lv = Float.valueOf((String) lv);
+ } else if (rc == Double.class) {
+ lv = Double.valueOf((String) lv);
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } catch (NumberFormatException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return lv.compareTo(rv);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected Boolean compare(Comparable lv, Comparable rv) {
+ return asBoolean(__compare(lv, rv, convertStringExpressions)) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ protected abstract boolean asBoolean(int answer);
+
+ public boolean matches(EvaluationContext context) throws Exception {
+ Object object = evaluate(context);
+ return object != null && object == Boolean.TRUE;
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/ConstantExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/ConstantExpression.java
new file mode 100644
index 0000000000..ca70f51a8e
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/ConstantExpression.java
@@ -0,0 +1,156 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * Represents a constant expression
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.ConstantExpression,
+ * but:
+ * 1. For long type constant, the range bound by java Long type;
+ * 2. For float type constant, the range bound by java Double type;
+ * 3. Remove Hex and Octal expression;
+ * 4. Add now expression to support to get current time.
+ *
+ */
+public class ConstantExpression implements Expression {
+
+ static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression {
+ public BooleanConstantExpression(Object value) {
+ super(value);
+ }
+
+ public boolean matches(EvaluationContext context) throws Exception {
+ Object object = evaluate(context);
+ return object != null && object == Boolean.TRUE;
+ }
+ }
+
+ public static final BooleanConstantExpression NULL = new BooleanConstantExpression(null);
+ public static final BooleanConstantExpression TRUE = new BooleanConstantExpression(Boolean.TRUE);
+ public static final BooleanConstantExpression FALSE = new BooleanConstantExpression(Boolean.FALSE);
+
+ private Object value;
+
+ public ConstantExpression(Object value) {
+ this.value = value;
+ }
+
+ public static ConstantExpression createFromDecimal(String text) {
+
+ // Strip off the 'l' or 'L' if needed.
+ if (text.endsWith("l") || text.endsWith("L")) {
+ text = text.substring(0, text.length() - 1);
+ }
+
+ // only support Long.MIN_VALUE ~ Long.MAX_VALUE
+ Number value = new Long(text);
+// try {
+// value = new Long(text);
+// } catch (NumberFormatException e) {
+// // The number may be too big to fit in a long.
+// value = new BigDecimal(text);
+// }
+
+ long l = value.longValue();
+ if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) {
+ value = Integer.valueOf(value.intValue());
+ }
+ return new ConstantExpression(value);
+ }
+
+ public static ConstantExpression createFloat(String text) {
+ Double value = new Double(text);
+ if (value > Double.MAX_VALUE) {
+ throw new RuntimeException(text + " is greater than " + Double.MAX_VALUE);
+ }
+ if (value < Double.MIN_VALUE) {
+ throw new RuntimeException(text + " is less than " + Double.MIN_VALUE);
+ }
+ return new ConstantExpression(value);
+ }
+
+ public static ConstantExpression createNow() {
+ return new NowExpression();
+ }
+
+ public Object evaluate(EvaluationContext context) throws Exception {
+ return value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString() {
+ Object value = getValue();
+ if (value == null) {
+ return "NULL";
+ }
+ if (value instanceof Boolean) {
+ return ((Boolean) value).booleanValue() ? "TRUE" : "FALSE";
+ }
+ if (value instanceof String) {
+ return encodeString((String) value);
+ }
+ return value.toString();
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object o) {
+
+ if (o == null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+ return toString().equals(o.toString());
+
+ }
+
+ /**
+ * Encodes the value of string so that it looks like it would look like when
+ * it was provided in a selector.
+ *
+ * @return
+ */
+ public static String encodeString(String s) {
+ StringBuffer b = new StringBuffer();
+ b.append('\'');
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '\'') {
+ b.append(c);
+ }
+ b.append(c);
+ }
+ b.append('\'');
+ return b.toString();
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/EmptyEvaluationContext.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/EmptyEvaluationContext.java
new file mode 100644
index 0000000000..52af2d0af2
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/EmptyEvaluationContext.java
@@ -0,0 +1,35 @@
+/*
+ * 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.filter.expression;
+
+import java.util.Map;
+
+/**
+ * Empty context.
+ */
+public class EmptyEvaluationContext implements EvaluationContext {
+ @Override
+ public Object get(String name) {
+ return null;
+ }
+
+ @Override
+ public Map keyValues() {
+ return null;
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/EvaluationContext.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/EvaluationContext.java
new file mode 100644
index 0000000000..094ef53ce3
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/EvaluationContext.java
@@ -0,0 +1,43 @@
+/*
+ * 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.filter.expression;
+
+import java.util.Map;
+
+/**
+ * Context of evaluate expression.
+ *
+ * Compare to org.apache.activemq.filter.MessageEvaluationContext, this is just an interface.
+ */
+public interface EvaluationContext {
+
+ /**
+ * Get value by name from context
+ *
+ * @param name
+ * @return
+ */
+ Object get(String name);
+
+ /**
+ * Context variables.
+ *
+ * @return
+ */
+ Map keyValues();
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java
new file mode 100644
index 0000000000..3e6d9b33e6
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java
@@ -0,0 +1,38 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * Interface of expression.
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.Expression,
+ * but the parameter is changed to an interface.
+ *
+ *
+ * @see org.apache.rocketmq.filter.expression.EvaluationContext
+ */
+public interface Expression {
+
+ /**
+ * Calculate express result with context.
+ *
+ * @param context context of evaluation
+ * @return the value of this expression
+ */
+ Object evaluate(EvaluationContext context) throws Exception;
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java
new file mode 100644
index 0000000000..1062bb8f51
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java
@@ -0,0 +1,94 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * A filter performing a comparison of two objects
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.LogicExpression,
+ *
+ */
+public abstract class LogicExpression extends BinaryExpression implements BooleanExpression {
+
+ /**
+ * @param left
+ * @param right
+ */
+ public LogicExpression(BooleanExpression left, BooleanExpression right) {
+ super(left, right);
+ }
+
+ public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) {
+ return new LogicExpression(lvalue, rvalue) {
+
+ public Object evaluate(EvaluationContext context) throws Exception {
+
+ Boolean lv = (Boolean) left.evaluate(context);
+ if (lv != null && lv.booleanValue()) {
+ return Boolean.TRUE;
+ }
+ Boolean rv = (Boolean) right.evaluate(context);
+ if (rv != null && rv.booleanValue()) {
+ return Boolean.TRUE;
+ }
+ if (lv == null || rv == null) {
+ return null;
+ }
+ return Boolean.FALSE;
+ }
+
+ public String getExpressionSymbol() {
+ return "||";
+ }
+ };
+ }
+
+ public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) {
+ return new LogicExpression(lvalue, rvalue) {
+
+ public Object evaluate(EvaluationContext context) throws Exception {
+
+ Boolean lv = (Boolean) left.evaluate(context);
+
+ if (lv != null && !lv.booleanValue()) {
+ return Boolean.FALSE;
+ }
+ Boolean rv = (Boolean) right.evaluate(context);
+ if (rv != null && !rv.booleanValue()) {
+ return Boolean.FALSE;
+ }
+ if (lv == null || rv == null) {
+ return null;
+ }
+ return Boolean.TRUE;
+ }
+
+ public String getExpressionSymbol() {
+ return "&&";
+ }
+ };
+ }
+
+ public abstract Object evaluate(EvaluationContext context) throws Exception;
+
+ public boolean matches(EvaluationContext context) throws Exception {
+ Object object = evaluate(context);
+ return object != null && object == Boolean.TRUE;
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java
new file mode 100644
index 0000000000..676a17ba49
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.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.filter.expression;
+
+/**
+ * Exception.
+ */
+public class MQFilterException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private final int responseCode;
+ private final String errorMessage;
+
+ public MQFilterException(String errorMessage, Throwable cause) {
+ super(cause);
+ this.responseCode = -1;
+ this.errorMessage = errorMessage;
+ }
+
+ public MQFilterException(int responseCode, String errorMessage) {
+ this.responseCode = responseCode;
+ this.errorMessage = errorMessage;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java
new file mode 100644
index 0000000000..d76caca9bd
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java
@@ -0,0 +1,36 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * Current time expression.Just for test.
+ */
+public class NowExpression extends ConstantExpression {
+ public NowExpression() {
+ super("now");
+ }
+
+ @Override
+ public Object evaluate(EvaluationContext context) throws Exception {
+ return new Long(System.currentTimeMillis());
+ }
+
+ public Object getValue() {
+ return new Long(System.currentTimeMillis());
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java
new file mode 100644
index 0000000000..b9657b0464
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java
@@ -0,0 +1,70 @@
+/*
+ * 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.filter.expression;
+
+/**
+ * Represents a property expression
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.PropertyExpression,
+ * but more simple and no transfer between expression and message property.
+ *
+ */
+public class PropertyExpression implements Expression {
+ private final String name;
+
+ public PropertyExpression(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Object evaluate(EvaluationContext context) throws Exception {
+ return context.get(name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object o) {
+
+ if (o == null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+ return name.equals(((PropertyExpression) o).name);
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java
new file mode 100644
index 0000000000..0519f4d56d
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java
@@ -0,0 +1,267 @@
+/*
+ * 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.filter.expression;
+
+import org.apache.rocketmq.filter.constant.UnaryType;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An expression which performs an operation on two expression values
+ *
+ * This class was taken from ActiveMQ org.apache.activemq.filter.UnaryExpression,
+ * but:
+ * 1. remove XPath and XQuery expression;
+ * 2. Add constant UnaryType to distinguish different unary expression;
+ * 3. Extract UnaryInExpression to an independent class.
+ *
+ */
+public abstract class UnaryExpression implements Expression {
+
+ private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE);
+ protected Expression right;
+
+ public UnaryType unaryType;
+
+ public UnaryExpression(Expression left) {
+ this.right = left;
+ }
+
+ public UnaryExpression(Expression left, UnaryType unaryType) {
+ this.setUnaryType(unaryType);
+ this.right = left;
+ }
+
+ public static Expression createNegate(Expression left) {
+ return new UnaryExpression(left, UnaryType.NEGATE) {
+ public Object evaluate(EvaluationContext context) throws Exception {
+ Object rvalue = right.evaluate(context);
+ if (rvalue == null) {
+ return null;
+ }
+ if (rvalue instanceof Number) {
+ return negate((Number) rvalue);
+ }
+ return null;
+ }
+
+ public String getExpressionSymbol() {
+ return "-";
+ }
+ };
+ }
+
+ public static BooleanExpression createInExpression(PropertyExpression right, List elements,
+ final boolean not) {
+
+ // Use a HashSet if there are many elements.
+ Collection t;
+ if (elements.size() == 0) {
+ t = null;
+ } else if (elements.size() < 5) {
+ t = elements;
+ } else {
+ t = new HashSet(elements);
+ }
+ final Collection inList = t;
+
+ return new UnaryInExpression(right, UnaryType.IN, inList, not) {
+ public Object evaluate(EvaluationContext context) throws Exception {
+
+ Object rvalue = right.evaluate(context);
+ if (rvalue == null) {
+ return null;
+ }
+ if (rvalue.getClass() != String.class) {
+ return null;
+ }
+
+ if ((inList != null && inList.contains(rvalue)) ^ not) {
+ return Boolean.TRUE;
+ } else {
+ return Boolean.FALSE;
+ }
+
+ }
+
+ public String toString() {
+ StringBuffer answer = new StringBuffer();
+ answer.append(right);
+ answer.append(" ");
+ answer.append(getExpressionSymbol());
+ answer.append(" ( ");
+
+ int count = 0;
+ for (Iterator i = inList.iterator(); i.hasNext(); ) {
+ Object o = (Object) i.next();
+ if (count != 0) {
+ answer.append(", ");
+ }
+ answer.append(o);
+ count++;
+ }
+
+ answer.append(" )");
+ return answer.toString();
+ }
+
+ public String getExpressionSymbol() {
+ if (not) {
+ return "NOT IN";
+ } else {
+ return "IN";
+ }
+ }
+ };
+ }
+
+ abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression {
+ public BooleanUnaryExpression(Expression left, UnaryType unaryType) {
+ super(left, unaryType);
+ }
+
+ public boolean matches(EvaluationContext context) throws Exception {
+ Object object = evaluate(context);
+ return object != null && object == Boolean.TRUE;
+ }
+ }
+
+ public static BooleanExpression createNOT(BooleanExpression left) {
+ return new BooleanUnaryExpression(left, UnaryType.NOT) {
+ public Object evaluate(EvaluationContext context) throws Exception {
+ Boolean lvalue = (Boolean) right.evaluate(context);
+ if (lvalue == null) {
+ return null;
+ }
+ return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
+ }
+
+ public String getExpressionSymbol() {
+ return "NOT";
+ }
+ };
+ }
+
+ public static BooleanExpression createBooleanCast(Expression left) {
+ return new BooleanUnaryExpression(left, UnaryType.BOOLEANCAST) {
+ public Object evaluate(EvaluationContext context) throws Exception {
+ Object rvalue = right.evaluate(context);
+ if (rvalue == null) {
+ return null;
+ }
+ if (!rvalue.getClass().equals(Boolean.class)) {
+ return Boolean.FALSE;
+ }
+ return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public String toString() {
+ return right.toString();
+ }
+
+ public String getExpressionSymbol() {
+ return "";
+ }
+ };
+ }
+
+ private static Number negate(Number left) {
+ Class clazz = left.getClass();
+ if (clazz == Integer.class) {
+ return new Integer(-left.intValue());
+ } else if (clazz == Long.class) {
+ return new Long(-left.longValue());
+ } else if (clazz == Float.class) {
+ return new Float(-left.floatValue());
+ } else if (clazz == Double.class) {
+ return new Double(-left.doubleValue());
+ } else if (clazz == BigDecimal.class) {
+ // We ussually get a big deciamal when we have Long.MIN_VALUE
+ // constant in the
+ // Selector. Long.MIN_VALUE is too big to store in a Long as a
+ // positive so we store it
+ // as a Big decimal. But it gets Negated right away.. to here we try
+ // to covert it back
+ // to a Long.
+ BigDecimal bd = (BigDecimal) left;
+ bd = bd.negate();
+
+ if (BD_LONG_MIN_VALUE.compareTo(bd) == 0) {
+ return Long.valueOf(Long.MIN_VALUE);
+ }
+ return bd;
+ } else {
+ throw new RuntimeException("Don't know how to negate: " + left);
+ }
+ }
+
+ public Expression getRight() {
+ return right;
+ }
+
+ public void setRight(Expression expression) {
+ right = expression;
+ }
+
+ public UnaryType getUnaryType() {
+ return unaryType;
+ }
+
+ public void setUnaryType(UnaryType unaryType) {
+ this.unaryType = unaryType;
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString() {
+ return "(" + getExpressionSymbol() + " " + right.toString() + ")";
+ }
+
+ /**
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ /**
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object o) {
+
+ if (o == null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+ return toString().equals(o.toString());
+
+ }
+
+ /**
+ * Returns the symbol that represents this binary expression. For example,
+ * addition is represented by "+"
+ *
+ * @return
+ */
+ public abstract String getExpressionSymbol();
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
new file mode 100644
index 0000000000..7d9083c09f
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
@@ -0,0 +1,61 @@
+/*
+ * 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.filter.expression;
+
+import org.apache.rocketmq.filter.constant.UnaryType;
+
+import java.util.Collection;
+
+/**
+ * In expression.
+ */
+abstract public class UnaryInExpression extends UnaryExpression implements BooleanExpression {
+
+ private boolean not;
+
+ private Collection inList;
+
+ public UnaryInExpression(Expression left, UnaryType unaryType,
+ Collection inList, boolean not) {
+ super(left, unaryType);
+ this.setInList(inList);
+ this.setNot(not);
+
+ }
+
+ public boolean matches(EvaluationContext context) throws Exception {
+ Object object = evaluate(context);
+ return object != null && object == Boolean.TRUE;
+ }
+
+ public boolean isNot() {
+ return not;
+ }
+
+ public void setNot(boolean not) {
+ this.not = not;
+ }
+
+ public Collection getInList() {
+ return inList;
+ }
+
+ public void setInList(Collection inList) {
+ this.inList = inList;
+ }
+};
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java
new file mode 100644
index 0000000000..2ccccaf554
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
+/* JavaCCOptions:KEEP_LINE_COL=null */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+ /**
+ * The version identifier for this Serializable class.
+ * Increment only if the serialized form of the
+ * class changes.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * This constructor is used by the method "generateParseException"
+ * in the generated parser. Calling this constructor generates
+ * a new object of this type with the fields "currentToken",
+ * "expectedTokenSequences", and "TOKEN_IMAGE" set.
+ */
+ public ParseException(Token currentTokenVal,
+ int[][] expectedTokenSequencesVal,
+ String[] tokenImageVal
+ ) {
+ super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever
+ * purpose you can think of. Constructing the exception in this
+ * manner makes the exception behave in the normal way - i.e., as
+ * documented in the class "Throwable". The fields "errorToken",
+ * "expectedTokenSequences", and "TOKEN_IMAGE" do not contain
+ * relevant information. The JavaCC generated code does not use
+ * these constructors.
+ */
+
+ public ParseException() {
+ super();
+ }
+
+ /**
+ * Constructor with message.
+ */
+ public ParseException(String message) {
+ super(message);
+ }
+
+ /**
+ * This is the last token that has been consumed successfully. If
+ * this object has been created due to a parse error, the token
+ * followng this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array
+ * of integers represents a sequence of tokens (by their ordinal
+ * values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "TOKEN_IMAGE" array of the generated
+ * parser within which the parse error occurred. This array is
+ * defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * It uses "currentToken" and "expectedTokenSequences" to generate a parse
+ * error message and returns it. If this object has been created
+ * due to a parse error, and you do not catch it (it gets thrown
+ * from the parser) the correct error message
+ * gets displayed.
+ */
+ private static String initialise(Token currentToken,
+ int[][] expectedTokenSequences,
+ String[] tokenImage) {
+ String eol = System.getProperty("line.separator", "\n");
+ StringBuffer expected = new StringBuffer();
+ int maxSize = 0;
+ for (int i = 0; i < expectedTokenSequences.length; i++) {
+ if (maxSize < expectedTokenSequences[i].length) {
+ maxSize = expectedTokenSequences[i].length;
+ }
+ for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+ }
+ if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+ expected.append("...");
+ }
+ expected.append(eol).append(" ");
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0)
+ retval += " ";
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += " " + tokenImage[tok.kind];
+ retval += " \"";
+ retval += add_escapes(tok.image);
+ retval += " \"";
+ tok = tok.next;
+ }
+ retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ retval += "." + eol;
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + eol + " ";
+ } else {
+ retval += "Was expecting one of:" + eol + " ";
+ }
+ retval += expected.toString();
+ return retval;
+ }
+
+ /**
+ * The end of line string for this machine.
+ */
+ protected String eol = System.getProperty("line.separator", "\n");
+
+ /**
+ * Used to convert raw characters to their escaped version
+ * when these raw version cannot be used as part of an ASCII
+ * string literal.
+ */
+ static String add_escapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i)) {
+ case 0:
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+}
+/* JavaCC - OriginalChecksum=4c829b0daa2c9af00ddafe2441eb9097 (do not edit this line) */
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java
new file mode 100644
index 0000000000..74e5501bee
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java
@@ -0,0 +1,1354 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SelectorParser.java */
+package org.apache.rocketmq.filter.parser;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.rocketmq.filter.expression.BooleanExpression;
+import org.apache.rocketmq.filter.expression.ComparisonExpression;
+import org.apache.rocketmq.filter.expression.ConstantExpression;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.LogicExpression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.expression.PropertyExpression;
+import org.apache.rocketmq.filter.expression.UnaryExpression;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj
+ */
+public class SelectorParser implements SelectorParserConstants {
+
+ private static final Cache PARSE_CACHE = CacheBuilder.newBuilder().maximumSize(100).build();
+ // private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:";
+
+ public static BooleanExpression parse(String sql) throws MQFilterException {
+ // sql = "("+sql+")";
+ Object result = PARSE_CACHE.getIfPresent(sql);
+ if (result instanceof MQFilterException) {
+ throw (MQFilterException) result;
+ } else if (result instanceof BooleanExpression) {
+ return (BooleanExpression) result;
+ } else {
+
+ // boolean convertStringExpressions = false;
+ // if( sql.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+ // convertStringExpressions = true;
+ // sql = sql.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+ // }
+ //
+ // if( convertStringExpressions ) {
+ // ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+ // }
+ ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+ try {
+
+ BooleanExpression e = new SelectorParser(sql).parse();
+ PARSE_CACHE.put(sql, e);
+ return e;
+ } catch (MQFilterException t) {
+ PARSE_CACHE.put(sql, t);
+ throw t;
+ } finally {
+ ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+ // if( convertStringExpressions ) {
+ // ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+ // }
+ }
+ }
+ }
+
+ public static void clearCache() {
+ PARSE_CACHE.cleanUp();
+ }
+
+ private String sql;
+
+ protected SelectorParser(String sql) {
+ this(new StringReader(sql));
+ this.sql = sql;
+ }
+
+ protected BooleanExpression parse() throws MQFilterException {
+ try {
+ return this.JmsSelector();
+ } catch (Throwable e) {
+ throw new MQFilterException("Invalid MessageSelector. ", e);
+ }
+ }
+
+ private BooleanExpression asBooleanExpression(Expression value) throws ParseException {
+ if (value instanceof BooleanExpression) {
+ return (BooleanExpression) value;
+ }
+ if (value instanceof PropertyExpression) {
+ return UnaryExpression.createBooleanCast(value);
+ }
+ throw new ParseException("Expression will not result in a boolean value: " + value);
+ }
+
+ // ----------------------------------------------------------------------------
+ // Grammer
+ // ----------------------------------------------------------------------------
+ final public BooleanExpression JmsSelector() throws ParseException {
+ Expression left = null;
+ left = orExpression();
+ {
+ if (true)
+ return asBooleanExpression(left);
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Expression orExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = andExpression();
+ label_1:
+ while (true) {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case OR:
+ break;
+ default:
+ jjLa1[0] = jjGen;
+ break label_1;
+ }
+ jj_consume_token(OR);
+ right = andExpression();
+ left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Expression andExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = equalityExpression();
+ label_2:
+ while (true) {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case AND:
+ break;
+ default:
+ jjLa1[1] = jjGen;
+ break label_2;
+ }
+ jj_consume_token(AND);
+ right = equalityExpression();
+ left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Expression equalityExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ left = comparisonExpression();
+ label_3:
+ while (true) {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case IS:
+ case 22:
+ case 23:
+ break;
+ default:
+ jjLa1[2] = jjGen;
+ break label_3;
+ }
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case 22:
+ jj_consume_token(22);
+ right = comparisonExpression();
+ left = ComparisonExpression.createEqual(left, right);
+ break;
+ case 23:
+ jj_consume_token(23);
+ right = comparisonExpression();
+ left = ComparisonExpression.createNotEqual(left, right);
+ break;
+ default:
+ jjLa1[3] = jjGen;
+ if (jj_2_1(2)) {
+ jj_consume_token(IS);
+ jj_consume_token(NULL);
+ left = ComparisonExpression.createIsNull(left);
+ } else {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case IS:
+ jj_consume_token(IS);
+ jj_consume_token(NOT);
+ jj_consume_token(NULL);
+ left = ComparisonExpression.createIsNotNull(left);
+ break;
+ default:
+ jjLa1[4] = jjGen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ }
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Expression comparisonExpression() throws ParseException {
+ Expression left;
+ Expression right;
+ Expression low;
+ Expression high;
+ String t, u;
+ boolean not;
+ ArrayList list;
+ left = unaryExpr();
+ label_4:
+ while (true) {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case NOT:
+ case BETWEEN:
+ case IN:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ break;
+ default:
+ jjLa1[5] = jjGen;
+ break label_4;
+ }
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case 24:
+ jj_consume_token(24);
+ right = unaryExpr();
+ left = ComparisonExpression.createGreaterThan(left, right);
+ break;
+ case 25:
+ jj_consume_token(25);
+ right = unaryExpr();
+ left = ComparisonExpression.createGreaterThanEqual(left, right);
+ break;
+ case 26:
+ jj_consume_token(26);
+ right = unaryExpr();
+ left = ComparisonExpression.createLessThan(left, right);
+ break;
+ case 27:
+ jj_consume_token(27);
+ right = unaryExpr();
+ left = ComparisonExpression.createLessThanEqual(left, right);
+ break;
+ case BETWEEN:
+ jj_consume_token(BETWEEN);
+ low = unaryExpr();
+ jj_consume_token(AND);
+ high = unaryExpr();
+ left = ComparisonExpression.createBetween(left, low, high);
+ break;
+ default:
+ jjLa1[8] = jjGen;
+ if (jj_2_2(2)) {
+ jj_consume_token(NOT);
+ jj_consume_token(BETWEEN);
+ low = unaryExpr();
+ jj_consume_token(AND);
+ high = unaryExpr();
+ left = ComparisonExpression.createNotBetween(left, low, high);
+ } else {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case IN:
+ jj_consume_token(IN);
+ jj_consume_token(28);
+ t = stringLitteral();
+ list = new ArrayList();
+ list.add(t);
+ label_5:
+ while (true) {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case 29:
+ break;
+ default:
+ jjLa1[6] = jjGen;
+ break label_5;
+ }
+ jj_consume_token(29);
+ t = stringLitteral();
+ list.add(t);
+ }
+ jj_consume_token(30);
+ left = ComparisonExpression.createInFilter(left, list);
+ break;
+ default:
+ jjLa1[9] = jjGen;
+ if (jj_2_3(2)) {
+ jj_consume_token(NOT);
+ jj_consume_token(IN);
+ jj_consume_token(28);
+ t = stringLitteral();
+ list = new ArrayList();
+ list.add(t);
+ label_6:
+ while (true) {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case 29:
+ break;
+ default:
+ jjLa1[7] = jjGen;
+ break label_6;
+ }
+ jj_consume_token(29);
+ t = stringLitteral();
+ list.add(t);
+ }
+ jj_consume_token(30);
+ left = ComparisonExpression.createNotInFilter(left, list);
+ } else {
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ }
+ }
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Expression unaryExpr() throws ParseException {
+ String s = null;
+ Expression left = null;
+ if (jj_2_4(2147483647)) {
+ jj_consume_token(31);
+ left = unaryExpr();
+ } else {
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case 32:
+ jj_consume_token(32);
+ left = unaryExpr();
+ left = UnaryExpression.createNegate(left);
+ break;
+ case NOT:
+ jj_consume_token(NOT);
+ left = unaryExpr();
+ left = UnaryExpression.createNOT(asBooleanExpression(left));
+ break;
+ case TRUE:
+ case FALSE:
+ case NULL:
+ case DECIMAL_LITERAL:
+ case FLOATING_POINT_LITERAL:
+ case STRING_LITERAL:
+ case ID:
+ case 28:
+ left = primaryExpr();
+ break;
+ default:
+ jjLa1[10] = jjGen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Expression primaryExpr() throws ParseException {
+ Expression left = null;
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case TRUE:
+ case FALSE:
+ case NULL:
+ case DECIMAL_LITERAL:
+ case FLOATING_POINT_LITERAL:
+ case STRING_LITERAL:
+ left = literal();
+ break;
+ case ID:
+ left = variable();
+ break;
+ case 28:
+ jj_consume_token(28);
+ left = orExpression();
+ jj_consume_token(30);
+ break;
+ default:
+ jjLa1[11] = jjGen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public ConstantExpression literal() throws ParseException {
+ Token t;
+ String s;
+ ConstantExpression left = null;
+ switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+ case STRING_LITERAL:
+ s = stringLitteral();
+ left = new ConstantExpression(s);
+ break;
+ case DECIMAL_LITERAL:
+ t = jj_consume_token(DECIMAL_LITERAL);
+ left = ConstantExpression.createFromDecimal(t.image);
+ break;
+ case FLOATING_POINT_LITERAL:
+ t = jj_consume_token(FLOATING_POINT_LITERAL);
+ left = ConstantExpression.createFloat(t.image);
+ break;
+ case TRUE:
+ jj_consume_token(TRUE);
+ left = ConstantExpression.TRUE;
+ break;
+ case FALSE:
+ jj_consume_token(FALSE);
+ left = ConstantExpression.FALSE;
+ break;
+ case NULL:
+ jj_consume_token(NULL);
+ left = ConstantExpression.NULL;
+ break;
+ default:
+ jjLa1[12] = jjGen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public String stringLitteral() throws ParseException {
+ Token t;
+ StringBuffer rc = new StringBuffer();
+ boolean first = true;
+ t = jj_consume_token(STRING_LITERAL);
+ // Decode the sting value.
+ String image = t.image;
+ for (int i = 1; i < image.length() - 1; i++) {
+ char c = image.charAt(i);
+ if (c == '\'')
+ i++;
+ rc.append(c);
+ }
+ {
+ if (true)
+ return rc.toString();
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public PropertyExpression variable() throws ParseException {
+ Token t;
+ PropertyExpression left = null;
+ t = jj_consume_token(ID);
+ left = new PropertyExpression(t.image);
+ {
+ if (true)
+ return left;
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ private boolean jj_2_1(int xla) {
+ jjLa = xla;
+ jjLastpos = jjScanpos = token;
+ try {
+ return !jj_3_1();
+ } catch (LookaheadSuccess ls) {
+ return true;
+ } finally {
+ jj_save(0, xla);
+ }
+ }
+
+ private boolean jj_2_2(int xla) {
+ jjLa = xla;
+ jjLastpos = jjScanpos = token;
+ try {
+ return !jj_3_2();
+ } catch (LookaheadSuccess ls) {
+ return true;
+ } finally {
+ jj_save(1, xla);
+ }
+ }
+
+ private boolean jj_2_3(int xla) {
+ jjLa = xla;
+ jjLastpos = jjScanpos = token;
+ try {
+ return !jj_3_3();
+ } catch (LookaheadSuccess ls) {
+ return true;
+ } finally {
+ jj_save(2, xla);
+ }
+ }
+
+ private boolean jj_2_4(int xla) {
+ jjLa = xla;
+ jjLastpos = jjScanpos = token;
+ try {
+ return !jj_3_4();
+ } catch (LookaheadSuccess ls) {
+ return true;
+ } finally {
+ jj_save(3, xla);
+ }
+ }
+
+ private boolean jj_3R_7() {
+ Token xsp;
+ xsp = jjScanpos;
+ if (jj_3R_8()) {
+ jjScanpos = xsp;
+ if (jj_3R_9()) {
+ jjScanpos = xsp;
+ if (jj_3R_10()) {
+ jjScanpos = xsp;
+ if (jj_3R_11())
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_43() {
+ if (jj_scan_token(29))
+ return true;
+ if (jj_3R_27())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_24() {
+ if (jj_scan_token(NULL))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_35() {
+ if (jj_scan_token(IS))
+ return true;
+ if (jj_scan_token(NOT))
+ return true;
+ if (jj_scan_token(NULL))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3_1() {
+ if (jj_scan_token(IS))
+ return true;
+ if (jj_scan_token(NULL))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_23() {
+ if (jj_scan_token(FALSE))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_34() {
+ if (jj_scan_token(23))
+ return true;
+ if (jj_3R_30())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_22() {
+ if (jj_scan_token(TRUE))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3_3() {
+ if (jj_scan_token(NOT))
+ return true;
+ if (jj_scan_token(IN))
+ return true;
+ if (jj_scan_token(28))
+ return true;
+ if (jj_3R_27())
+ return true;
+ Token xsp;
+ while (true) {
+ xsp = jjScanpos;
+ if (jj_3R_43()) {
+ jjScanpos = xsp;
+ break;
+ }
+ }
+ if (jj_scan_token(30))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_31() {
+ Token xsp;
+ xsp = jjScanpos;
+ if (jj_3R_33()) {
+ jjScanpos = xsp;
+ if (jj_3R_34()) {
+ jjScanpos = xsp;
+ if (jj_3_1()) {
+ jjScanpos = xsp;
+ if (jj_3R_35())
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_33() {
+ if (jj_scan_token(22))
+ return true;
+ if (jj_3R_30())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_42() {
+ if (jj_scan_token(29))
+ return true;
+ if (jj_3R_27())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_21() {
+ if (jj_scan_token(FLOATING_POINT_LITERAL))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_20() {
+ if (jj_scan_token(DECIMAL_LITERAL))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_28() {
+ if (jj_3R_30())
+ return true;
+ Token xsp;
+ while (true) {
+ xsp = jjScanpos;
+ if (jj_3R_31()) {
+ jjScanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_41() {
+ if (jj_scan_token(IN))
+ return true;
+ if (jj_scan_token(28))
+ return true;
+ if (jj_3R_27())
+ return true;
+ Token xsp;
+ while (true) {
+ xsp = jjScanpos;
+ if (jj_3R_42()) {
+ jjScanpos = xsp;
+ break;
+ }
+ }
+ if (jj_scan_token(30))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_19() {
+ if (jj_3R_27())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_29() {
+ if (jj_scan_token(AND))
+ return true;
+ if (jj_3R_28())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_16() {
+ Token xsp;
+ xsp = jjScanpos;
+ if (jj_3R_19()) {
+ jjScanpos = xsp;
+ if (jj_3R_20()) {
+ jjScanpos = xsp;
+ if (jj_3R_21()) {
+ jjScanpos = xsp;
+ if (jj_3R_22()) {
+ jjScanpos = xsp;
+ if (jj_3R_23()) {
+ jjScanpos = xsp;
+ if (jj_3R_24())
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3_2() {
+ if (jj_scan_token(NOT))
+ return true;
+ if (jj_scan_token(BETWEEN))
+ return true;
+ if (jj_3R_7())
+ return true;
+ if (jj_scan_token(AND))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_40() {
+ if (jj_scan_token(BETWEEN))
+ return true;
+ if (jj_3R_7())
+ return true;
+ if (jj_scan_token(AND))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_25() {
+ if (jj_3R_28())
+ return true;
+ Token xsp;
+ while (true) {
+ xsp = jjScanpos;
+ if (jj_3R_29()) {
+ jjScanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_39() {
+ if (jj_scan_token(27))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_15() {
+ if (jj_scan_token(28))
+ return true;
+ if (jj_3R_18())
+ return true;
+ if (jj_scan_token(30))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_14() {
+ if (jj_3R_17())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_38() {
+ if (jj_scan_token(26))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_13() {
+ if (jj_3R_16())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_26() {
+ if (jj_scan_token(OR))
+ return true;
+ if (jj_3R_25())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_17() {
+ if (jj_scan_token(ID))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_37() {
+ if (jj_scan_token(25))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_12() {
+ Token xsp;
+ xsp = jjScanpos;
+ if (jj_3R_13()) {
+ jjScanpos = xsp;
+ if (jj_3R_14()) {
+ jjScanpos = xsp;
+ if (jj_3R_15())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_32() {
+ Token xsp;
+ xsp = jjScanpos;
+ if (jj_3R_36()) {
+ jjScanpos = xsp;
+ if (jj_3R_37()) {
+ jjScanpos = xsp;
+ if (jj_3R_38()) {
+ jjScanpos = xsp;
+ if (jj_3R_39()) {
+ jjScanpos = xsp;
+ if (jj_3R_40()) {
+ jjScanpos = xsp;
+ if (jj_3_2()) {
+ jjScanpos = xsp;
+ if (jj_3R_41()) {
+ jjScanpos = xsp;
+ if (jj_3_3())
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_36() {
+ if (jj_scan_token(24))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_11() {
+ if (jj_3R_12())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_18() {
+ if (jj_3R_25())
+ return true;
+ Token xsp;
+ while (true) {
+ xsp = jjScanpos;
+ if (jj_3R_26()) {
+ jjScanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3_4() {
+ if (jj_scan_token(31))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_10() {
+ if (jj_scan_token(NOT))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_9() {
+ if (jj_scan_token(32))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_27() {
+ if (jj_scan_token(STRING_LITERAL))
+ return true;
+ return false;
+ }
+
+ private boolean jj_3R_30() {
+ if (jj_3R_7())
+ return true;
+ Token xsp;
+ while (true) {
+ xsp = jjScanpos;
+ if (jj_3R_32()) {
+ jjScanpos = xsp;
+ break;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_8() {
+ if (jj_scan_token(31))
+ return true;
+ if (jj_3R_7())
+ return true;
+ return false;
+ }
+
+ /**
+ * Generated Token Manager.
+ */
+ public SelectorParserTokenManager tokenSource;
+ SimpleCharStream jjInputStream;
+ /**
+ * Current token.
+ */
+ public Token token;
+ /**
+ * Next token.
+ */
+ public Token jjNt;
+ private int jjNtk;
+ private Token jjScanpos, jjLastpos;
+ private int jjLa;
+ private int jjGen;
+ final private int[] jjLa1 = new int[13];
+ static private int[] jjLa10;
+ static private int[] jjLa11;
+
+ static {
+ jj_la1_init_0();
+ jj_la1_init_1();
+ }
+
+ private static void jj_la1_init_0() {
+ jjLa10 = new int[]{0x400, 0x200, 0xc10000, 0xc00000, 0x10000, 0xf001900, 0x20000000, 0x20000000, 0xf000800,
+ 0x1000, 0x1036e100, 0x1036e000, 0x16e000};
+ }
+
+ private static void jj_la1_init_1() {
+ jjLa11 = new int[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0};
+ }
+
+ final private JJCalls[] jj2Rtns = new JJCalls[4];
+ private boolean jjRescan = false;
+ private int jjGc = 0;
+
+ /**
+ * Constructor with InputStream.
+ */
+ public SelectorParser(java.io.InputStream stream) {
+ this(stream, null);
+ }
+
+ /**
+ * Constructor with InputStream and supplied encoding
+ */
+ public SelectorParser(java.io.InputStream stream, String encoding) {
+ try {
+ jjInputStream = new SimpleCharStream(stream, encoding, 1, 1);
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ tokenSource = new SelectorParserTokenManager(jjInputStream);
+ token = new Token();
+ jjNtk = -1;
+ jjGen = 0;
+ for (int i = 0; i < 13; i++)
+ jjLa1[i] = -1;
+ for (int i = 0; i < jj2Rtns.length; i++)
+ jj2Rtns[i] = new JJCalls();
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream stream) {
+ ReInit(stream, null);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream stream, String encoding) {
+ try {
+ jjInputStream.ReInit(stream, encoding, 1, 1);
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ tokenSource.ReInit(jjInputStream);
+ token = new Token();
+ jjNtk = -1;
+ jjGen = 0;
+ for (int i = 0; i < 13; i++)
+ jjLa1[i] = -1;
+ for (int i = 0; i < jj2Rtns.length; i++)
+ jj2Rtns[i] = new JJCalls();
+ }
+
+ /**
+ * Constructor.
+ */
+ public SelectorParser(java.io.Reader stream) {
+ jjInputStream = new SimpleCharStream(stream, 1, 1);
+ tokenSource = new SelectorParserTokenManager(jjInputStream);
+ token = new Token();
+ jjNtk = -1;
+ jjGen = 0;
+ for (int i = 0; i < 13; i++)
+ jjLa1[i] = -1;
+ for (int i = 0; i < jj2Rtns.length; i++)
+ jj2Rtns[i] = new JJCalls();
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.Reader stream) {
+ jjInputStream.ReInit(stream, 1, 1);
+ tokenSource.ReInit(jjInputStream);
+ token = new Token();
+ jjNtk = -1;
+ jjGen = 0;
+ for (int i = 0; i < 13; i++)
+ jjLa1[i] = -1;
+ for (int i = 0; i < jj2Rtns.length; i++)
+ jj2Rtns[i] = new JJCalls();
+ }
+
+ /**
+ * Constructor with generated Token Manager.
+ */
+ public SelectorParser(SelectorParserTokenManager tm) {
+ tokenSource = tm;
+ token = new Token();
+ jjNtk = -1;
+ jjGen = 0;
+ for (int i = 0; i < 13; i++)
+ jjLa1[i] = -1;
+ for (int i = 0; i < jj2Rtns.length; i++)
+ jj2Rtns[i] = new JJCalls();
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(SelectorParserTokenManager tm) {
+ tokenSource = tm;
+ token = new Token();
+ jjNtk = -1;
+ jjGen = 0;
+ for (int i = 0; i < 13; i++)
+ jjLa1[i] = -1;
+ for (int i = 0; i < jj2Rtns.length; i++)
+ jj2Rtns[i] = new JJCalls();
+ }
+
+ private Token jj_consume_token(int kind) throws ParseException {
+ Token oldToken;
+ if ((oldToken = token).next != null)
+ token = token.next;
+ else
+ token = token.next = tokenSource.getNextToken();
+ jjNtk = -1;
+ if (token.kind == kind) {
+ jjGen++;
+ if (++jjGc > 100) {
+ jjGc = 0;
+ for (int i = 0; i < jj2Rtns.length; i++) {
+ JJCalls c = jj2Rtns[i];
+ while (c != null) {
+ if (c.gen < jjGen)
+ c.first = null;
+ c = c.next;
+ }
+ }
+ }
+ return token;
+ }
+ token = oldToken;
+ jjKind = kind;
+ throw generateParseException();
+ }
+
+ static private final class LookaheadSuccess extends java.lang.Error {
+ }
+
+ final private LookaheadSuccess jjLs = new LookaheadSuccess();
+
+ private boolean jj_scan_token(int kind) {
+ if (jjScanpos == jjLastpos) {
+ jjLa--;
+ if (jjScanpos.next == null) {
+ jjLastpos = jjScanpos = jjScanpos.next = tokenSource.getNextToken();
+ } else {
+ jjLastpos = jjScanpos = jjScanpos.next;
+ }
+ } else {
+ jjScanpos = jjScanpos.next;
+ }
+ if (jjRescan) {
+ int i = 0;
+ Token tok = token;
+ while (tok != null && tok != jjScanpos) {
+ i++;
+ tok = tok.next;
+ }
+ if (tok != null)
+ jj_add_error_token(kind, i);
+ }
+ if (jjScanpos.kind != kind)
+ return true;
+ if (jjLa == 0 && jjScanpos == jjLastpos)
+ throw jjLs;
+ return false;
+ }
+
+ /**
+ * Get the next Token.
+ */
+ final public Token getNextToken() {
+ if (token.next != null)
+ token = token.next;
+ else
+ token = token.next = tokenSource.getNextToken();
+ jjNtk = -1;
+ jjGen++;
+ return token;
+ }
+
+ /**
+ * Get the specific Token.
+ */
+ final public Token getToken(int index) {
+ Token t = token;
+ for (int i = 0; i < index; i++) {
+ if (t.next != null)
+ t = t.next;
+ else
+ t = t.next = tokenSource.getNextToken();
+ }
+ return t;
+ }
+
+ private int jj_ntk() {
+ if ((jjNt = token.next) == null)
+ return jjNtk = (token.next = tokenSource.getNextToken()).kind;
+ else
+ return jjNtk = jjNt.kind;
+ }
+
+ private java.util.List jjExpentries = new java.util.ArrayList();
+ private int[] jjExpentry;
+ private int jjKind = -1;
+ private int[] jjLasttokens = new int[100];
+ private int jjEndpos;
+
+ private void jj_add_error_token(int kind, int pos) {
+ if (pos >= 100)
+ return;
+ if (pos == jjEndpos + 1) {
+ jjLasttokens[jjEndpos++] = kind;
+ } else if (jjEndpos != 0) {
+ jjExpentry = new int[jjEndpos];
+ for (int i = 0; i < jjEndpos; i++) {
+ jjExpentry[i] = jjLasttokens[i];
+ }
+ jj_entries_loop:
+ for (java.util.Iterator> it = jjExpentries.iterator(); it.hasNext(); ) {
+ int[] oldentry = (int[]) (it.next());
+ if (oldentry.length == jjExpentry.length) {
+ for (int i = 0; i < jjExpentry.length; i++) {
+ if (oldentry[i] != jjExpentry[i]) {
+ continue jj_entries_loop;
+ }
+ }
+ jjExpentries.add(jjExpentry);
+ break jj_entries_loop;
+ }
+ }
+ if (pos != 0)
+ jjLasttokens[(jjEndpos = pos) - 1] = kind;
+ }
+ }
+
+ /**
+ * Generate ParseException.
+ */
+ public ParseException generateParseException() {
+ jjExpentries.clear();
+ boolean[] la1tokens = new boolean[33];
+ if (jjKind >= 0) {
+ la1tokens[jjKind] = true;
+ jjKind = -1;
+ }
+ for (int i = 0; i < 13; i++) {
+ if (jjLa1[i] == jjGen) {
+ for (int j = 0; j < 32; j++) {
+ if ((jjLa10[i] & (1 << j)) != 0) {
+ la1tokens[j] = true;
+ }
+ if ((jjLa11[i] & (1 << j)) != 0) {
+ la1tokens[32 + j] = true;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < 33; i++) {
+ if (la1tokens[i]) {
+ jjExpentry = new int[1];
+ jjExpentry[0] = i;
+ jjExpentries.add(jjExpentry);
+ }
+ }
+ jjEndpos = 0;
+ jj_rescan_token();
+ jj_add_error_token(0, 0);
+ int[][] exptokseq = new int[jjExpentries.size()][];
+ for (int i = 0; i < jjExpentries.size(); i++) {
+ exptokseq[i] = jjExpentries.get(i);
+ }
+ return new ParseException(token, exptokseq, TOKEN_IMAGE);
+ }
+
+ /**
+ * Enable tracing.
+ */
+ final public void enable_tracing() {
+ }
+
+ /**
+ * Disable tracing.
+ */
+ final public void disable_tracing() {
+ }
+
+ private void jj_rescan_token() {
+ jjRescan = true;
+ for (int i = 0; i < 4; i++) {
+ try {
+ JJCalls p = jj2Rtns[i];
+ do {
+ if (p.gen > jjGen) {
+ jjLa = p.arg;
+ jjLastpos = jjScanpos = p.first;
+ switch (i) {
+ case 0:
+ jj_3_1();
+ break;
+ case 1:
+ jj_3_2();
+ break;
+ case 2:
+ jj_3_3();
+ break;
+ case 3:
+ jj_3_4();
+ break;
+ }
+ }
+ p = p.next;
+ } while (p != null);
+ } catch (LookaheadSuccess ls) {
+ }
+ }
+ jjRescan = false;
+ }
+
+ private void jj_save(int index, int xla) {
+ JJCalls p = jj2Rtns[index];
+ while (p.gen > jjGen) {
+ if (p.next == null) {
+ p = p.next = new JJCalls();
+ break;
+ }
+ p = p.next;
+ }
+ p.gen = jjGen + xla - jjLa;
+ p.first = token;
+ p.arg = xla;
+ }
+
+ static final class JJCalls {
+ int gen;
+ Token first;
+ int arg;
+ JJCalls next;
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj
new file mode 100644
index 0000000000..5d1a4a7d8f
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file was taken from ActiveMQ activemq-client/src/main/grammar/SelectorParser.jj.
+ *
+ * There are some modifications:
+ * 1. Convert string expressions default;
+ * 2. HEX_LITERAL and OCTAL_LITERAL were removed;
+ * 3. LIKE, ESCAPE, XPATH and XQUERY were removed;
+ * 4. Computation expressions were removed;
+ */
+
+// ----------------------------------------------------------------------------
+// OPTIONS
+// ----------------------------------------------------------------------------
+options {
+ STATIC = false;
+ UNICODE_INPUT = true;
+
+ //ERROR_REPORTING = false;
+}
+
+// ----------------------------------------------------------------------------
+// PARSER
+// ----------------------------------------------------------------------------
+
+PARSER_BEGIN(SelectorParser)
+
+package org.apache.rocketmq.filter.parser;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.rocketmq.filter.expression.BooleanExpression;
+import org.apache.rocketmq.filter.expression.ComparisonExpression;
+import org.apache.rocketmq.filter.expression.ConstantExpression;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.LogicExpression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.expression.PropertyExpression;
+import org.apache.rocketmq.filter.expression.UnaryExpression;
+import org.apache.rocketmq.filter.util.LRUCache;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj
+ */
+public class SelectorParser {
+
+ private static final Cache PARSE_CACHE = CacheBuilder.newBuilder().maximumSize(100).build();
+// private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:";
+
+ public static BooleanExpression parse(String sql) throws MQFilterException {
+// sql = "("+sql+")";
+ Object result = PARSE_CACHE.getIfPresent(sql);
+ if (result instanceof MQFilterException) {
+ throw (MQFilterException) result;
+ } else if (result instanceof BooleanExpression) {
+ return (BooleanExpression) result;
+ } else {
+
+// boolean convertStringExpressions = false;
+// if( sql.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+// convertStringExpressions = true;
+// sql = sql.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+// }
+//
+// if( convertStringExpressions ) {
+// ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+// }
+ ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+ try {
+
+ BooleanExpression e = new SelectorParser(sql).parse();
+ PARSE_CACHE.put(sql, e);
+ return e;
+ } catch (MQFilterException t) {
+ PARSE_CACHE.put(sql, t);
+ throw t;
+ } finally {
+ ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+// if( convertStringExpressions ) {
+// ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+// }
+ }
+ }
+ }
+
+ public static void clearCache() {
+ PARSE_CACHE.cleanUp();
+ }
+
+ private String sql;
+
+ protected SelectorParser(String sql) {
+ this(new StringReader(sql));
+ this.sql = sql;
+ }
+
+ protected BooleanExpression parse() throws MQFilterException {
+ try {
+ return this.JmsSelector();
+ }
+ catch (Throwable e) {
+ throw new MQFilterException("Invalid MessageSelector. ", e);
+ }
+ }
+
+ private BooleanExpression asBooleanExpression(Expression value) throws ParseException {
+ if (value instanceof BooleanExpression) {
+ return (BooleanExpression) value;
+ }
+ if (value instanceof PropertyExpression) {
+ return UnaryExpression.createBooleanCast( value );
+ }
+ throw new ParseException("Expression will not result in a boolean value: " + value);
+ }
+
+}
+
+PARSER_END(SelectorParser)
+
+// ----------------------------------------------------------------------------
+// Tokens
+// ----------------------------------------------------------------------------
+
+/* White Space */
+SPECIAL_TOKEN :
+{
+ " " | "\t" | "\n" | "\r" | "\f"
+}
+
+/* Comments */
+SKIP:
+{
+
+}
+
+SKIP:
+{
+
+}
+
+/* Reserved Words */
+TOKEN [IGNORE_CASE] :
+{
+ < NOT : "NOT">
+ | < AND : "AND">
+ | < OR : "OR">
+ | < BETWEEN : "BETWEEN">
+ | < IN : "IN">
+ | < TRUE : "TRUE" >
+ | < FALSE : "FALSE" >
+ | < NULL : "NULL" >
+ | < IS : "IS" >
+}
+
+/* Literals */
+TOKEN [IGNORE_CASE] :
+
+{
+
+ < DECIMAL_LITERAL: "0" | ["1"-"9"] (["0"-"9"])* (["l","L"])? >
+ | < FLOATING_POINT_LITERAL:
+ (["0"-"9"])+ "." (["0"-"9"])* ()? // matches: 5.5 or 5. or 5.5E10 or 5.E10
+ | "." (["0"-"9"])+ ()? // matches: .5 or .5E10
+ | (["0"-"9"])+ // matches: 5E10
+ >
+ | < #EXPONENT: "E" (["+","-"])? (["0"-"9"])+ >
+ | < STRING_LITERAL: "'" ( ("''") | ~["'"] )* "'" >
+}
+
+TOKEN [IGNORE_CASE] :
+{
+ < ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* >
+}
+
+// ----------------------------------------------------------------------------
+// Grammer
+// ----------------------------------------------------------------------------
+BooleanExpression JmsSelector() :
+{
+ Expression left=null;
+}
+{
+ (
+ left = orExpression()
+ )
+ {
+ return asBooleanExpression(left);
+ }
+
+}
+
+Expression orExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ (
+ left = andExpression()
+ (
+ right = andExpression()
+ {
+ left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ )*
+ )
+ {
+ return left;
+ }
+
+}
+
+
+Expression andExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ (
+ left = equalityExpression()
+ (
+ right = equalityExpression()
+ {
+ left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right));
+ }
+ )*
+ )
+ {
+ return left;
+ }
+}
+
+Expression equalityExpression() :
+{
+ Expression left;
+ Expression right;
+}
+{
+ (
+ left = comparisonExpression()
+ (
+
+ "=" right = comparisonExpression()
+ {
+ left = ComparisonExpression.createEqual(left, right);
+ }
+ |
+ "<>" right = comparisonExpression()
+ {
+ left = ComparisonExpression.createNotEqual(left, right);
+ }
+ |
+ LOOKAHEAD(2)
+
+ {
+ left = ComparisonExpression.createIsNull(left);
+ }
+ |
+
+ {
+ left = ComparisonExpression.createIsNotNull(left);
+ }
+ )*
+ )
+ {
+ return left;
+ }
+}
+
+Expression comparisonExpression() :
+{
+ Expression left;
+ Expression right;
+ Expression low;
+ Expression high;
+ String t, u;
+ boolean not;
+ ArrayList list;
+}
+{
+ (
+ left = unaryExpr()
+ (
+
+ ">" right = unaryExpr()
+ {
+ left = ComparisonExpression.createGreaterThan(left, right);
+ }
+ |
+ ">=" right = unaryExpr()
+ {
+ left = ComparisonExpression.createGreaterThanEqual(left, right);
+ }
+ |
+ "<" right = unaryExpr()
+ {
+ left = ComparisonExpression.createLessThan(left, right);
+ }
+ |
+ "<=" right = unaryExpr()
+ {
+ left = ComparisonExpression.createLessThanEqual(left, right);
+ }
+ |
+ low = unaryExpr() high = unaryExpr()
+ {
+ left = ComparisonExpression.createBetween(left, low, high);
+ }
+ |
+ LOOKAHEAD(2)
+ low = unaryExpr() high = unaryExpr()
+ {
+ left = ComparisonExpression.createNotBetween(left, low, high);
+ }
+ |
+
+ "("
+ t = stringLitteral()
+ {
+ list = new ArrayList();
+ list.add( t );
+ }
+ (
+ ","
+ t = stringLitteral()
+ {
+ list.add( t );
+ }
+
+ )*
+ ")"
+ {
+ left = ComparisonExpression.createInFilter(left, list);
+ }
+ |
+ LOOKAHEAD(2)
+
+ "("
+ t = stringLitteral()
+ {
+ list = new ArrayList();
+ list.add( t );
+ }
+ (
+ ","
+ t = stringLitteral()
+ {
+ list.add( t );
+ }
+
+ )*
+ ")"
+ {
+ left = ComparisonExpression.createNotInFilter(left, list);
+ }
+
+ )*
+ )
+ {
+ return left;
+ }
+}
+
+Expression unaryExpr() :
+{
+ String s=null;
+ Expression left=null;
+}
+{
+ (
+ LOOKAHEAD( "+" unaryExpr() )
+ "+" left=unaryExpr()
+ |
+ "-" left=unaryExpr()
+ {
+ left = UnaryExpression.createNegate(left);
+ }
+ |
+ left=unaryExpr()
+ {
+ left = UnaryExpression.createNOT( asBooleanExpression(left) );
+ }
+ |
+ left = primaryExpr()
+ )
+ {
+ return left;
+ }
+
+}
+
+Expression primaryExpr() :
+{
+ Expression left=null;
+}
+{
+ (
+ left = literal()
+ |
+ left = variable()
+ |
+ "(" left = orExpression() ")"
+ )
+ {
+ return left;
+ }
+}
+
+
+
+ConstantExpression literal() :
+{
+ Token t;
+ String s;
+ ConstantExpression left=null;
+}
+{
+ (
+ (
+ s = stringLitteral()
+ {
+ left = new ConstantExpression(s);
+ }
+ )
+ |
+ (
+ t =
+ {
+ left = ConstantExpression.createFromDecimal(t.image);
+ }
+ )
+ |
+ (
+ t =
+ {
+ left = ConstantExpression.createFloat(t.image);
+ }
+ )
+ |
+ (
+
+ {
+ left = ConstantExpression.TRUE;
+ }
+ )
+ |
+ (
+
+ {
+ left = ConstantExpression.FALSE;
+ }
+ )
+ |
+ (
+
+ {
+ left = ConstantExpression.NULL;
+ }
+ )
+ )
+ {
+ return left;
+ }
+}
+
+String stringLitteral() :
+{
+ Token t;
+ StringBuffer rc = new StringBuffer();
+ boolean first=true;
+}
+{
+ t =
+ {
+ // Decode the sting value.
+ String image = t.image;
+ for( int i=1; i < image.length()-1; i++ ) {
+ char c = image.charAt(i);
+ if( c == '\'' )
+ i++;
+ rc.append(c);
+ }
+ return rc.toString();
+ }
+}
+
+PropertyExpression variable() :
+{
+ Token t;
+ PropertyExpression left=null;
+}
+{
+ (
+ t =
+ {
+ left = new PropertyExpression(t.image);
+ }
+ )
+ {
+ return left;
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserConstants.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserConstants.java
new file mode 100644
index 0000000000..915658ca60
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserConstants.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SelectorParserConstants.java */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * Token literal values and constants.
+ * Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+public interface SelectorParserConstants {
+
+ /**
+ * End of File.
+ */
+ int EOF = 0;
+ /**
+ * RegularExpression Id.
+ */
+ int LINE_COMMENT = 6;
+ /**
+ * RegularExpression Id.
+ */
+ int BLOCK_COMMENT = 7;
+ /**
+ * RegularExpression Id.
+ */
+ int NOT = 8;
+ /**
+ * RegularExpression Id.
+ */
+ int AND = 9;
+ /**
+ * RegularExpression Id.
+ */
+ int OR = 10;
+ /**
+ * RegularExpression Id.
+ */
+ int BETWEEN = 11;
+ /**
+ * RegularExpression Id.
+ */
+ int IN = 12;
+ /**
+ * RegularExpression Id.
+ */
+ int TRUE = 13;
+ /**
+ * RegularExpression Id.
+ */
+ int FALSE = 14;
+ /**
+ * RegularExpression Id.
+ */
+ int NULL = 15;
+ /**
+ * RegularExpression Id.
+ */
+ int IS = 16;
+ /**
+ * RegularExpression Id.
+ */
+ int DECIMAL_LITERAL = 17;
+ /**
+ * RegularExpression Id.
+ */
+ int FLOATING_POINT_LITERAL = 18;
+ /**
+ * RegularExpression Id.
+ */
+ int EXPONENT = 19;
+ /**
+ * RegularExpression Id.
+ */
+ int STRING_LITERAL = 20;
+ /**
+ * RegularExpression Id.
+ */
+ int ID = 21;
+
+ /**
+ * Lexical state.
+ */
+ int DEFAULT = 0;
+
+ /**
+ * Literal token values.
+ */
+ String[] TOKEN_IMAGE = {
+ "",
+ "\" \"",
+ "\"\\t\"",
+ "\"\\n\"",
+ "\"\\r\"",
+ "\"\\f\"",
+ "",
+ "",
+ "\"NOT\"",
+ "\"AND\"",
+ "\"OR\"",
+ "\"BETWEEN\"",
+ "\"IN\"",
+ "\"TRUE\"",
+ "\"FALSE\"",
+ "\"NULL\"",
+ "\"IS\"",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "\"=\"",
+ "\"<>\"",
+ "\">\"",
+ "\">=\"",
+ "\"<\"",
+ "\"<=\"",
+ "\"(\"",
+ "\",\"",
+ "\")\"",
+ "\"+\"",
+ "\"-\"",
+ };
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserTokenManager.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserTokenManager.java
new file mode 100644
index 0000000000..354f5ba21b
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParserTokenManager.java
@@ -0,0 +1,919 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SelectorParserTokenManager.java */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * Token Manager.
+ */
+public class SelectorParserTokenManager implements SelectorParserConstants {
+
+ /**
+ * Debug output.
+ */
+ public java.io.PrintStream debugStream = System.out;
+
+ /**
+ * Set debug output.
+ */
+ public void setDebugStream(java.io.PrintStream ds) {
+ debugStream = ds;
+ }
+
+ private int jjStopAtPos(int pos, int kind) {
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+ }
+
+ private int jjMoveStringLiteralDfa0_0() {
+ switch (curChar) {
+ case 9:
+ jjmatchedKind = 2;
+ return jjMoveNfa_0(5, 0);
+ case 10:
+ jjmatchedKind = 3;
+ return jjMoveNfa_0(5, 0);
+ case 12:
+ jjmatchedKind = 5;
+ return jjMoveNfa_0(5, 0);
+ case 13:
+ jjmatchedKind = 4;
+ return jjMoveNfa_0(5, 0);
+ case 32:
+ jjmatchedKind = 1;
+ return jjMoveNfa_0(5, 0);
+ case 40:
+ jjmatchedKind = 28;
+ return jjMoveNfa_0(5, 0);
+ case 41:
+ jjmatchedKind = 30;
+ return jjMoveNfa_0(5, 0);
+ case 43:
+ jjmatchedKind = 31;
+ return jjMoveNfa_0(5, 0);
+ case 44:
+ jjmatchedKind = 29;
+ return jjMoveNfa_0(5, 0);
+ case 45:
+ jjmatchedKind = 32;
+ return jjMoveNfa_0(5, 0);
+ case 60:
+ jjmatchedKind = 26;
+ return jjMoveStringLiteralDfa1_0(0x8800000L);
+ case 61:
+ jjmatchedKind = 22;
+ return jjMoveNfa_0(5, 0);
+ case 62:
+ jjmatchedKind = 24;
+ return jjMoveStringLiteralDfa1_0(0x2000000L);
+ case 65:
+ return jjMoveStringLiteralDfa1_0(0x200L);
+ case 66:
+ return jjMoveStringLiteralDfa1_0(0x800L);
+ case 70:
+ return jjMoveStringLiteralDfa1_0(0x4000L);
+ case 73:
+ return jjMoveStringLiteralDfa1_0(0x11000L);
+ case 78:
+ return jjMoveStringLiteralDfa1_0(0x8100L);
+ case 79:
+ return jjMoveStringLiteralDfa1_0(0x400L);
+ case 84:
+ return jjMoveStringLiteralDfa1_0(0x2000L);
+ case 97:
+ return jjMoveStringLiteralDfa1_0(0x200L);
+ case 98:
+ return jjMoveStringLiteralDfa1_0(0x800L);
+ case 102:
+ return jjMoveStringLiteralDfa1_0(0x4000L);
+ case 105:
+ return jjMoveStringLiteralDfa1_0(0x11000L);
+ case 110:
+ return jjMoveStringLiteralDfa1_0(0x8100L);
+ case 111:
+ return jjMoveStringLiteralDfa1_0(0x400L);
+ case 116:
+ return jjMoveStringLiteralDfa1_0(0x2000L);
+ default:
+ return jjMoveNfa_0(5, 0);
+ }
+ }
+
+ private int jjMoveStringLiteralDfa1_0(long active0) {
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ return jjMoveNfa_0(5, 0);
+ }
+ switch (curChar) {
+ case 61:
+ if ((active0 & 0x2000000L) != 0L) {
+ jjmatchedKind = 25;
+ jjmatchedPos = 1;
+ } else if ((active0 & 0x8000000L) != 0L) {
+ jjmatchedKind = 27;
+ jjmatchedPos = 1;
+ }
+ break;
+ case 62:
+ if ((active0 & 0x800000L) != 0L) {
+ jjmatchedKind = 23;
+ jjmatchedPos = 1;
+ }
+ break;
+ case 65:
+ return jjMoveStringLiteralDfa2_0(active0, 0x4000L);
+ case 69:
+ return jjMoveStringLiteralDfa2_0(active0, 0x800L);
+ case 78:
+ if ((active0 & 0x1000L) != 0L) {
+ jjmatchedKind = 12;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x200L);
+ case 79:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+ case 82:
+ if ((active0 & 0x400L) != 0L) {
+ jjmatchedKind = 10;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x2000L);
+ case 83:
+ if ((active0 & 0x10000L) != 0L) {
+ jjmatchedKind = 16;
+ jjmatchedPos = 1;
+ }
+ break;
+ case 85:
+ return jjMoveStringLiteralDfa2_0(active0, 0x8000L);
+ case 97:
+ return jjMoveStringLiteralDfa2_0(active0, 0x4000L);
+ case 101:
+ return jjMoveStringLiteralDfa2_0(active0, 0x800L);
+ case 110:
+ if ((active0 & 0x1000L) != 0L) {
+ jjmatchedKind = 12;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x200L);
+ case 111:
+ return jjMoveStringLiteralDfa2_0(active0, 0x100L);
+ case 114:
+ if ((active0 & 0x400L) != 0L) {
+ jjmatchedKind = 10;
+ jjmatchedPos = 1;
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x2000L);
+ case 115:
+ if ((active0 & 0x10000L) != 0L) {
+ jjmatchedKind = 16;
+ jjmatchedPos = 1;
+ }
+ break;
+ case 117:
+ return jjMoveStringLiteralDfa2_0(active0, 0x8000L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 1);
+ }
+
+ private int jjMoveStringLiteralDfa2_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L)
+ return jjMoveNfa_0(5, 1);
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ return jjMoveNfa_0(5, 1);
+ }
+ switch (curChar) {
+ case 68:
+ if ((active0 & 0x200L) != 0L) {
+ jjmatchedKind = 9;
+ jjmatchedPos = 2;
+ }
+ break;
+ case 76:
+ return jjMoveStringLiteralDfa3_0(active0, 0xc000L);
+ case 84:
+ if ((active0 & 0x100L) != 0L) {
+ jjmatchedKind = 8;
+ jjmatchedPos = 2;
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0x800L);
+ case 85:
+ return jjMoveStringLiteralDfa3_0(active0, 0x2000L);
+ case 100:
+ if ((active0 & 0x200L) != 0L) {
+ jjmatchedKind = 9;
+ jjmatchedPos = 2;
+ }
+ break;
+ case 108:
+ return jjMoveStringLiteralDfa3_0(active0, 0xc000L);
+ case 116:
+ if ((active0 & 0x100L) != 0L) {
+ jjmatchedKind = 8;
+ jjmatchedPos = 2;
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0x800L);
+ case 117:
+ return jjMoveStringLiteralDfa3_0(active0, 0x2000L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 2);
+ }
+
+ private int jjMoveStringLiteralDfa3_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L)
+ return jjMoveNfa_0(5, 2);
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ return jjMoveNfa_0(5, 2);
+ }
+ switch (curChar) {
+ case 69:
+ if ((active0 & 0x2000L) != 0L) {
+ jjmatchedKind = 13;
+ jjmatchedPos = 3;
+ }
+ break;
+ case 76:
+ if ((active0 & 0x8000L) != 0L) {
+ jjmatchedKind = 15;
+ jjmatchedPos = 3;
+ }
+ break;
+ case 83:
+ return jjMoveStringLiteralDfa4_0(active0, 0x4000L);
+ case 87:
+ return jjMoveStringLiteralDfa4_0(active0, 0x800L);
+ case 101:
+ if ((active0 & 0x2000L) != 0L) {
+ jjmatchedKind = 13;
+ jjmatchedPos = 3;
+ }
+ break;
+ case 108:
+ if ((active0 & 0x8000L) != 0L) {
+ jjmatchedKind = 15;
+ jjmatchedPos = 3;
+ }
+ break;
+ case 115:
+ return jjMoveStringLiteralDfa4_0(active0, 0x4000L);
+ case 119:
+ return jjMoveStringLiteralDfa4_0(active0, 0x800L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 3);
+ }
+
+ private int jjMoveStringLiteralDfa4_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L)
+ return jjMoveNfa_0(5, 3);
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ return jjMoveNfa_0(5, 3);
+ }
+ switch (curChar) {
+ case 69:
+ if ((active0 & 0x4000L) != 0L) {
+ jjmatchedKind = 14;
+ jjmatchedPos = 4;
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0x800L);
+ case 101:
+ if ((active0 & 0x4000L) != 0L) {
+ jjmatchedKind = 14;
+ jjmatchedPos = 4;
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0x800L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 4);
+ }
+
+ private int jjMoveStringLiteralDfa5_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L)
+ return jjMoveNfa_0(5, 4);
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ return jjMoveNfa_0(5, 4);
+ }
+ switch (curChar) {
+ case 69:
+ return jjMoveStringLiteralDfa6_0(active0, 0x800L);
+ case 101:
+ return jjMoveStringLiteralDfa6_0(active0, 0x800L);
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 5);
+ }
+
+ private int jjMoveStringLiteralDfa6_0(long old0, long active0) {
+ if (((active0 &= old0)) == 0L)
+ return jjMoveNfa_0(5, 5);
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ return jjMoveNfa_0(5, 5);
+ }
+ switch (curChar) {
+ case 78:
+ if ((active0 & 0x800L) != 0L) {
+ jjmatchedKind = 11;
+ jjmatchedPos = 6;
+ }
+ break;
+ case 110:
+ if ((active0 & 0x800L) != 0L) {
+ jjmatchedKind = 11;
+ jjmatchedPos = 6;
+ }
+ break;
+ default:
+ break;
+ }
+ return jjMoveNfa_0(5, 6);
+ }
+
+ static final long[] JJ_BIT_VEC_0 = {
+ 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+ };
+ static final long[] JJ_BIT_VEC_2 = {
+ 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+ };
+
+ private int jjMoveNfa_0(int startState, int curPos) {
+ int strKind = jjmatchedKind;
+ int strPos = jjmatchedPos;
+ int seenUpto;
+ inputStream.backup(seenUpto = curPos + 1);
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ throw new Error("Internal Error");
+ }
+ curPos = 0;
+ int startsAt = 0;
+ jjnewStateCnt = 40;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int kind = 0x7fffffff;
+ for (;;) {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64) {
+ long l = 1L << curChar;
+ do {
+ switch (jjstateSet[--i]) {
+ case 5:
+ if ((0x3ff000000000000L & l) != 0L)
+ jjCheckNAddStates(0, 3);
+ else if (curChar == 36) {
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(28);
+ } else if (curChar == 39)
+ jjCheckNAddStates(4, 6);
+ else if (curChar == 46)
+ jjCheckNAdd(18);
+ else if (curChar == 47)
+ jjstateSet[jjnewStateCnt++] = 6;
+ else if (curChar == 45)
+ jjstateSet[jjnewStateCnt++] = 0;
+ if ((0x3fe000000000000L & l) != 0L) {
+ if (kind > 17)
+ kind = 17;
+ jjCheckNAddTwoStates(15, 16);
+ } else if (curChar == 48) {
+ if (kind > 17)
+ kind = 17;
+ }
+ break;
+ case 0:
+ if (curChar == 45)
+ jjCheckNAddStates(7, 9);
+ break;
+ case 1:
+ if ((0xffffffffffffdbffL & l) != 0L)
+ jjCheckNAddStates(7, 9);
+ break;
+ case 2:
+ if ((0x2400L & l) != 0L && kind > 6)
+ kind = 6;
+ break;
+ case 3:
+ if (curChar == 10 && kind > 6)
+ kind = 6;
+ break;
+ case 4:
+ if (curChar == 13)
+ jjstateSet[jjnewStateCnt++] = 3;
+ break;
+ case 6:
+ if (curChar == 42)
+ jjCheckNAddTwoStates(7, 8);
+ break;
+ case 7:
+ if ((0xfffffbffffffffffL & l) != 0L)
+ jjCheckNAddTwoStates(7, 8);
+ break;
+ case 8:
+ if (curChar == 42)
+ jjCheckNAddStates(10, 12);
+ break;
+ case 9:
+ if ((0xffff7bffffffffffL & l) != 0L)
+ jjCheckNAddTwoStates(10, 8);
+ break;
+ case 10:
+ if ((0xfffffbffffffffffL & l) != 0L)
+ jjCheckNAddTwoStates(10, 8);
+ break;
+ case 11:
+ if (curChar == 47 && kind > 7)
+ kind = 7;
+ break;
+ case 12:
+ if (curChar == 47)
+ jjstateSet[jjnewStateCnt++] = 6;
+ break;
+ case 13:
+ if (curChar == 48 && kind > 17)
+ kind = 17;
+ break;
+ case 14:
+ if ((0x3fe000000000000L & l) == 0L)
+ break;
+ if (kind > 17)
+ kind = 17;
+ jjCheckNAddTwoStates(15, 16);
+ break;
+ case 15:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 17)
+ kind = 17;
+ jjCheckNAddTwoStates(15, 16);
+ break;
+ case 17:
+ if (curChar == 46)
+ jjCheckNAdd(18);
+ break;
+ case 18:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAddTwoStates(18, 19);
+ break;
+ case 20:
+ if ((0x280000000000L & l) != 0L)
+ jjCheckNAdd(21);
+ break;
+ case 21:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(21);
+ break;
+ case 22:
+ case 23:
+ if (curChar == 39)
+ jjCheckNAddStates(4, 6);
+ break;
+ case 24:
+ if (curChar == 39)
+ jjstateSet[jjnewStateCnt++] = 23;
+ break;
+ case 25:
+ if ((0xffffff7fffffffffL & l) != 0L)
+ jjCheckNAddStates(4, 6);
+ break;
+ case 26:
+ if (curChar == 39 && kind > 20)
+ kind = 20;
+ break;
+ case 27:
+ if (curChar != 36)
+ break;
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(28);
+ break;
+ case 28:
+ if ((0x3ff001000000000L & l) == 0L)
+ break;
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(28);
+ break;
+ case 29:
+ if ((0x3ff000000000000L & l) != 0L)
+ jjCheckNAddStates(0, 3);
+ break;
+ case 30:
+ if ((0x3ff000000000000L & l) != 0L)
+ jjCheckNAddTwoStates(30, 31);
+ break;
+ case 31:
+ if (curChar != 46)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAddTwoStates(32, 33);
+ break;
+ case 32:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAddTwoStates(32, 33);
+ break;
+ case 34:
+ if ((0x280000000000L & l) != 0L)
+ jjCheckNAdd(35);
+ break;
+ case 35:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(35);
+ break;
+ case 36:
+ if ((0x3ff000000000000L & l) != 0L)
+ jjCheckNAddTwoStates(36, 37);
+ break;
+ case 38:
+ if ((0x280000000000L & l) != 0L)
+ jjCheckNAdd(39);
+ break;
+ case 39:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 18)
+ kind = 18;
+ jjCheckNAdd(39);
+ break;
+ default:
+ break;
+ }
+ } while (i != startsAt);
+ } else if (curChar < 128) {
+ long l = 1L << (curChar & 077);
+ do {
+ switch (jjstateSet[--i]) {
+ case 5:
+ case 28:
+ if ((0x7fffffe87fffffeL & l) == 0L)
+ break;
+ if (kind > 21)
+ kind = 21;
+ jjCheckNAdd(28);
+ break;
+ case 1:
+ jjAddStates(7, 9);
+ break;
+ case 7:
+ jjCheckNAddTwoStates(7, 8);
+ break;
+ case 9:
+ case 10:
+ jjCheckNAddTwoStates(10, 8);
+ break;
+ case 16:
+ if ((0x100000001000L & l) != 0L && kind > 17)
+ kind = 17;
+ break;
+ case 19:
+ if ((0x2000000020L & l) != 0L)
+ jjAddStates(13, 14);
+ break;
+ case 25:
+ jjAddStates(4, 6);
+ break;
+ case 33:
+ if ((0x2000000020L & l) != 0L)
+ jjAddStates(15, 16);
+ break;
+ case 37:
+ if ((0x2000000020L & l) != 0L)
+ jjAddStates(17, 18);
+ break;
+ default:
+ break;
+ }
+ } while (i != startsAt);
+ } else {
+ int hiByte = (int) (curChar >> 8);
+ int i1 = hiByte >> 6;
+ long l1 = 1L << (hiByte & 077);
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ do {
+ switch (jjstateSet[--i]) {
+ case 1:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+ jjAddStates(7, 9);
+ break;
+ case 7:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+ jjCheckNAddTwoStates(7, 8);
+ break;
+ case 9:
+ case 10:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+ jjCheckNAddTwoStates(10, 8);
+ break;
+ case 25:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+ jjAddStates(4, 6);
+ break;
+ default:
+ break;
+ }
+ } while (i != startsAt);
+ }
+ if (kind != 0x7fffffff) {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 40 - (jjnewStateCnt = startsAt)))
+ break;
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ break;
+ }
+ }
+ if (jjmatchedPos > strPos)
+ return curPos;
+
+ int toRet = Math.max(curPos, seenUpto);
+
+ if (curPos < toRet)
+ for (i = toRet - Math.min(curPos, seenUpto); i-- > 0; )
+ try {
+ curChar = inputStream.readChar();
+ } catch (java.io.IOException e) {
+ throw new Error("Internal Error : Please send a bug report.");
+ }
+
+ if (jjmatchedPos < strPos) {
+ jjmatchedKind = strKind;
+ jjmatchedPos = strPos;
+ } else if (jjmatchedPos == strPos && jjmatchedKind > strKind)
+ jjmatchedKind = strKind;
+
+ return toRet;
+ }
+
+ static final int[] JJ_NEXT_STATES = {
+ 30, 31, 36, 37, 24, 25, 26, 1, 2, 4, 8, 9, 11, 20, 21, 34,
+ 35, 38, 39,
+ };
+
+ private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) {
+ switch (hiByte) {
+ case 0:
+ return (JJ_BIT_VEC_2[i2] & l2) != 0L;
+ default:
+ if ((JJ_BIT_VEC_0[i1] & l1) != 0L)
+ return true;
+ return false;
+ }
+ }
+
+ /**
+ * Token literal values.
+ */
+ public static final String[] JJ_STR_LITERAL_IMAGES = {
+ "", null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, "\75", "\74\76", "\76",
+ "\76\75", "\74", "\74\75", "\50", "\54", "\51", "\53", "\55"};
+
+ /**
+ * Lexer state names.
+ */
+ public static final String[] LEX_STATE_NAMES = {
+ "DEFAULT",
+ };
+ static final long[] JJ_TO_TOKEN = {
+ 0x1fff7ff01L,
+ };
+ static final long[] JJ_TO_SKIP = {
+ 0xfeL,
+ };
+ static final long[] JJ_TO_SPECIAL = {
+ 0x3eL,
+ };
+ protected SimpleCharStream inputStream;
+ private final int[] jjrounds = new int[40];
+ private final int[] jjstateSet = new int[80];
+ protected char curChar;
+
+ /**
+ * Constructor.
+ */
+ public SelectorParserTokenManager(SimpleCharStream stream) {
+ if (SimpleCharStream.STATIC_FLAG)
+ throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+ inputStream = stream;
+ }
+
+ /**
+ * Constructor.
+ */
+ public SelectorParserTokenManager(SimpleCharStream stream, int lexState) {
+ this(stream);
+ SwitchTo(lexState);
+ }
+
+ /**
+ * Reinitialise parser.
+ */
+ public void ReInit(SimpleCharStream stream) {
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ inputStream = stream;
+ ReInitRounds();
+ }
+
+ private void ReInitRounds() {
+ int i;
+ jjround = 0x80000001;
+ for (i = 40; i-- > 0; )
+ jjrounds[i] = 0x80000000;
+ }
+
+ /**
+ * Reinitialise parser.
+ */
+ public void ReInit(SimpleCharStream stream, int lexState) {
+ ReInit(stream);
+ SwitchTo(lexState);
+ }
+
+ /**
+ * Switch to specified lex state.
+ */
+ public void SwitchTo(int lexState) {
+ if (lexState >= 1 || lexState < 0)
+ throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.",
+ TokenMgrError.INVALID_LEXICAL_STATE);
+ else
+ curLexState = lexState;
+ }
+
+ protected Token jjFillToken() {
+ final Token t;
+ final String curTokenImage;
+ final int beginLine;
+ final int endLine;
+ final int beginColumn;
+ final int endColumn;
+ String im = JJ_STR_LITERAL_IMAGES[jjmatchedKind];
+ curTokenImage = (im == null) ? inputStream.GetImage() : im;
+ beginLine = inputStream.getBeginLine();
+ beginColumn = inputStream.getBeginColumn();
+ endLine = inputStream.getEndLine();
+ endColumn = inputStream.getEndColumn();
+ t = Token.newToken(jjmatchedKind, curTokenImage);
+
+ t.beginLine = beginLine;
+ t.endLine = endLine;
+ t.beginColumn = beginColumn;
+ t.endColumn = endColumn;
+
+ return t;
+ }
+
+ int curLexState = 0;
+ int defaultLexState = 0;
+ int jjnewStateCnt;
+ int jjround;
+ int jjmatchedPos;
+ int jjmatchedKind;
+
+ /**
+ * Get the next Token.
+ */
+ public Token getNextToken() {
+ Token specialToken = null;
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop:
+ for (;;) {
+ try {
+ curChar = inputStream.BeginToken();
+ } catch (java.io.IOException e) {
+ jjmatchedKind = 0;
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ }
+
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ if (jjmatchedKind != 0x7fffffff) {
+ if (jjmatchedPos + 1 < curPos)
+ inputStream.backup(curPos - jjmatchedPos - 1);
+ if ((JJ_TO_TOKEN[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) {
+ matchedToken = jjFillToken();
+ matchedToken.specialToken = specialToken;
+ return matchedToken;
+ } else {
+ if ((JJ_TO_SPECIAL[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) {
+ matchedToken = jjFillToken();
+ if (specialToken == null)
+ specialToken = matchedToken;
+ else {
+ matchedToken.specialToken = specialToken;
+ specialToken = specialToken.next = matchedToken;
+ }
+ }
+ continue EOFLoop;
+ }
+ }
+ int errorLine = inputStream.getEndLine();
+ int errorColumn = inputStream.getEndColumn();
+ String errorAfter = null;
+ boolean eofSeen = false;
+ try {
+ inputStream.readChar();
+ inputStream.backup(1);
+ } catch (java.io.IOException e1) {
+ eofSeen = true;
+ errorAfter = curPos <= 1 ? "" : inputStream.GetImage();
+ if (curChar == '\n' || curChar == '\r') {
+ errorLine++;
+ errorColumn = 0;
+ } else
+ errorColumn++;
+ }
+ if (!eofSeen) {
+ inputStream.backup(1);
+ errorAfter = curPos <= 1 ? "" : inputStream.GetImage();
+ }
+ throw new TokenMgrError(eofSeen, curLexState, errorLine, errorColumn, errorAfter, curChar,
+ TokenMgrError.LEXICAL_ERROR);
+ }
+ }
+
+ private void jjCheckNAdd(int state) {
+ if (jjrounds[state] != jjround) {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+ }
+
+ private void jjAddStates(int start, int end) {
+ do {
+ jjstateSet[jjnewStateCnt++] = JJ_NEXT_STATES[start];
+ } while (start++ != end);
+ }
+
+ private void jjCheckNAddTwoStates(int state1, int state2) {
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+ }
+
+ private void jjCheckNAddStates(int start, int end) {
+ do {
+ jjCheckNAdd(JJ_NEXT_STATES[start]);
+ } while (start++ != end);
+ }
+
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java
new file mode 100644
index 0000000000..94a54b41c2
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java
@@ -0,0 +1,502 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */
+/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream {
+ /**
+ * Whether parser is static.
+ */
+ public static final boolean STATIC_FLAG = false;
+ int bufsize;
+ int available;
+ int tokenBegin;
+ /**
+ * Position in buffer.
+ */
+ public int bufpos = -1;
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+ protected int tabSize = 8;
+
+ protected void setTabSize(int i) {
+ tabSize = i;
+ }
+
+ protected int getTabSize(int i) {
+ return tabSize;
+ }
+
+ protected void ExpandBuff(boolean wrapAround) {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try {
+ if (wrapAround) {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = bufpos += bufsize - tokenBegin;
+ } else {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = bufpos -= tokenBegin;
+ }
+ } catch (Throwable t) {
+ throw new Error(t.getMessage());
+ }
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException {
+ if (maxNextCharInd == available) {
+ if (available == bufsize) {
+ if (tokenBegin > 2048) {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ } else if (tokenBegin < 0)
+ bufpos = maxNextCharInd = 0;
+ else
+ ExpandBuff(false);
+ } else if (available > tokenBegin)
+ available = bufsize;
+ else if ((tokenBegin - available) < 2048)
+ ExpandBuff(true);
+ else
+ available = tokenBegin;
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) {
+ inputStream.close();
+ throw new java.io.IOException();
+ } else
+ maxNextCharInd += i;
+ return;
+ } catch (java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1)
+ tokenBegin = bufpos;
+ throw e;
+ }
+ }
+
+ /**
+ * Start.
+ */
+ public char BeginToken() throws java.io.IOException {
+ tokenBegin = -1;
+ char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(char c) {
+ column++;
+
+ if (prevCharIsLF) {
+ prevCharIsLF = false;
+ line += column = 1;
+ } else if (prevCharIsCR) {
+ prevCharIsCR = false;
+ if (c == '\n') {
+ prevCharIsLF = true;
+ } else
+ line += column = 1;
+ }
+
+ switch (c) {
+ case '\r':
+ prevCharIsCR = true;
+ break;
+ case '\n':
+ prevCharIsLF = true;
+ break;
+ case '\t':
+ column--;
+ column += tabSize - (column % tabSize);
+ break;
+ default:
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ /**
+ * Read a character.
+ */
+ public char readChar() throws java.io.IOException {
+ if (inBuf > 0) {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd)
+ FillBuff();
+
+ char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return c;
+ }
+
+ @Deprecated
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+
+ public int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ @Deprecated
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+
+ public int getLine() {
+ return bufline[bufpos];
+ }
+
+ /**
+ * Get token end column number.
+ */
+ public int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * Get token end line number.
+ */
+ public int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ /**
+ * Get token beginning column number.
+ */
+ public int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ /**
+ * Get token beginning line number.
+ */
+ public int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ /**
+ * Backup a number of characters.
+ */
+ public void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0)
+ bufpos += bufsize;
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize) {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn) {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.Reader dstream) {
+ this(dstream, 1, 1, 4096);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize) {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length) {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn) {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.Reader dstream) {
+ ReInit(dstream, 1, 1, 4096);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException {
+ this(encoding == null ?
+ new java.io.InputStreamReader(dstream) :
+ new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize) {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException {
+ this(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn) {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException {
+ this(dstream, encoding, 1, 1, 4096);
+ }
+
+ /**
+ * Constructor.
+ */
+ public SimpleCharStream(java.io.InputStream dstream) {
+ this(dstream, 1, 1, 4096);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException {
+ ReInit(encoding == null ?
+ new java.io.InputStreamReader(dstream) :
+ new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize) {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException {
+ ReInit(dstream, encoding, 1, 1, 4096);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream dstream) {
+ ReInit(dstream, 1, 1, 4096);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException {
+ ReInit(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ /**
+ * Reinitialise.
+ */
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn) {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ /**
+ * Get token literal value.
+ */
+ public String GetImage() {
+ if (bufpos >= tokenBegin)
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+ /**
+ * Get the suffix.
+ */
+ public char[] GetSuffix(int len) {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len)
+ System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+ len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Reset buffer when finished.
+ */
+ public void Done() {
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /**
+ * Method to adjust line and column numbers for the start of a token.
+ */
+ public void adjustBeginLineColumn(int newLine, int newCol) {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin) {
+ len = bufpos - tokenBegin + inBuf + 1;
+ } else {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len) {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len) {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+ bufline[j] = newLine++;
+ else
+ bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+}
+/* JavaCC - OriginalChecksum=af79bfe4b18b4b4ea9720ffeb7e52fc5 (do not edit this line) */
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/Token.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/Token.java
new file mode 100644
index 0000000000..8e6a48a086
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/Token.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 5.0 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token implements java.io.Serializable {
+
+ /**
+ * The version identifier for this Serializable class.
+ * Increment only if the serialized form of the
+ * class changes.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * An integer that describes the kind of this token. This numbering
+ * system is determined by JavaCCParser, and a table of these numbers is
+ * stored in the file ...Constants.java.
+ */
+ public int kind;
+
+ /**
+ * The line number of the first character of this Token.
+ */
+ public int beginLine;
+ /**
+ * The column number of the first character of this Token.
+ */
+ public int beginColumn;
+ /**
+ * The line number of the last character of this Token.
+ */
+ public int endLine;
+ /**
+ * The column number of the last character of this Token.
+ */
+ public int endColumn;
+
+ /**
+ * The string image of the token.
+ */
+ public String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input
+ * stream. If this is the last token from the input stream, or if the
+ * token manager has not read tokens beyond this one, this field is
+ * set to null. This is true only if this token is also a regular
+ * token. Otherwise, see below for a description of the contents of
+ * this field.
+ */
+ public Token next;
+
+ /**
+ * This field is used to access special tokens that occur prior to this
+ * token, but after the immediately preceding regular (non-special) token.
+ * If there are no such special tokens, this field is set to null.
+ * When there are more than one such special token, this field refers
+ * to the last of these special tokens, which in turn refers to the next
+ * previous special token through its specialToken field, and so on
+ * until the first special token (whose specialToken field is null).
+ * The next fields of special tokens refer to other special tokens that
+ * immediately follow it (without an intervening regular token). If there
+ * is no such token, this field is null.
+ */
+ public Token specialToken;
+
+ /**
+ * An optional attribute value of the Token.
+ * Tokens which are not used as syntactic sugar will often contain
+ * meaningful values that will be used later on by the compiler or
+ * interpreter. This attribute value is often different from the image.
+ * Any subclass of Token that actually wants to return a non-null value can
+ * override this method as appropriate.
+ */
+ public Object getValue() {
+ return null;
+ }
+
+ /**
+ * No-argument constructor
+ */
+ public Token() {
+ }
+
+ /**
+ * Constructs a new token for the specified Image.
+ */
+ public Token(int kind) {
+ this(kind, null);
+ }
+
+ /**
+ * Constructs a new token for the specified Image and Kind.
+ */
+ public Token(int kind, String image) {
+ this.kind = kind;
+ this.image = image;
+ }
+
+ /**
+ * Returns the image.
+ */
+ public String toString() {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you
+ * can create and return subclass objects based on the value of ofKind.
+ * Simply add the cases to the switch for all those special cases.
+ * For example, if you have a subclass of Token called IDToken that
+ * you want to create if ofKind is ID, simply add something like :
+ *
+ * case MyParserConstants.ID : return new IDToken(ofKind, image);
+ *
+ * to the following switch statement. Then you can cast matchedToken
+ * variable to the appropriate type and use sit in your lexical actions.
+ */
+ public static Token newToken(int ofKind, String image) {
+ switch (ofKind) {
+ default:
+ return new Token(ofKind, image);
+ }
+ }
+
+ public static Token newToken(int ofKind) {
+ return newToken(ofKind, null);
+ }
+
+}
+/* JavaCC - OriginalChecksum=6b0af88eb45a551d929d3cdd9582f827 (do not edit this line) */
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/TokenMgrError.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/TokenMgrError.java
new file mode 100644
index 0000000000..75d83e5f17
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/TokenMgrError.java
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 5.0 */
+/* JavaCCOptions: */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * Token Manager Error.
+ */
+public class TokenMgrError extends Error {
+
+ /**
+ * The version identifier for this Serializable class.
+ * Increment only if the serialized form of the
+ * class changes.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+ /**
+ * Lexical error occurred.
+ */
+ static final int LEXICAL_ERROR = 0;
+
+ /**
+ * An attempt was made to create a second instance of a static token manager.
+ */
+ static final int STATIC_LEXER_ERROR = 1;
+
+ /**
+ * Tried to change to an invalid lexical state.
+ */
+ static final int INVALID_LEXICAL_STATE = 2;
+
+ /**
+ * Detected (and bailed out of) an infinite loop in the token manager.
+ */
+ static final int LOOP_DETECTED = 3;
+
+ /**
+ * Indicates the reason why the exception is thrown. It will have
+ * one of the above 4 values.
+ */
+ int errorCode;
+
+ /**
+ * Replaces unprintable characters by their escaped (or unicode escaped)
+ * equivalents in the given string
+ */
+ protected static final String addEscapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i)) {
+ case 0:
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the
+ * token manager to indicate a lexical error.
+ * Parameters :
+ * eofSeen : indicates if EOF caused the lexical error
+ * curLexState : lexical state in which this error occurred
+ * errorLine : line number when the error occurred
+ * errorColumn : column number when the error occurred
+ * errorAfter : prefix that was seen before this error occurred
+ * curchar : the offending character
+ * Note: You can customize the lexical error message by modifying this method.
+ */
+ protected static String LexicalError(boolean eofSeen, int lexState, int errorLine, int errorColumn,
+ String errorAfter, char curChar) {
+ return "Lexical error at line " +
+ errorLine + ", column " +
+ errorColumn + ". Encountered: " +
+ (eofSeen ?
+ " " :
+ ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int) curChar + "), ") +
+ "after : \"" + addEscapes(errorAfter) + "\"";
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages.
+ * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+ * of end-users concern, so you can return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ * from this method for such cases in the release version of your parser.
+ */
+ public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ /**
+ * No arg constructor.
+ */
+ public TokenMgrError() {
+ }
+
+ /**
+ * Constructor with message and reason.
+ */
+ public TokenMgrError(String message, int reason) {
+ super(message);
+ errorCode = reason;
+ }
+
+ /**
+ * Full Constructor.
+ */
+ public TokenMgrError(boolean eofSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar,
+ int reason) {
+ this(LexicalError(eofSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+ }
+}
+/* JavaCC - OriginalChecksum=e960778c8dcd73e167ed5bfddd59f288 (do not edit this line) */
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/util/BitsArray.java b/filter/src/main/java/org/apache/rocketmq/filter/util/BitsArray.java
new file mode 100644
index 0000000000..986685490e
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/util/BitsArray.java
@@ -0,0 +1,260 @@
+/*
+ * 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.filter.util;
+
+/**
+ * Wrapper of bytes array, in order to operate single bit easily.
+ */
+public class BitsArray implements Cloneable {
+
+ private byte[] bytes;
+ private int bitLength;
+
+ public static BitsArray create(int bitLength) {
+ return new BitsArray(bitLength);
+ }
+
+ public static BitsArray create(byte[] bytes, int bitLength) {
+ return new BitsArray(bytes, bitLength);
+ }
+
+ public static BitsArray create(byte[] bytes) {
+ return new BitsArray(bytes);
+ }
+
+ private BitsArray(int bitLength) {
+ this.bitLength = bitLength;
+ // init bytes
+ int temp = bitLength / Byte.SIZE;
+ if (bitLength % Byte.SIZE > 0) {
+ temp++;
+ }
+ bytes = new byte[temp];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) 0x00;
+ }
+ }
+
+ private BitsArray(byte[] bytes, int bitLength) {
+ if (bytes == null || bytes.length < 1) {
+ throw new IllegalArgumentException("Bytes is empty!");
+ }
+
+ if (bitLength < 1) {
+ throw new IllegalArgumentException("Bit is less than 1.");
+ }
+
+ if (bitLength < bytes.length * Byte.SIZE) {
+ throw new IllegalArgumentException("BitLength is less than bytes.length() * " + Byte.SIZE);
+ }
+
+ this.bytes = new byte[bytes.length];
+ System.arraycopy(bytes, 0, this.bytes, 0, this.bytes.length);
+ this.bitLength = bitLength;
+ }
+
+ private BitsArray(byte[] bytes) {
+ if (bytes == null || bytes.length < 1) {
+ throw new IllegalArgumentException("Bytes is empty!");
+ }
+
+ this.bitLength = bytes.length * Byte.SIZE;
+ this.bytes = new byte[bytes.length];
+ System.arraycopy(bytes, 0, this.bytes, 0, this.bytes.length);
+ }
+
+ public int bitLength() {
+ return this.bitLength;
+ }
+
+ public int byteLength() {
+ return this.bytes.length;
+ }
+
+ public byte[] bytes() {
+ return this.bytes;
+ }
+
+ public void xor(final BitsArray other) {
+ checkInitialized(this);
+ checkInitialized(other);
+
+ int minByteLength = Math.min(this.byteLength(), other.byteLength());
+
+ for (int i = 0; i < minByteLength; i++) {
+ this.bytes[i] = (byte) (this.bytes[i] ^ other.getByte(i));
+ }
+ }
+
+ public void xor(int bitPos, boolean set) {
+ checkBitPosition(bitPos, this);
+
+ boolean value = getBit(bitPos);
+ if (value ^ set) {
+ setBit(bitPos, true);
+ } else {
+ setBit(bitPos, false);
+ }
+ }
+
+ public void or(final BitsArray other) {
+ checkInitialized(this);
+ checkInitialized(other);
+
+ int minByteLength = Math.min(this.byteLength(), other.byteLength());
+
+ for (int i = 0; i < minByteLength; i++) {
+ this.bytes[i] = (byte) (this.bytes[i] | other.getByte(i));
+ }
+ }
+
+ public void or(int bitPos, boolean set) {
+ checkBitPosition(bitPos, this);
+
+ if (set) {
+ setBit(bitPos, true);
+ }
+ }
+
+ public void and(final BitsArray other) {
+ checkInitialized(this);
+ checkInitialized(other);
+
+ int minByteLength = Math.min(this.byteLength(), other.byteLength());
+
+ for (int i = 0; i < minByteLength; i++) {
+ this.bytes[i] = (byte) (this.bytes[i] & other.getByte(i));
+ }
+ }
+
+ public void and(int bitPos, boolean set) {
+ checkBitPosition(bitPos, this);
+
+ if (!set) {
+ setBit(bitPos, false);
+ }
+ }
+
+ public void not(int bitPos) {
+ checkBitPosition(bitPos, this);
+
+ setBit(bitPos, !getBit(bitPos));
+ }
+
+ public void setBit(int bitPos, boolean set) {
+ checkBitPosition(bitPos, this);
+ int sub = subscript(bitPos);
+ int pos = position(bitPos);
+ if (set) {
+ this.bytes[sub] = (byte) (this.bytes[sub] | pos);
+ } else {
+ this.bytes[sub] = (byte) (this.bytes[sub] & ~pos);
+ }
+ }
+
+ public void setByte(int bytePos, byte set) {
+ checkBytePosition(bytePos, this);
+
+ this.bytes[bytePos] = set;
+ }
+
+ public boolean getBit(int bitPos) {
+ checkBitPosition(bitPos, this);
+
+ return (this.bytes[subscript(bitPos)] & position(bitPos)) != 0;
+ }
+
+ public byte getByte(int bytePos) {
+ checkBytePosition(bytePos, this);
+
+ return this.bytes[bytePos];
+ }
+
+ protected int subscript(int bitPos) {
+ return bitPos / Byte.SIZE;
+ }
+
+ protected int position(int bitPos) {
+ return 1 << bitPos % Byte.SIZE;
+ }
+
+ protected void checkBytePosition(int bytePos, BitsArray bitsArray) {
+ checkInitialized(bitsArray);
+ if (bytePos > bitsArray.byteLength()) {
+ throw new IllegalArgumentException("BytePos is greater than " + bytes.length);
+ }
+ if (bytePos < 0) {
+ throw new IllegalArgumentException("BytePos is less than 0");
+ }
+ }
+
+ protected void checkBitPosition(int bitPos, BitsArray bitsArray) {
+ checkInitialized(bitsArray);
+ if (bitPos > bitsArray.bitLength()) {
+ throw new IllegalArgumentException("BitPos is greater than " + bitLength);
+ }
+ if (bitPos < 0) {
+ throw new IllegalArgumentException("BitPos is less than 0");
+ }
+ }
+
+ protected void checkInitialized(BitsArray bitsArray) {
+ if (bitsArray.bytes() == null) {
+ throw new RuntimeException("Not initialized!");
+ }
+ }
+
+ public BitsArray clone() {
+ byte[] clone = new byte[this.byteLength()];
+
+ System.arraycopy(this.bytes, 0, clone, 0, this.byteLength());
+
+ return create(clone, bitLength());
+ }
+
+ @Override
+ public String toString() {
+ if (this.bytes == null) {
+ return "null";
+ }
+ StringBuilder stringBuilder = new StringBuilder(this.bytes.length * Byte.SIZE);
+ for (int i = this.bytes.length - 1; i >= 0; i--) {
+
+ int j = Byte.SIZE - 1;
+ if (i == this.bytes.length - 1 && this.bitLength % Byte.SIZE > 0) {
+ // not full byte
+ j = this.bitLength % Byte.SIZE;
+ }
+
+ for (; j >= 0; j--) {
+
+ byte mask = (byte) (1 << j);
+ if ((this.bytes[i] & mask) == mask) {
+ stringBuilder.append("1");
+ } else {
+ stringBuilder.append("0");
+ }
+ }
+ if (i % 8 == 0) {
+ stringBuilder.append("\n");
+ }
+ }
+
+ return stringBuilder.toString();
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilter.java b/filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilter.java
new file mode 100644
index 0000000000..f61090622e
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilter.java
@@ -0,0 +1,338 @@
+/*
+ * 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.filter.util;
+
+import com.google.common.hash.Hashing;
+
+import java.nio.charset.Charset;
+
+/**
+ * Simple implement of bloom filter.
+ */
+public class BloomFilter {
+
+ public static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ // as error rate, 10/100 = 0.1
+ private int f = 10;
+ private int n = 128;
+
+ // hash function num, by calculation.
+ private int k;
+ // bit count, by calculation.
+ private int m;
+
+ /**
+ * Create bloom filter by error rate and mapping num.
+ *
+ * @param f error rate
+ * @param n num will mapping to bit
+ * @return
+ */
+ public static BloomFilter createByFn(int f, int n) {
+ return new BloomFilter(f, n);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param f error rate
+ * @param n num will mapping to bit
+ */
+ private BloomFilter(int f, int n) {
+ if (f < 1 || f >= 100) {
+ throw new IllegalArgumentException("f must be greater or equal than 1 and less than 100");
+ }
+ if (n < 1) {
+ throw new IllegalArgumentException("n must be greater than 0");
+ }
+
+ this.f = f;
+ this.n = n;
+
+ // set p = e^(-kn/m)
+ // f = (1 - p)^k = e^(kln(1-p))
+ // when p = 0.5, k = ln2 * (m/n), f = (1/2)^k = (0.618)^(m/n)
+ double errorRate = f / 100.0;
+ this.k = (int) Math.ceil(logMN(0.5, errorRate));
+
+ if (this.k < 1) {
+ throw new IllegalArgumentException("Hash function num is less than 1, maybe you should change the value of error rate or bit num!");
+ }
+
+ // m >= n*log2(1/f)*log2(e)
+ this.m = (int) Math.ceil(this.n * logMN(2, 1 / errorRate) * logMN(2, Math.E));
+ // m%8 = 0
+ this.m = (int) (Byte.SIZE * Math.ceil(this.m / (Byte.SIZE * 1.0)));
+ }
+
+ /**
+ * Calculate bit positions of {@code str}.
+ *
+ * See "Less Hashing, Same Performance: Building a Better Bloom Filter" by Adam Kirsch and Michael
+ * Mitzenmacher.
+ *
+ *
+ * @param str
+ * @return
+ */
+ public int[] calcBitPositions(String str) {
+ int[] bitPositions = new int[this.k];
+
+ long hash64 = Hashing.murmur3_128().hashString(str, UTF_8).asLong();
+
+ int hash1 = (int) hash64;
+ int hash2 = (int) (hash64 >>> 32);
+
+ for (int i = 1; i <= this.k; i++) {
+ int combinedHash = hash1 + (i * hash2);
+ // Flip all the bits if it's negative (guaranteed positive number)
+ if (combinedHash < 0) {
+ combinedHash = ~combinedHash;
+ }
+ bitPositions[i - 1] = combinedHash % this.m;
+ }
+
+ return bitPositions;
+ }
+
+ /**
+ * Calculate bit positions of {@code str} to construct {@code BloomFilterData}
+ *
+ * @param str
+ * @return
+ */
+ public BloomFilterData generate(String str) {
+ int[] bitPositions = calcBitPositions(str);
+
+ return new BloomFilterData(bitPositions, this.m);
+ }
+
+ /**
+ * Calculate bit positions of {@code str}, then set the related {@code bits} positions to 1.
+ *
+ * @param str
+ * @param bits
+ */
+ public void hashTo(String str, BitsArray bits) {
+ hashTo(calcBitPositions(str), bits);
+ }
+
+ /**
+ * Set the related {@code bits} positions to 1.
+ *
+ * @param bitPositions
+ * @param bits
+ */
+ public void hashTo(int[] bitPositions, BitsArray bits) {
+ check(bits);
+
+ for (int i : bitPositions) {
+ bits.setBit(i, true);
+ }
+ }
+
+ /**
+ * Extra check:
+ *
1. check {@code filterData} belong to this bloom filter.
+ *
+ * Then set the related {@code bits} positions to 1.
+ *
+ *
+ * @param filterData
+ * @param bits
+ */
+ public void hashTo(BloomFilterData filterData, BitsArray bits) {
+ if (!isValid(filterData)) {
+ throw new IllegalArgumentException(
+ String.format("Bloom filter data may not belong to this filter! %s, %s",
+ filterData, this.toString())
+ );
+ }
+ hashTo(filterData.getBitPos(), bits);
+ }
+
+ /**
+ * Calculate bit positions of {@code str}, then check all the related {@code bits} positions is 1.
+ *
+ * @param str
+ * @param bits
+ * @return true: all the related {@code bits} positions is 1
+ */
+ public boolean isHit(String str, BitsArray bits) {
+ return isHit(calcBitPositions(str), bits);
+ }
+
+ /**
+ * Check all the related {@code bits} positions is 1.
+ *
+ * @param bitPositions
+ * @param bits
+ * @return true: all the related {@code bits} positions is 1
+ */
+ public boolean isHit(int[] bitPositions, BitsArray bits) {
+ check(bits);
+ boolean ret = bits.getBit(bitPositions[0]);
+ for (int i = 1; i < bitPositions.length; i++) {
+ ret &= bits.getBit(bitPositions[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Check all the related {@code bits} positions is 1.
+ *
+ * @param filterData
+ * @param bits
+ * @return true: all the related {@code bits} positions is 1
+ */
+ public boolean isHit(BloomFilterData filterData, BitsArray bits) {
+ if (!isValid(filterData)) {
+ throw new IllegalArgumentException(
+ String.format("Bloom filter data may not belong to this filter! %s, %s",
+ filterData, this.toString())
+ );
+ }
+ return isHit(filterData.getBitPos(), bits);
+ }
+
+ /**
+ * Check whether one of {@code bitPositions} has been occupied.
+ *
+ * @param bitPositions
+ * @param bits
+ * @return true: if all positions have been occupied.
+ */
+ public boolean checkFalseHit(int[] bitPositions, BitsArray bits) {
+ for (int j = 0; j < bitPositions.length; j++) {
+ int pos = bitPositions[j];
+
+ // check position of bits has been set.
+ // that mean no one occupy the position.
+ if (!bits.getBit(pos)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void check(BitsArray bits) {
+ if (bits.bitLength() != this.m) {
+ throw new IllegalArgumentException(
+ String.format("Length(%d) of bits in BitsArray is not equal to %d!", bits.bitLength(), this.m)
+ );
+ }
+ }
+
+ /**
+ * Check {@code BloomFilterData} is valid, and belong to this bloom filter.
+ *
1. not null
+ *
2. {@link org.apache.rocketmq.filter.util.BloomFilterData#getBitNum} must be equal to {@code m}
+ *
3. {@link org.apache.rocketmq.filter.util.BloomFilterData#getBitPos} is not null
+ *
4. {@link org.apache.rocketmq.filter.util.BloomFilterData#getBitPos}'s length is equal to {@code k}
+ *
+ * @param filterData
+ * @return
+ */
+ public boolean isValid(BloomFilterData filterData) {
+ if (filterData == null
+ || filterData.getBitNum() != this.m
+ || filterData.getBitPos() == null
+ || filterData.getBitPos().length != this.k) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * error rate.
+ *
+ * @return
+ */
+ public int getF() {
+ return f;
+ }
+
+ /**
+ * expect mapping num.
+ *
+ * @return
+ */
+ public int getN() {
+ return n;
+ }
+
+ /**
+ * hash function num.
+ *
+ * @return
+ */
+ public int getK() {
+ return k;
+ }
+
+ /**
+ * total bit num.
+ *
+ * @return
+ */
+ public int getM() {
+ return m;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof BloomFilter))
+ return false;
+
+ BloomFilter that = (BloomFilter) o;
+
+ if (f != that.f)
+ return false;
+ if (k != that.k)
+ return false;
+ if (m != that.m)
+ return false;
+ if (n != that.n)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = f;
+ result = 31 * result + n;
+ result = 31 * result + k;
+ result = 31 * result + m;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("f: %d, n: %d, k: %d, m: %d", f, n, k, m);
+ }
+
+ protected double logMN(double m, double n) {
+ return Math.log(n) / Math.log(m);
+ }
+}
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilterData.java b/filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilterData.java
new file mode 100644
index 0000000000..de02d927de
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/util/BloomFilterData.java
@@ -0,0 +1,83 @@
+/*
+ * 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.filter.util;
+
+import java.util.Arrays;
+
+/**
+ * Data generated by bloom filter, include:
+ *
1. Bit positions allocated to requester;
+ *
2. Total bit num when allocating;
+ */
+public class BloomFilterData {
+
+ private int[] bitPos;
+ private int bitNum;
+
+ public BloomFilterData() {
+ }
+
+ public BloomFilterData(int[] bitPos, int bitNum) {
+ this.bitPos = bitPos;
+ this.bitNum = bitNum;
+ }
+
+ public int[] getBitPos() {
+ return bitPos;
+ }
+
+ public int getBitNum() {
+ return bitNum;
+ }
+
+ public void setBitPos(final int[] bitPos) {
+ this.bitPos = bitPos;
+ }
+
+ public void setBitNum(final int bitNum) {
+ this.bitNum = bitNum;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (!(o instanceof BloomFilterData)) return false;
+
+ final BloomFilterData that = (BloomFilterData) o;
+
+ if (bitNum != that.bitNum) return false;
+ if (!Arrays.equals(bitPos, that.bitPos)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = bitPos != null ? Arrays.hashCode(bitPos) : 0;
+ result = 31 * result + bitNum;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "BloomFilterData{" +
+ "bitPos=" + Arrays.toString(bitPos) +
+ ", bitNum=" + bitNum +
+ '}';
+ }
+}
diff --git a/filter/src/test/java/org/apache/rocketmq/filter/BitsArrayTest.java b/filter/src/test/java/org/apache/rocketmq/filter/BitsArrayTest.java
new file mode 100644
index 0000000000..ef81b29b88
--- /dev/null
+++ b/filter/src/test/java/org/apache/rocketmq/filter/BitsArrayTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.filter.util.BitsArray;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BitsArrayTest {
+
+ BitsArray gen(int bitCount) {
+ BitsArray bitsArray = BitsArray.create(bitCount);
+
+ for (int i = 0; i < bitCount / Byte.SIZE; i++) {
+ bitsArray.setByte(i, (byte) (new Random(System.currentTimeMillis())).nextInt(0xff));
+ try {
+ Thread.sleep(2);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ return bitsArray;
+ }
+
+ int bitLength = Byte.SIZE;
+
+ @Test
+ public void testConstructor() {
+ BitsArray bitsArray = BitsArray.create(8);
+
+ assertThat(bitsArray.byteLength() == 1 && bitsArray.bitLength() == 8).isTrue();
+
+ bitsArray = BitsArray.create(9);
+
+ assertThat(bitsArray.byteLength() == 2 && bitsArray.bitLength() == 9).isTrue();
+
+ bitsArray = BitsArray.create(7);
+
+ assertThat(bitsArray.byteLength() == 1 && bitsArray.bitLength() == 7).isTrue();
+ }
+
+ @Test
+ public void testSet() {
+ BitsArray bitsArray = gen(bitLength);
+ BitsArray backUp = bitsArray.clone();
+
+ boolean val = bitsArray.getBit(2);
+
+ bitsArray.setBit(2, !val);
+
+ bitsArray.xor(backUp);
+
+ assertThat(bitsArray.getBit(2)).isTrue();
+ }
+
+ @Test
+ public void testAndOr() {
+ BitsArray bitsArray = gen(bitLength);
+
+ boolean val = bitsArray.getBit(2);
+
+ if (val) {
+ bitsArray.and(2, false);
+ assertThat(!bitsArray.getBit(2)).isTrue();
+ } else {
+ bitsArray.or(2, true);
+ assertThat(bitsArray.getBit(2)).isTrue();
+ }
+ }
+
+ @Test
+ public void testXor() {
+ BitsArray bitsArray = gen(bitLength);
+
+ boolean val = bitsArray.getBit(2);
+
+ bitsArray.xor(2, !val);
+
+ assertThat(bitsArray.getBit(2)).isTrue();
+ }
+
+ @Test
+ public void testNot() {
+ BitsArray bitsArray = gen(bitLength);
+ BitsArray backUp = bitsArray.clone();
+
+ bitsArray.not(2);
+
+ bitsArray.xor(backUp);
+
+ assertThat(bitsArray.getBit(2)).isTrue();
+ }
+
+ @Test
+ public void testOr() {
+ BitsArray b1 = BitsArray.create(new byte[]{(byte) 0xff, 0x00});
+ BitsArray b2 = BitsArray.create(new byte[]{0x00, (byte) 0xff});
+
+ b1.or(b2);
+
+ for (int i = 0; i < b1.bitLength(); i++) {
+ assertThat(b1.getBit(i)).isTrue();
+ }
+ }
+}
diff --git a/filter/src/test/java/org/apache/rocketmq/filter/BloomFilterTest.java b/filter/src/test/java/org/apache/rocketmq/filter/BloomFilterTest.java
new file mode 100644
index 0000000000..c6097ee85c
--- /dev/null
+++ b/filter/src/test/java/org/apache/rocketmq/filter/BloomFilterTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.filter.util.BitsArray;
+import org.apache.rocketmq.filter.util.BloomFilter;
+import org.apache.rocketmq.filter.util.BloomFilterData;
+import org.junit.Test;
+
+import java.util.Random;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BloomFilterTest {
+
+ @Test
+ public void testEquals() {
+ BloomFilter a = BloomFilter.createByFn(10, 20);
+
+ BloomFilter b = BloomFilter.createByFn(10, 20);
+
+ BloomFilter c = BloomFilter.createByFn(12, 20);
+
+ BloomFilter d = BloomFilter.createByFn(10, 30);
+
+ assertThat(a).isEqualTo(b);
+ assertThat(a).isNotEqualTo(c);
+ assertThat(a).isNotEqualTo(d);
+ assertThat(d).isNotEqualTo(c);
+
+ assertThat(a.hashCode()).isEqualTo(b.hashCode());
+ assertThat(a.hashCode()).isNotEqualTo(c.hashCode());
+ assertThat(a.hashCode()).isNotEqualTo(d.hashCode());
+ assertThat(c.hashCode()).isNotEqualTo(d.hashCode());
+ }
+
+ @Test
+ public void testHashTo() {
+ String cid = "CID_abc_efg";
+
+ BloomFilter bloomFilter = BloomFilter.createByFn(10, 20);
+
+ BitsArray bits = BitsArray.create(bloomFilter.getM());
+
+ int[] bitPos = bloomFilter.calcBitPositions(cid);
+
+ bloomFilter.hashTo(cid, bits);
+
+ for (int bit : bitPos) {
+ assertThat(bits.getBit(bit)).isTrue();
+ }
+ }
+
+ @Test
+ public void testCalcBitPositions() {
+ String cid = "CID_abc_efg";
+
+ BloomFilter bloomFilter = BloomFilter.createByFn(10, 20);
+
+ int[] bitPos = bloomFilter.calcBitPositions(cid);
+
+ assertThat(bitPos).isNotNull();
+ assertThat(bitPos.length).isEqualTo(bloomFilter.getK());
+
+ int[] bitPos2 = bloomFilter.calcBitPositions(cid);
+
+ assertThat(bitPos2).isNotNull();
+ assertThat(bitPos2.length).isEqualTo(bloomFilter.getK());
+
+ assertThat(bitPos).isEqualTo(bitPos2);
+ }
+
+ @Test
+ public void testIsHit() {
+ String cid = "CID_abc_efg";
+ String cid2 = "CID_abc_123";
+
+ BloomFilter bloomFilter = BloomFilter.createByFn(10, 20);
+
+ BitsArray bits = BitsArray.create(bloomFilter.getM());
+
+ bloomFilter.hashTo(cid, bits);
+
+ assertThat(bloomFilter.isHit(cid, bits)).isTrue();
+ assertThat(!bloomFilter.isHit(cid2, bits)).isTrue();
+
+ bloomFilter.hashTo(cid2, bits);
+
+ assertThat(bloomFilter.isHit(cid, bits)).isTrue();
+ assertThat(bloomFilter.isHit(cid2, bits)).isTrue();
+ }
+
+ @Test
+ public void testBloomFilterData() {
+ BloomFilterData bloomFilterData = new BloomFilterData(new int[]{1, 2, 3}, 128);
+ BloomFilterData bloomFilterData1 = new BloomFilterData(new int[]{1, 2, 3}, 128);
+ BloomFilterData bloomFilterData2 = new BloomFilterData(new int[]{1, 2, 3}, 129);
+
+ assertThat(bloomFilterData).isEqualTo(bloomFilterData1);
+ assertThat(bloomFilterData2).isNotEqualTo(bloomFilterData);
+ assertThat(bloomFilterData2).isNotEqualTo(bloomFilterData1);
+
+ assertThat(bloomFilterData.hashCode()).isEqualTo(bloomFilterData1.hashCode());
+ assertThat(bloomFilterData2.hashCode()).isNotEqualTo(bloomFilterData.hashCode());
+ assertThat(bloomFilterData2.hashCode()).isNotEqualTo(bloomFilterData1.hashCode());
+
+ assertThat(bloomFilterData.getBitPos()).isEqualTo(bloomFilterData2.getBitPos());
+ assertThat(bloomFilterData.getBitNum()).isEqualTo(bloomFilterData1.getBitNum());
+ assertThat(bloomFilterData.getBitNum()).isNotEqualTo(bloomFilterData2.getBitNum());
+
+ bloomFilterData2.setBitNum(128);
+
+ assertThat(bloomFilterData).isEqualTo(bloomFilterData2);
+
+ bloomFilterData2.setBitPos(new int[]{1, 2, 3, 4});
+
+ assertThat(bloomFilterData).isNotEqualTo(bloomFilterData2);
+
+ BloomFilterData nullData = new BloomFilterData();
+
+ assertThat(nullData.getBitNum()).isEqualTo(0);
+ assertThat(nullData.getBitPos()).isNull();
+
+ BloomFilter bloomFilter = BloomFilter.createByFn(1, 300);
+
+ assertThat(bloomFilter).isNotNull();
+ assertThat(bloomFilter.isValid(bloomFilterData)).isFalse();
+ }
+
+ @Test
+ public void testCheckFalseHit() {
+ BloomFilter bloomFilter = BloomFilter.createByFn(1, 300);
+ BitsArray bits = BitsArray.create(bloomFilter.getM());
+ int falseHit = 0;
+ for (int i = 0; i < bloomFilter.getN(); i++) {
+ String str = randomString((new Random(System.nanoTime())).nextInt(127) + 10);
+ int[] bitPos = bloomFilter.calcBitPositions(str);
+
+ if (bloomFilter.checkFalseHit(bitPos, bits)) {
+ falseHit++;
+ }
+
+ bloomFilter.hashTo(bitPos, bits);
+ }
+
+ assertThat(falseHit).isLessThanOrEqualTo(bloomFilter.getF() * bloomFilter.getN() / 100);
+ }
+
+ private String randomString(int length) {
+ StringBuilder stringBuilder = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ stringBuilder.append((char) ((new Random(System.nanoTime())).nextInt(123 - 97) + 97));
+ }
+
+ return stringBuilder.toString();
+ }
+}
diff --git a/filter/src/test/java/org/apache/rocketmq/filter/ExpressionTest.java b/filter/src/test/java/org/apache/rocketmq/filter/ExpressionTest.java
new file mode 100644
index 0000000000..0ee81c98a2
--- /dev/null
+++ b/filter/src/test/java/org/apache/rocketmq/filter/ExpressionTest.java
@@ -0,0 +1,594 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.filter.expression.ComparisonExpression;
+import org.apache.rocketmq.filter.expression.ConstantExpression;
+import org.apache.rocketmq.filter.expression.EvaluationContext;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.expression.PropertyExpression;
+import org.apache.rocketmq.filter.parser.SelectorParser;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ExpressionTest {
+
+ private static String andExpression = "a=3 and b<>4 And c>5 AND d<=4";
+ private static String orExpression = "a=3 or b<>4 Or c>5 OR d<=4";
+ private static String inExpression = "a in ('3', '4', '5')";
+ private static String notInExpression = "a not in ('3', '4', '5')";
+ private static String betweenExpression = "a between 2 and 10";
+ private static String notBetweenExpression = "a not between 2 and 10";
+ private static String isNullExpression = "a is null";
+ private static String isNotNullExpression = "a is not null";
+ private static String equalExpression = "a is not null and a='hello'";
+ private static String booleanExpression = "a=TRUE OR b=FALSE";
+ private static String nullOrExpression = "a is null OR a='hello'";
+ private static String stringHasString = "TAGS is not null and TAGS='''''tag'''''";
+
+ @Test
+ public void testEvaluate_stringHasString() {
+ Expression expr = genExp(stringHasString);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("TAGS", "''tag''")
+ );
+
+ eval(expr, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_now() {
+ EvaluationContext context = genContext(
+ KeyValue.c("a", System.currentTimeMillis())
+ );
+
+ Expression nowExpression = ConstantExpression.createNow();
+ Expression propertyExpression = new PropertyExpression("a");
+
+ Expression expression = ComparisonExpression.createLessThanEqual(propertyExpression,
+ nowExpression);
+
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_stringCompare() {
+ Expression expression = genExp("a between up and low");
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "3.14")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+
+ {
+ context = genContext(
+ KeyValue.c("a", "3.14"),
+ KeyValue.c("up", "up"),
+ KeyValue.c("low", "low")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ {
+ expression = genExp("key is not null and key between 0 and 100");
+
+ context = genContext(
+ KeyValue.c("key", "con")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ {
+ expression = genExp("a between 0 and 100");
+
+ context = genContext(
+ KeyValue.c("a", "abc")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ {
+ expression = genExp("a=b");
+
+ context = genContext(
+ KeyValue.c("a", "3.14"),
+ KeyValue.c("b", "3.14")
+ );
+
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ {
+ expression = genExp("a<>b");
+
+ context = genContext(
+ KeyValue.c("a", "3.14"),
+ KeyValue.c("b", "3.14")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ {
+ expression = genExp("a<>b");
+
+ context = genContext(
+ KeyValue.c("a", "3.14"),
+ KeyValue.c("b", "3.141")
+ );
+
+ eval(expression, context, Boolean.TRUE);
+ }
+ }
+
+ @Test
+ public void testEvaluate_exponent() {
+ Expression expression = genExp("a > 3.1E10");
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", String.valueOf(3.1415 * Math.pow(10, 10)))
+ );
+
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_floatNumber() {
+ Expression expression = genExp("a > 3.14");
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", String.valueOf(3.1415))
+ );
+
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_twoVariable() {
+ Expression expression = genExp("a > b");
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", String.valueOf(10)),
+ KeyValue.c("b", String.valueOf(20))
+ );
+
+ eval(expression, context, Boolean.FALSE);
+
+ context = genContext(
+ KeyValue.c("b", String.valueOf(10)),
+ KeyValue.c("a", String.valueOf(20))
+ );
+
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_nullOr() {
+ Expression expression = genExp(nullOrExpression);
+
+ EvaluationContext context = genContext(
+ );
+
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "hello")
+ );
+
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "abc")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_boolean() {
+ Expression expression = genExp(booleanExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "true"),
+ KeyValue.c("b", "false")
+ );
+
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "false"),
+ KeyValue.c("b", "true")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_equal() {
+ Expression expression = genExp(equalExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "hello")
+ );
+
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_andTrue() {
+ Expression expression = genExp(andExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", 3),
+ KeyValue.c("b", 5),
+ KeyValue.c("c", 6),
+ KeyValue.c("d", 1)
+ );
+
+ for (int i = 0; i < 500; i++) {
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ long start = System.currentTimeMillis();
+ for (int j = 0; j < 100; j++) {
+ for (int i = 0; i < 1000; i++) {
+ eval(expression, context, Boolean.TRUE);
+ }
+ }
+
+ // use string
+ context = genContext(
+ KeyValue.c("a", "3"),
+ KeyValue.c("b", "5"),
+ KeyValue.c("c", "6"),
+ KeyValue.c("d", "1")
+ );
+
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_andFalse() {
+ Expression expression = genExp(andExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", 4),
+ KeyValue.c("b", 5),
+ KeyValue.c("c", 6),
+ KeyValue.c("d", 1)
+ );
+
+ eval(expression, context, Boolean.FALSE);
+
+ // use string
+ context = genContext(
+ KeyValue.c("a", "4"),
+ KeyValue.c("b", "5"),
+ KeyValue.c("c", "6"),
+ KeyValue.c("d", "1")
+ );
+
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_orTrue() {
+ Expression expression = genExp(orExpression);
+
+ // first
+ EvaluationContext context = genContext(
+ KeyValue.c("a", 3)
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ // second
+ context = genContext(
+ KeyValue.c("a", 4),
+ KeyValue.c("b", 5)
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ // third
+ context = genContext(
+ KeyValue.c("a", 4),
+ KeyValue.c("b", 4),
+ KeyValue.c("c", 6)
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ // forth
+ context = genContext(
+ KeyValue.c("a", 4),
+ KeyValue.c("b", 4),
+ KeyValue.c("c", 3),
+ KeyValue.c("d", 2)
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_orFalse() {
+ Expression expression = genExp(orExpression);
+ // forth
+ EvaluationContext context = genContext(
+ KeyValue.c("a", 4),
+ KeyValue.c("b", 4),
+ KeyValue.c("c", 3),
+ KeyValue.c("d", 10)
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_inTrue() {
+ Expression expression = genExp(inExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "3")
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "4")
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "5")
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_inFalse() {
+ Expression expression = genExp(inExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "8")
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_notInTrue() {
+ Expression expression = genExp(notInExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "8")
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_notInFalse() {
+ Expression expression = genExp(notInExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "3")
+ );
+ eval(expression, context, Boolean.FALSE);
+
+ context = genContext(
+ KeyValue.c("a", "4")
+ );
+ eval(expression, context, Boolean.FALSE);
+
+ context = genContext(
+ KeyValue.c("a", "5")
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_betweenTrue() {
+ Expression expression = genExp(betweenExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "2")
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "10")
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "3")
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_betweenFalse() {
+ Expression expression = genExp(betweenExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "1")
+ );
+ eval(expression, context, Boolean.FALSE);
+
+ context = genContext(
+ KeyValue.c("a", "11")
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_notBetweenTrue() {
+ Expression expression = genExp(notBetweenExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "1")
+ );
+ eval(expression, context, Boolean.TRUE);
+
+ context = genContext(
+ KeyValue.c("a", "11")
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_notBetweenFalse() {
+ Expression expression = genExp(notBetweenExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "2")
+ );
+ eval(expression, context, Boolean.FALSE);
+
+ context = genContext(
+ KeyValue.c("a", "10")
+ );
+ eval(expression, context, Boolean.FALSE);
+
+ context = genContext(
+ KeyValue.c("a", "3")
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_isNullTrue() {
+ Expression expression = genExp(isNullExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("abc", "2")
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_isNullFalse() {
+ Expression expression = genExp(isNullExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "2")
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ @Test
+ public void testEvaluate_isNotNullTrue() {
+ Expression expression = genExp(isNotNullExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("a", "2")
+ );
+ eval(expression, context, Boolean.TRUE);
+ }
+
+ @Test
+ public void testEvaluate_isNotNullFalse() {
+ Expression expression = genExp(isNotNullExpression);
+
+ EvaluationContext context = genContext(
+ KeyValue.c("abc", "2")
+ );
+ eval(expression, context, Boolean.FALSE);
+ }
+
+ protected void eval(Expression expression, EvaluationContext context, Boolean result) {
+ Object ret = null;
+ try {
+ ret = expression.evaluate(context);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+ if (ret == null || !(ret instanceof Boolean)) {
+ assertThat(result).isFalse();
+ } else {
+ assertThat(result).isEqualTo(ret);
+ }
+ }
+
+ protected EvaluationContext genContext(KeyValue... keyValues) {
+ if (keyValues == null || keyValues.length < 1) {
+ return new PropertyContext();
+ }
+
+ PropertyContext context = new PropertyContext();
+ for (KeyValue keyValue : keyValues) {
+ context.properties.put(keyValue.key, keyValue.value);
+ }
+
+ return context;
+ }
+
+ protected Expression genExp(String exp) {
+ Expression expression = null;
+
+ try {
+ expression = SelectorParser.parse(exp);
+
+ assertThat(expression).isNotNull();
+ } catch (MQFilterException e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ return expression;
+ }
+
+ static class KeyValue {
+ public static KeyValue c(String key, Object value) {
+ return new KeyValue(key, value);
+ }
+
+ public KeyValue(String key, Object value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String key;
+ public Object value;
+ }
+
+ class PropertyContext implements EvaluationContext {
+
+ public Map properties = new HashMap(8);
+
+ @Override
+ public Object get(final String name) {
+ return properties.get(name);
+ }
+
+ @Override
+ public Map keyValues() {
+ return properties;
+ }
+
+ }
+}
diff --git a/filter/src/test/java/org/apache/rocketmq/filter/FilterSpiTest.java b/filter/src/test/java/org/apache/rocketmq/filter/FilterSpiTest.java
new file mode 100644
index 0000000000..22eeb8603e
--- /dev/null
+++ b/filter/src/test/java/org/apache/rocketmq/filter/FilterSpiTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.common.filter.ExpressionType;
+import org.apache.rocketmq.filter.expression.EmptyEvaluationContext;
+import org.apache.rocketmq.filter.expression.EvaluationContext;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FilterSpiTest {
+
+ static class NothingExpression implements Expression {
+
+ @Override
+ public Object evaluate(final EvaluationContext context) throws Exception {
+ return Boolean.TRUE;
+ }
+ }
+
+ static class NothingFilter implements FilterSpi {
+ @Override
+ public Expression compile(final String expr) throws MQFilterException {
+ return new NothingExpression();
+ }
+
+ @Override
+ public String ofType() {
+ return "Nothing";
+ }
+ }
+
+
+ @Test
+ public void testRegister() {
+ FilterFactory.INSTANCE.register(new NothingFilter());
+
+ Expression expr = null;
+ try {
+ expr = FilterFactory.INSTANCE.get("Nothing").compile("abc");
+ } catch (MQFilterException e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ assertThat(expr).isNotNull();
+
+ try {
+ assertThat((Boolean) expr.evaluate(new EmptyEvaluationContext())).isTrue();
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+ }
+
+ @Test
+ public void testGet() {
+ try {
+ assertThat((Boolean) FilterFactory.INSTANCE.get(ExpressionType.SQL92).compile("a is not null and a > 0")
+ .evaluate(new EmptyEvaluationContext())).isFalse();
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+ }
+}
diff --git a/filter/src/test/java/org/apache/rocketmq/filter/ParserTest.java b/filter/src/test/java/org/apache/rocketmq/filter/ParserTest.java
new file mode 100644
index 0000000000..36ef2714fc
--- /dev/null
+++ b/filter/src/test/java/org/apache/rocketmq/filter/ParserTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.filter;
+
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.parser.SelectorParser;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ParserTest {
+
+ private static String andExpression = "a=3 and b<>4 And c>5 AND d<=4";
+ private static String andExpressionHasBlank = "a=3 and b<>4 And c>5 AND d<=4";
+ private static String orExpression = "a=3 or b<>4 Or c>5 OR d<=4";
+ private static String inExpression = "a in ('3', '4', '5')";
+ private static String notInExpression = "(a not in ('6', '4', '5')) or (b in ('3', '4', '5'))";
+ private static String betweenExpression = "(a between 2 and 10) AND (b not between 6 and 9)";
+ private static String equalNullExpression = "a is null";
+ private static String notEqualNullExpression = "a is not null";
+ private static String nowExpression = "a <= now";
+
+ private static String invalidExpression = "a and between 2 and 10";
+ private static String illegalBetween = " a between 10 and 0";
+
+ @Test
+ public void testParse_valid() {
+ for (String expr : Arrays.asList(
+ andExpression, orExpression, inExpression, notInExpression, betweenExpression,
+ equalNullExpression, notEqualNullExpression, nowExpression
+ )) {
+
+ try {
+ Expression expression = SelectorParser.parse(expr);
+ assertThat(expression).isNotNull();
+ } catch (MQFilterException e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ }
+ }
+
+ @Test
+ public void testParse_invalid() {
+ try {
+ SelectorParser.parse(invalidExpression);
+
+ assertThat(Boolean.TRUE).isFalse();
+ } catch (MQFilterException e) {
+ }
+ }
+
+ @Test
+ public void testParse_decimalOverFlow() {
+ try {
+ String str = "100000000000000000000000";
+
+ SelectorParser.parse("a > " + str);
+
+ assertThat(Boolean.TRUE).isFalse();
+ } catch (Exception e) {
+ }
+ }
+
+ @Test
+ public void testParse_floatOverFlow() {
+ try {
+ String str = "1";
+ for (int i = 0; i < 2048; i++) {
+ str += "111111111111111111111111111111111111111111111111111";
+ }
+ str += ".";
+ for (int i = 0; i < 2048; i++) {
+ str += "111111111111111111111111111111111111111111111111111";
+ }
+
+ SelectorParser.parse("a > " + str);
+
+ assertThat(Boolean.TRUE).isFalse();
+ } catch (Exception e) {
+ }
+ }
+
+ @Test
+ public void testParse_illegalBetween() {
+ try {
+ SelectorParser.parse(illegalBetween);
+
+ assertThat(Boolean.TRUE).isFalse();
+ } catch (Exception e) {
+ }
+ }
+
+ @Test
+ public void testEquals() {
+ try {
+ Expression expr1 = SelectorParser.parse(andExpression);
+
+ Expression expr2 = SelectorParser.parse(andExpressionHasBlank);
+
+ Expression expr3 = SelectorParser.parse(orExpression);
+
+ assertThat(expr1).isEqualTo(expr2);
+ assertThat(expr1).isNotEqualTo(expr3);
+ } catch (MQFilterException e) {
+ e.printStackTrace();
+ assertThat(Boolean.TRUE).isFalse();
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 47df84d42b..feb8b14845 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,6 +178,7 @@
examplefiltersrvsrvutil
+ filtertestdistribution
@@ -553,6 +554,11 @@
rocketmq-srvutil${project.version}
+
+ org.apache.rocketmq
+ rocketmq-filter
+ ${project.version}
+ ${project.groupId}rocketmq-example
@@ -603,6 +609,11 @@
commons-lang33.4
+
+ com.google.guava
+ guava
+ 19.0
+
diff --git a/srvutil/pom.xml b/srvutil/pom.xml
index 32699035ca..6dc0377fdb 100644
--- a/srvutil/pom.xml
+++ b/srvutil/pom.xml
@@ -41,5 +41,9 @@
commons-clicommons-cli
+
+ com.google.guava
+ guava
+
diff --git a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
index 5be8258e9f..7841febf70 100644
--- a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
+++ b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
@@ -314,10 +314,11 @@ public class CommitLog {
// 17 properties
short propertiesLength = byteBuffer.getShort();
+ Map propertiesMap = null;
if (propertiesLength > 0) {
byteBuffer.get(bytesContent, 0, propertiesLength);
String properties = new String(bytesContent, 0, propertiesLength, MessageDecoder.CHARSET_UTF8);
- Map propertiesMap = MessageDecoder.string2messageProperties(properties);
+ propertiesMap = MessageDecoder.string2messageProperties(properties);
keys = propertiesMap.get(MessageConst.PROPERTY_KEYS);
@@ -369,8 +370,9 @@ public class CommitLog {
queueOffset, // 7
keys, // 8
uniqKey, //9
- sysFlag, // 9
- preparedTransactionOffset// 10
+ sysFlag, // 10
+ preparedTransactionOffset, // 11
+ propertiesMap // 12
);
} catch (Exception e) {
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/CommitLogDispatcher.java b/store/src/main/java/org/apache/rocketmq/store/CommitLogDispatcher.java
new file mode 100644
index 0000000000..e1564a9ef5
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/CommitLogDispatcher.java
@@ -0,0 +1,26 @@
+/*
+ * 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.store;
+
+/**
+ * Dispatcher of commit log.
+ */
+public interface CommitLogDispatcher {
+
+ void dispatch(final DispatchRequest request);
+}
diff --git a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
index 919c6372ee..d03ff0f32e 100644
--- a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
+++ b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueue.java
@@ -20,6 +20,7 @@ import java.io.File;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.store.config.StorePathConfigHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,6 +41,7 @@ public class ConsumeQueue {
private final int mappedFileSize;
private long maxPhysicOffset = -1;
private volatile long minLogicOffset = 0;
+ private ConsumeQueueExt consumeQueueExt = null;
public ConsumeQueue(
final String topic,
@@ -61,11 +63,24 @@ public class ConsumeQueue {
this.mappedFileQueue = new MappedFileQueue(queueDir, mappedFileSize, null);
this.byteBufferIndex = ByteBuffer.allocate(CQ_STORE_UNIT_SIZE);
+
+ if (defaultMessageStore.getMessageStoreConfig().isEnableConsumeQueueExt()) {
+ this.consumeQueueExt = new ConsumeQueueExt(
+ topic,
+ queueId,
+ StorePathConfigHelper.getStorePathConsumeQueueExt(defaultMessageStore.getMessageStoreConfig().getStorePathRootDir()),
+ defaultMessageStore.getMessageStoreConfig().getMappedFileSizeConsumeQueueExt(),
+ defaultMessageStore.getMessageStoreConfig().getBitMapLengthConsumeQueueExt()
+ );
+ }
}
public boolean load() {
boolean result = this.mappedFileQueue.load();
log.info("load consume queue " + this.topic + "-" + this.queueId + " " + (result ? "OK" : "Failed"));
+ if (isExtReadEnable()) {
+ result &= this.consumeQueueExt.load();
+ }
return result;
}
@@ -82,6 +97,7 @@ public class ConsumeQueue {
ByteBuffer byteBuffer = mappedFile.sliceByteBuffer();
long processOffset = mappedFile.getFileFromOffset();
long mappedFileOffset = 0;
+ long maxExtAddr = 1;
while (true) {
for (int i = 0; i < mappedFileSizeLogics; i += CQ_STORE_UNIT_SIZE) {
long offset = byteBuffer.getLong();
@@ -91,6 +107,9 @@ public class ConsumeQueue {
if (offset >= 0 && size > 0) {
mappedFileOffset = i + CQ_STORE_UNIT_SIZE;
this.maxPhysicOffset = offset;
+ if (isExtAddr(tagsCode)) {
+ maxExtAddr = tagsCode;
+ }
} else {
log.info("recover current consume queue file over, " + mappedFile.getFileName() + " "
+ offset + " " + size + " " + tagsCode);
@@ -123,6 +142,12 @@ public class ConsumeQueue {
this.mappedFileQueue.setFlushedWhere(processOffset);
this.mappedFileQueue.setCommittedWhere(processOffset);
this.mappedFileQueue.truncateDirtyFiles(processOffset);
+
+ if (isExtReadEnable()) {
+ this.consumeQueueExt.recover();
+ log.info("Truncate consume queue extend file by max {}", maxExtAddr);
+ this.consumeQueueExt.truncateByMaxAddress(maxExtAddr);
+ }
}
}
@@ -200,7 +225,7 @@ public class ConsumeQueue {
int logicFileSize = this.mappedFileSize;
this.maxPhysicOffset = phyOffet - 1;
-
+ long maxExtAddr = 1;
while (true) {
MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();
if (mappedFile != null) {
@@ -213,7 +238,7 @@ public class ConsumeQueue {
for (int i = 0; i < logicFileSize; i += CQ_STORE_UNIT_SIZE) {
long offset = byteBuffer.getLong();
int size = byteBuffer.getInt();
- byteBuffer.getLong();
+ long tagsCode = byteBuffer.getLong();
if (0 == i) {
if (offset >= phyOffet) {
@@ -225,6 +250,10 @@ public class ConsumeQueue {
mappedFile.setCommittedPosition(pos);
mappedFile.setFlushedPosition(pos);
this.maxPhysicOffset = offset;
+ // This maybe not take effect, when not every consume queue has extend file.
+ if (isExtAddr(tagsCode)) {
+ maxExtAddr = tagsCode;
+ }
}
} else {
@@ -239,6 +268,9 @@ public class ConsumeQueue {
mappedFile.setCommittedPosition(pos);
mappedFile.setFlushedPosition(pos);
this.maxPhysicOffset = offset;
+ if (isExtAddr(tagsCode)) {
+ maxExtAddr = tagsCode;
+ }
if (pos == logicFileSize) {
return;
@@ -252,6 +284,10 @@ public class ConsumeQueue {
break;
}
}
+
+ if (isExtReadEnable()) {
+ this.consumeQueueExt.truncateByMaxAddress(maxExtAddr);
+ }
}
public long getLastOffset() {
@@ -285,7 +321,12 @@ public class ConsumeQueue {
}
public boolean flush(final int flushLeastPages) {
- return this.mappedFileQueue.flush(flushLeastPages);
+ boolean result = this.mappedFileQueue.flush(flushLeastPages);
+ if (isExtReadEnable()) {
+ result = result & this.consumeQueueExt.flush(flushLeastPages);
+ }
+
+ return result;
}
public int deleteExpiredFile(long offset) {
@@ -296,6 +337,7 @@ public class ConsumeQueue {
public void correctMinOffset(long phyMinOffset) {
MappedFile mappedFile = this.mappedFileQueue.getFirstMappedFile();
+ long minExtAddr = 1;
if (mappedFile != null) {
SelectMappedBufferResult result = mappedFile.selectMappedBuffer(0);
if (result != null) {
@@ -303,12 +345,16 @@ public class ConsumeQueue {
for (int i = 0; i < result.getSize(); i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = result.getByteBuffer().getLong();
result.getByteBuffer().getInt();
- result.getByteBuffer().getLong();
+ long tagsCode = result.getByteBuffer().getLong();
if (offsetPy >= phyMinOffset) {
this.minLogicOffset = result.getMappedFile().getFileFromOffset() + i;
log.info("Compute logical min offset: {}, topic: {}, queueId: {}",
this.getMinOffsetInQueue(), this.topic, this.queueId);
+ // This maybe not take effect, when not every consume queue has extend file.
+ if (isExtAddr(tagsCode)) {
+ minExtAddr = tagsCode;
+ }
break;
}
}
@@ -319,24 +365,43 @@ public class ConsumeQueue {
}
}
}
+
+ if (isExtReadEnable()) {
+ this.consumeQueueExt.truncateByMinAddress(minExtAddr);
+ }
}
public long getMinOffsetInQueue() {
return this.minLogicOffset / CQ_STORE_UNIT_SIZE;
}
- public void putMessagePositionInfoWrapper(long offset, int size, long tagsCode, long storeTimestamp,
- long logicOffset) {
+ public void putMessagePositionInfoWrapper(DispatchRequest request) {
final int maxRetries = 30;
boolean canWrite = this.defaultMessageStore.getRunningFlags().isCQWriteable();
for (int i = 0; i < maxRetries && canWrite; i++) {
- boolean result = this.putMessagePositionInfo(offset, size, tagsCode, logicOffset);
+ long tagsCode = request.getTagsCode();
+ if (isExtWriteEnable()) {
+ ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
+ cqExtUnit.setFilterBitMap(request.getBitMap());
+ cqExtUnit.setMsgStoreTime(request.getStoreTimestamp());
+ cqExtUnit.setTagsCode(request.getTagsCode());
+
+ long extAddr = this.consumeQueueExt.put(cqExtUnit);
+ if (isExtAddr(extAddr)) {
+ tagsCode = extAddr;
+ } else {
+ log.warn("Save consume queue extend fail, So just save tagsCode! {}, topic:{}, queueId:{}, offset:{}", cqExtUnit,
+ topic, queueId, request.getCommitLogOffset());
+ }
+ }
+ boolean result = this.putMessagePositionInfo(request.getCommitLogOffset(),
+ request.getMsgSize(), tagsCode, request.getConsumeQueueOffset());
if (result) {
- this.defaultMessageStore.getStoreCheckpoint().setLogicsMsgTimestamp(storeTimestamp);
+ this.defaultMessageStore.getStoreCheckpoint().setLogicsMsgTimestamp(request.getStoreTimestamp());
return;
} else {
// XXX: warn and notify me
- log.warn("[BUG]put commit log position info to " + topic + ":" + queueId + " " + offset
+ log.warn("[BUG]put commit log position info to " + topic + ":" + queueId + " " + request.getCommitLogOffset()
+ " failed, retry " + i + " times");
try {
@@ -423,6 +488,20 @@ public class ConsumeQueue {
return null;
}
+ public ConsumeQueueExt.CqExtUnit getExt(final long offset) {
+ if (isExtReadEnable()) {
+ return this.consumeQueueExt.get(offset);
+ }
+ return null;
+ }
+
+ public boolean getExt(final long offset, ConsumeQueueExt.CqExtUnit cqExtUnit) {
+ if (isExtReadEnable()) {
+ return this.consumeQueueExt.get(offset, cqExtUnit);
+ }
+ return false;
+ }
+
public long getMinLogicOffset() {
return minLogicOffset;
}
@@ -457,6 +536,9 @@ public class ConsumeQueue {
this.maxPhysicOffset = -1;
this.minLogicOffset = 0;
this.mappedFileQueue.destroy();
+ if (isExtReadEnable()) {
+ this.consumeQueueExt.destroy();
+ }
}
public long getMessageTotalInQueue() {
@@ -469,5 +551,27 @@ public class ConsumeQueue {
public void checkSelf() {
mappedFileQueue.checkSelf();
+ if (isExtReadEnable()) {
+ this.consumeQueueExt.checkSelf();
+ }
+ }
+
+ protected boolean isExtReadEnable() {
+ return this.consumeQueueExt != null;
+ }
+
+ protected boolean isExtWriteEnable() {
+ return this.consumeQueueExt != null
+ && this.defaultMessageStore.getMessageStoreConfig().isEnableConsumeQueueExt();
+ }
+
+ /**
+ * Check {@code tagsCode} is address of extend file or tags code.
+ *
+ * @param tagsCode
+ * @return
+ */
+ public boolean isExtAddr(long tagsCode) {
+ return isExtReadEnable() && this.consumeQueueExt.isExtAddr(tagsCode);
}
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/ConsumeQueueExt.java b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueueExt.java
new file mode 100644
index 0000000000..1a177e99d5
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/ConsumeQueueExt.java
@@ -0,0 +1,638 @@
+/*
+ * 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.store;
+
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Extend of consume queue, to store something not important,
+ * such as message store time, filter bit map and etc.
+ *
+ *
1. This class is used only by {@link ConsumeQueue}
+ *
2. And is week reliable.
+ *
3. Be careful, address returned is always less than 0.
+ *
4. Pls keep this file small.
+ */
+public class ConsumeQueueExt {
+ private static final Logger log = LoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);
+
+ private final MappedFileQueue mappedFileQueue;
+ private final String topic;
+ private final int queueId;
+
+ private final String storePath;
+ private final int mappedFileSize;
+ private ByteBuffer tempContainer;
+
+ public static final int END_BLANK_DATA_LENGTH = 4;
+
+ /**
+ * Addr can not exceed this value.For compatible.
+ */
+ public static final long MAX_ADDR = Integer.MIN_VALUE - 1L;
+ public static final long MAX_REAL_OFFSET = MAX_ADDR - Long.MIN_VALUE;
+
+ /**
+ * Constructor.
+ *
+ * @param topic topic
+ * @param queueId id of queue
+ * @param storePath root dir of files to store.
+ * @param mappedFileSize file size
+ * @param bitMapLength bit map length.
+ */
+ public ConsumeQueueExt(final String topic,
+ final int queueId,
+ final String storePath,
+ final int mappedFileSize,
+ final int bitMapLength) {
+
+ this.storePath = storePath;
+ this.mappedFileSize = mappedFileSize;
+
+ this.topic = topic;
+ this.queueId = queueId;
+
+ String queueDir = this.storePath
+ + File.separator + topic
+ + File.separator + queueId;
+
+ this.mappedFileQueue = new MappedFileQueue(queueDir, mappedFileSize, null);
+
+ if (bitMapLength > 0) {
+ this.tempContainer = ByteBuffer.allocate(
+ bitMapLength / Byte.SIZE
+ );
+ }
+ }
+
+ /**
+ * Check whether {@code address} point to extend file.
+ *
+ * Just test {@code address} is less than 0.
+ *
+ *
+ * @param address
+ * @return
+ */
+ public boolean isExtAddr(final long address) {
+ return address <= MAX_ADDR;
+ }
+
+ /**
+ * Transform {@code address}(decorated by {@link #decorate}) to offset in mapped file.
+ *
+ * if {@code address} is less than 0, return {@code address} - {@link java.lang.Long#MIN_VALUE};
+ * else, just return {@code address}
+ *
+ *
+ * @param address
+ * @return
+ */
+ public long unDecorate(final long address) {
+ if (isExtAddr(address)) {
+ return address - Long.MIN_VALUE;
+ }
+ return address;
+ }
+
+ /**
+ * Decorate {@code offset} from mapped file, in order to distinguish with tagsCode(saved in cq originally).
+ *
+ * if {@code offset} is greater than or equal to 0, then return {@code offset} + {@link java.lang.Long#MIN_VALUE};
+ * else, just return {@code offset}
+ *
+ *
+ * @param offset
+ * @return ext address(value is less than 0)
+ */
+ public long decorate(final long offset) {
+ if (!isExtAddr(offset)) {
+ return offset + Long.MIN_VALUE;
+ }
+ return offset;
+ }
+
+ /**
+ * Get data from buffer.
+ *
+ * @param address less than 0
+ * @return
+ */
+ public CqExtUnit get(final long address) {
+ CqExtUnit cqExtUnit = new CqExtUnit();
+ if (get(address, cqExtUnit)) {
+ return cqExtUnit;
+ }
+
+ return null;
+ }
+
+ /**
+ * Get data from buffer, and set to {@code cqExtUnit}
+ *
+ * @param address less than 0
+ * @param cqExtUnit
+ * @return
+ */
+ public boolean get(final long address, final CqExtUnit cqExtUnit) {
+ if (!isExtAddr(address)) {
+ return false;
+ }
+
+ final int mappedFileSize = this.mappedFileSize;
+ final long realOffset = unDecorate(address);
+
+ MappedFile mappedFile = this.mappedFileQueue.findMappedFileByOffset(realOffset, realOffset == 0);
+ if (mappedFile == null) {
+ return false;
+ }
+
+ int pos = (int) (realOffset % mappedFileSize);
+
+ SelectMappedBufferResult bufferResult = mappedFile.selectMappedBuffer(pos);
+ if (bufferResult == null) {
+ log.warn("[BUG] Consume queue extend unit({}) is not found!", realOffset);
+ return false;
+ }
+ boolean ret = false;
+ try {
+ ret = cqExtUnit.read(bufferResult.getByteBuffer());
+ } finally {
+ bufferResult.release();
+ }
+
+ return ret;
+ }
+
+ /**
+ * Save to mapped buffer of file and return address.
+ *
+ * Be careful, this method is not thread safe.
+ *
+ *
+ * @param cqExtUnit
+ * @return success: < 0: fail: >=0
+ */
+ public long put(final CqExtUnit cqExtUnit) {
+ final int retryTimes = 3;
+ try {
+ int size = cqExtUnit.calcUnitSize();
+ if (size > CqExtUnit.MAX_EXT_UNIT_SIZE) {
+ log.error("Size of cq ext unit is greater than {}, {}", CqExtUnit.MAX_EXT_UNIT_SIZE, cqExtUnit);
+ return 1;
+ }
+ if (this.mappedFileQueue.getMaxOffset() + size > MAX_REAL_OFFSET) {
+ log.warn("Capacity of ext is maximum!{}, {}", this.mappedFileQueue.getMaxOffset(), size);
+ return 1;
+ }
+ // unit size maybe change.but, the same most of the time.
+ if (this.tempContainer == null || this.tempContainer.capacity() < size) {
+ this.tempContainer = ByteBuffer.allocate(size);
+ }
+
+ for (int i = 0; i < retryTimes; i++) {
+ MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();
+
+ if (mappedFile == null || mappedFile.isFull()) {
+ mappedFile = this.mappedFileQueue.getLastMappedFile(0);
+ }
+
+ if (mappedFile == null) {
+ log.error("Create mapped file when save consume queue extend, {}", cqExtUnit);
+ continue;
+ }
+ final int wrotePosition = mappedFile.getWrotePosition();
+ final int blankSize = this.mappedFileSize - wrotePosition - END_BLANK_DATA_LENGTH;
+
+ // check whether has enough space.
+ if (size > blankSize) {
+ fullFillToEnd(mappedFile, wrotePosition);
+ log.info("No enough space(need:{}, has:{}) of file {}, so fill to end",
+ size, blankSize, mappedFile.getFileName());
+ continue;
+ }
+
+ if (mappedFile.appendMessage(cqExtUnit.write(this.tempContainer), 0, size)) {
+ return decorate(wrotePosition + mappedFile.getFileFromOffset());
+ }
+ }
+ } catch (Throwable e) {
+ log.error("Save consume queue extend error, " + cqExtUnit, e);
+ }
+
+ return 1;
+ }
+
+ protected void fullFillToEnd(final MappedFile mappedFile, final int wrotePosition) {
+ ByteBuffer mappedFileBuffer = mappedFile.sliceByteBuffer();
+ mappedFileBuffer.position(wrotePosition);
+
+ // ending.
+ mappedFileBuffer.putShort((short) -1);
+
+ mappedFile.setWrotePosition(this.mappedFileSize);
+ }
+
+ /**
+ * Load data from file when startup.
+ *
+ * @return
+ */
+ public boolean load() {
+ boolean result = this.mappedFileQueue.load();
+ log.info("load consume queue extend" + this.topic + "-" + this.queueId + " " + (result ? "OK" : "Failed"));
+ return result;
+ }
+
+ /**
+ * Check whether the step size in mapped file queue is correct.
+ */
+ public void checkSelf() {
+ this.mappedFileQueue.checkSelf();
+ }
+
+ /**
+ * Recover.
+ */
+ public void recover() {
+ final List mappedFiles = this.mappedFileQueue.getMappedFiles();
+ if (mappedFiles == null || mappedFiles.isEmpty()) {
+ return;
+ }
+
+ // load all files, consume queue will truncate extend files.
+ int index = 0;
+
+ MappedFile mappedFile = mappedFiles.get(index);
+ ByteBuffer byteBuffer = mappedFile.sliceByteBuffer();
+ long processOffset = mappedFile.getFileFromOffset();
+ long mappedFileOffset = 0;
+ CqExtUnit extUnit = new CqExtUnit();
+ while (true) {
+ extUnit.readBySkip(byteBuffer);
+
+ // check whether write sth.
+ if (extUnit.getSize() > 0) {
+ mappedFileOffset += extUnit.getSize();
+ continue;
+ }
+
+ index++;
+ if (index < mappedFiles.size()) {
+ mappedFile = mappedFiles.get(index);
+ byteBuffer = mappedFile.sliceByteBuffer();
+ processOffset = mappedFile.getFileFromOffset();
+ mappedFileOffset = 0;
+ log.info("Recover next consume queue extend file, " + mappedFile.getFileName());
+ continue;
+ }
+
+ log.info("All files of consume queue extend has been recovered over, last mapped file "
+ + mappedFile.getFileName());
+ break;
+ }
+
+ processOffset += mappedFileOffset;
+ this.mappedFileQueue.setFlushedWhere(processOffset);
+ this.mappedFileQueue.setCommittedWhere(processOffset);
+ this.mappedFileQueue.truncateDirtyFiles(processOffset);
+ }
+
+ /**
+ * Delete files before {@code minAddress}.
+ *
+ * @param minAddress less than 0
+ */
+ public void truncateByMinAddress(final long minAddress) {
+ if (!isExtAddr(minAddress)) {
+ return;
+ }
+
+ log.info("Truncate consume queue ext by min {}.", minAddress);
+
+ List willRemoveFiles = new ArrayList();
+
+ List mappedFiles = this.mappedFileQueue.getMappedFiles();
+ final long realOffset = unDecorate(minAddress);
+
+ for (MappedFile file : mappedFiles) {
+ long fileTailOffset = file.getFileFromOffset() + this.mappedFileSize;
+
+ if (fileTailOffset < realOffset) {
+ log.info("Destroy consume queue ext by min: file={}, fileTailOffset={}, minOffset={}", file.getFileName(),
+ fileTailOffset, realOffset);
+ if (file.destroy(1000)) {
+ willRemoveFiles.add(file);
+ }
+ }
+ }
+
+ this.mappedFileQueue.deleteExpiredFile(willRemoveFiles);
+ }
+
+ /**
+ * Delete files after {@code maxAddress}, and reset wrote/commit/flush position to last file.
+ *
+ * @param maxAddress less than 0
+ */
+ public void truncateByMaxAddress(final long maxAddress) {
+ if (!isExtAddr(maxAddress)) {
+ return;
+ }
+
+ log.info("Truncate consume queue ext by max {}.", maxAddress);
+
+ CqExtUnit cqExtUnit = get(maxAddress);
+ if (cqExtUnit == null) {
+ log.error("[BUG] address {} of consume queue extend not found!", maxAddress);
+ return;
+ }
+
+ final long realOffset = unDecorate(maxAddress);
+
+ this.mappedFileQueue.truncateDirtyFiles(realOffset + cqExtUnit.getSize());
+ }
+
+ /**
+ * flush buffer to file.
+ *
+ * @param flushLeastPages
+ * @return
+ */
+ public boolean flush(final int flushLeastPages) {
+ return this.mappedFileQueue.flush(flushLeastPages);
+ }
+
+ /**
+ * delete files and directory.
+ */
+ public void destroy() {
+ this.mappedFileQueue.destroy();
+ }
+
+ /**
+ * Max address(value is less than 0).
+ *
+ *
+ * Be careful: it's an address just when invoking this method.
+ *
+ *
+ * @return
+ */
+ public long getMaxAddress() {
+ MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();
+ if (mappedFile == null) {
+ return decorate(0);
+ }
+ return decorate(mappedFile.getFileFromOffset() + mappedFile.getWrotePosition());
+ }
+
+ /**
+ * Minus address saved in file.
+ *
+ * @return
+ */
+ public long getMinAddress() {
+ MappedFile firstFile = this.mappedFileQueue.getFirstMappedFile();
+ if (firstFile == null) {
+ return decorate(0);
+ }
+ return decorate(firstFile.getFileFromOffset());
+ }
+
+ /**
+ * Store unit.
+ */
+ public static class CqExtUnit {
+ public static final short MIN_EXT_UNIT_SIZE
+ = 2 * 1 // size, 32k max
+ + 8 * 2 // msg time + tagCode
+ + 2; // bitMapSize
+
+ public static final int MAX_EXT_UNIT_SIZE = Short.MAX_VALUE;
+
+ public CqExtUnit() {}
+
+ public CqExtUnit(Long tagsCode, long msgStoreTime, byte[] filterBitMap) {
+ this.tagsCode = tagsCode == null ? 0 : tagsCode;
+ this.msgStoreTime = msgStoreTime;
+ this.filterBitMap = filterBitMap;
+ this.bitMapSize = (short) (filterBitMap == null ? 0 : filterBitMap.length);
+ this.size = (short) (MIN_EXT_UNIT_SIZE + this.bitMapSize);
+ }
+
+ /**
+ * unit size
+ */
+ private short size;
+ /**
+ * has code of tags
+ */
+ private long tagsCode;
+ /**
+ * the time to store into commit log of message
+ */
+ private long msgStoreTime;
+ /**
+ * size of bit map
+ */
+ private short bitMapSize;
+ /**
+ * filter bit map
+ */
+ private byte[] filterBitMap;
+
+ /**
+ * build unit from buffer from current position.
+ *
+ * @param buffer
+ * @return
+ */
+ private boolean read(final ByteBuffer buffer) {
+ if (buffer.position() + 2 > buffer.limit()) {
+ return false;
+ }
+
+ this.size = buffer.getShort();
+
+ if (this.size < 1) {
+ return false;
+ }
+
+ this.tagsCode = buffer.getLong();
+ this.msgStoreTime = buffer.getLong();
+ this.bitMapSize = buffer.getShort();
+
+ if (this.bitMapSize < 1) {
+ return true;
+ }
+
+ if (this.filterBitMap == null || this.filterBitMap.length != this.bitMapSize) {
+ this.filterBitMap = new byte[bitMapSize];
+ }
+
+ buffer.get(this.filterBitMap);
+ return true;
+ }
+
+ /**
+ * Only read first 2 byte to get unit size.
+ *
+ * if size > 0, then skip buffer position with size.
+ *
1. @{code container} can be null, it will be created if null.
+ *
2. if capacity of @{code container} is less than unit size, it will be created also.
+ *
3. Pls be sure that size of unit is not greater than {@link #MAX_EXT_UNIT_SIZE}
+ *
+ * @param container
+ * @return
+ */
+ private byte[] write(final ByteBuffer container) {
+ this.bitMapSize = (short) (filterBitMap == null ? 0 : filterBitMap.length);
+ this.size = (short) (MIN_EXT_UNIT_SIZE + this.bitMapSize);
+
+ ByteBuffer temp = container;
+
+ if (temp == null || temp.capacity() < this.size) {
+ temp = ByteBuffer.allocate(this.size);
+ }
+
+ temp.flip();
+ temp.limit(this.size);
+
+ temp.putShort(this.size);
+ temp.putLong(this.tagsCode);
+ temp.putLong(this.msgStoreTime);
+ temp.putShort(this.bitMapSize);
+ if (this.bitMapSize > 0) {
+ temp.put(this.filterBitMap);
+ }
+
+ return temp.array();
+ }
+
+ /**
+ * Calculate unit size by current data.
+ *
+ * @return
+ */
+ private int calcUnitSize() {
+ int sizeTemp = MIN_EXT_UNIT_SIZE + (filterBitMap == null ? 0 : filterBitMap.length);
+ return sizeTemp;
+ }
+
+ public long getTagsCode() {
+ return tagsCode;
+ }
+
+ public void setTagsCode(final long tagsCode) {
+ this.tagsCode = tagsCode;
+ }
+
+ public long getMsgStoreTime() {
+ return msgStoreTime;
+ }
+
+ public void setMsgStoreTime(final long msgStoreTime) {
+ this.msgStoreTime = msgStoreTime;
+ }
+
+ public byte[] getFilterBitMap() {
+ if (this.bitMapSize < 1) {
+ return null;
+ }
+ return filterBitMap;
+ }
+
+ public void setFilterBitMap(final byte[] filterBitMap) {
+ this.filterBitMap = filterBitMap;
+ // not safe transform, but size will be calculate by #calcUnitSize
+ this.bitMapSize = (short) (filterBitMap == null ? 0 : filterBitMap.length);
+ }
+
+ public short getSize() {
+ return size;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CqExtUnit)) return false;
+
+ CqExtUnit cqExtUnit = (CqExtUnit) o;
+
+ if (bitMapSize != cqExtUnit.bitMapSize) return false;
+ if (msgStoreTime != cqExtUnit.msgStoreTime) return false;
+ if (size != cqExtUnit.size) return false;
+ if (tagsCode != cqExtUnit.tagsCode) return false;
+ if (!Arrays.equals(filterBitMap, cqExtUnit.filterBitMap)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) size;
+ result = 31 * result + (int) (tagsCode ^ (tagsCode >>> 32));
+ result = 31 * result + (int) (msgStoreTime ^ (msgStoreTime >>> 32));
+ result = 31 * result + (int) bitMapSize;
+ result = 31 * result + (filterBitMap != null ? Arrays.hashCode(filterBitMap) : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "CqExtUnit{" +
+ "size=" + size +
+ ", tagsCode=" + tagsCode +
+ ", msgStoreTime=" + msgStoreTime +
+ ", bitMapSize=" + bitMapSize +
+ ", filterBitMap=" + Arrays.toString(filterBitMap) +
+ '}';
+ }
+ }
+}
diff --git a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageFilter.java b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageFilter.java
index 13500261a5..9db87f3196 100644
--- a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageFilter.java
+++ b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageFilter.java
@@ -18,26 +18,33 @@ package org.apache.rocketmq.store;
import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
public class DefaultMessageFilter implements MessageFilter {
- @Override
- public boolean isMessageMatched(SubscriptionData subscriptionData, Long tagsCode) {
- if (tagsCode == null) {
- return true;
- }
+ private SubscriptionData subscriptionData;
- if (null == subscriptionData) {
- return true;
- }
-
- if (subscriptionData.isClassFilterMode())
- return true;
-
- if (subscriptionData.getSubString().equals(SubscriptionData.SUB_ALL)) {
- return true;
- }
-
- return subscriptionData.getCodeSet().contains(tagsCode.intValue());
+ public DefaultMessageFilter(final SubscriptionData subscriptionData) {
+ this.subscriptionData = subscriptionData;
}
+ @Override
+ public boolean isMatchedByConsumeQueue(Long tagsCode, ConsumeQueueExt.CqExtUnit cqExtUnit) {
+ if (null == tagsCode || null == subscriptionData) {
+ return true;
+ }
+
+ if (subscriptionData.isClassFilterMode()) {
+ return true;
+ }
+
+ return subscriptionData.getSubString().equals(SubscriptionData.SUB_ALL)
+ || subscriptionData.getCodeSet().contains(tagsCode.intValue());
+ }
+
+ @Override
+ public boolean isMatchedByCommitLog(ByteBuffer msgBuffer, Map properties) {
+ return true;
+ }
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
index 0edfeec47b..7bed62c7c5 100644
--- a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
+++ b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
@@ -23,6 +23,7 @@ import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -41,7 +42,6 @@ import org.apache.rocketmq.common.constant.LoggerName;
import org.apache.rocketmq.common.message.MessageDecoder;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageExtBatch;
-import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.common.running.RunningStats;
import org.apache.rocketmq.common.sysflag.MessageSysFlag;
import org.apache.rocketmq.store.config.BrokerRole;
@@ -60,8 +60,6 @@ import static org.apache.rocketmq.store.config.BrokerRole.SLAVE;
public class DefaultMessageStore implements MessageStore {
private static final Logger log = LoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);
- private final MessageFilter messageFilter = new DefaultMessageFilter();
-
private final MessageStoreConfig messageStoreConfig;
// CommitLog
private final CommitLog commitLog;
@@ -103,6 +101,8 @@ public class DefaultMessageStore implements MessageStore {
private AtomicLong printTimes = new AtomicLong(0);
+ private final LinkedList dispatcherList;
+
public DefaultMessageStore(final MessageStoreConfig messageStoreConfig, final BrokerStatsManager brokerStatsManager,
final MessageArrivingListener messageArrivingListener, final BrokerConfig brokerConfig) throws IOException {
this.messageArrivingListener = messageArrivingListener;
@@ -133,6 +133,10 @@ public class DefaultMessageStore implements MessageStore {
this.allocateMappedFileService.start();
this.indexService.start();
+
+ this.dispatcherList = new LinkedList<>();
+ this.dispatcherList.addLast(new CommitLogDispatcherBuildConsumeQueue());
+ this.dispatcherList.addLast(new CommitLogDispatcherBuildIndex());
}
public void truncateDirtyLogicFiles(long phyOffset) {
@@ -409,7 +413,7 @@ public class DefaultMessageStore implements MessageStore {
}
public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset, final int maxMsgNums,
- final SubscriptionData subscriptionData) {
+ final MessageFilter messageFilter) {
if (this.shutdown) {
log.warn("message store has shutdown, so getMessage is forbidden");
return null;
@@ -464,6 +468,7 @@ public class DefaultMessageStore implements MessageStore {
int i = 0;
final int maxFilterMessageCount = Math.max(16000, maxMsgNums * ConsumeQueue.CQ_STORE_UNIT_SIZE);
final boolean diskFallRecorded = this.messageStoreConfig.isDiskFallRecorded();
+ ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
for (; i < bufferConsumeQueue.getSize() && i < maxFilterMessageCount; i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = bufferConsumeQueue.getByteBuffer().getLong();
int sizePy = bufferConsumeQueue.getByteBuffer().getInt();
@@ -483,29 +488,51 @@ public class DefaultMessageStore implements MessageStore {
break;
}
- if (this.messageFilter.isMessageMatched(subscriptionData, tagsCode)) {
- SelectMappedBufferResult selectResult = this.commitLog.getMessage(offsetPy, sizePy);
- if (selectResult != null) {
- this.storeStatsService.getGetMessageTransferedMsgCount().incrementAndGet();
- getResult.addMessage(selectResult);
- status = GetMessageStatus.FOUND;
- nextPhyFileStartOffset = Long.MIN_VALUE;
+ boolean extRet = false;
+ if (consumeQueue.isExtAddr(tagsCode)) {
+ extRet = consumeQueue.getExt(tagsCode, cqExtUnit);
+ if (extRet) {
+ tagsCode = cqExtUnit.getTagsCode();
} else {
- if (getResult.getBufferTotalSize() == 0) {
- status = GetMessageStatus.MESSAGE_WAS_REMOVING;
- }
-
- nextPhyFileStartOffset = this.commitLog.rollNextFile(offsetPy);
+ // can't find ext content.Client will filter messages by tag also.
+ log.error("[BUG] can't find consume queue extend file content!addr={}, offsetPy={}, sizePy={}, topic={}, group={}",
+ tagsCode, offsetPy, sizePy, topic, group);
}
- } else {
+ }
+
+ if (messageFilter != null
+ && !messageFilter.isMatchedByConsumeQueue(tagsCode, extRet ? cqExtUnit : null)) {
if (getResult.getBufferTotalSize() == 0) {
status = GetMessageStatus.NO_MATCHED_MESSAGE;
}
- if (log.isDebugEnabled()) {
- log.debug("message type not matched, client: " + subscriptionData + " server: " + tagsCode);
- }
+ continue;
}
+
+ SelectMappedBufferResult selectResult = this.commitLog.getMessage(offsetPy, sizePy);
+ if (null == selectResult) {
+ if (getResult.getBufferTotalSize() == 0) {
+ status = GetMessageStatus.MESSAGE_WAS_REMOVING;
+ }
+
+ nextPhyFileStartOffset = this.commitLog.rollNextFile(offsetPy);
+ continue;
+ }
+
+ if (messageFilter != null
+ && !messageFilter.isMatchedByCommitLog(selectResult.getByteBuffer().slice(), null)) {
+ if (getResult.getBufferTotalSize() == 0) {
+ status = GetMessageStatus.NO_MATCHED_MESSAGE;
+ }
+ // release...
+ selectResult.release();
+ continue;
+ }
+
+ this.storeStatsService.getGetMessageTransferedMsgCount().incrementAndGet();
+ getResult.addMessage(selectResult);
+ status = GetMessageStatus.FOUND;
+ nextPhyFileStartOffset = Long.MIN_VALUE;
}
if (diskFallRecorded) {
@@ -1318,27 +1345,14 @@ public class DefaultMessageStore implements MessageStore {
}
public void doDispatch(DispatchRequest req) {
- final int tranType = MessageSysFlag.getTransactionValue(req.getSysFlag());
- switch (tranType) {
- case MessageSysFlag.TRANSACTION_NOT_TYPE:
- case MessageSysFlag.TRANSACTION_COMMIT_TYPE:
- DefaultMessageStore.this.putMessagePositionInfo(req.getTopic(), req.getQueueId(), req.getCommitLogOffset(), req.getMsgSize(),
- req.getTagsCode(), req.getStoreTimestamp(), req.getConsumeQueueOffset());
- break;
- case MessageSysFlag.TRANSACTION_PREPARED_TYPE:
- case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:
- break;
- }
-
- if (DefaultMessageStore.this.getMessageStoreConfig().isMessageIndexEnable()) {
- DefaultMessageStore.this.indexService.buildIndex(req);
+ for (CommitLogDispatcher dispatcher : this.dispatcherList) {
+ dispatcher.dispatch(req);
}
}
- public void putMessagePositionInfo(String topic, int queueId, long offset, int size, long tagsCode, long storeTimestamp,
- long logicOffset) {
- ConsumeQueue cq = this.findConsumeQueue(topic, queueId);
- cq.putMessagePositionInfoWrapper(offset, size, tagsCode, storeTimestamp, logicOffset);
+ public void putMessagePositionInfo(DispatchRequest dispatchRequest) {
+ ConsumeQueue cq = this.findConsumeQueue(dispatchRequest.getTopic(), dispatchRequest.getQueueId());
+ cq.putMessagePositionInfoWrapper(dispatchRequest);
}
public BrokerStatsManager getBrokerStatsManager() {
@@ -1354,6 +1368,20 @@ public class DefaultMessageStore implements MessageStore {
return remainTransientStoreBufferNumbs() == 0;
}
+ @Override
+ public LinkedList getDispatcherList() {
+ return this.dispatcherList;
+ }
+
+ @Override
+ public ConsumeQueue getConsumeQueue(String topic, int queueId) {
+ ConcurrentHashMap map = consumeQueueTable.get(topic);
+ if (map == null) {
+ return null;
+ }
+ return map.get(queueId);
+ }
+
public void unlockMappedFile(final MappedFile mappedFile) {
this.scheduledExecutorService.schedule(new Runnable() {
@Override
@@ -1363,6 +1391,33 @@ public class DefaultMessageStore implements MessageStore {
}, 6, TimeUnit.SECONDS);
}
+ class CommitLogDispatcherBuildConsumeQueue implements CommitLogDispatcher {
+
+ @Override
+ public void dispatch(DispatchRequest request) {
+ final int tranType = MessageSysFlag.getTransactionValue(request.getSysFlag());
+ switch (tranType) {
+ case MessageSysFlag.TRANSACTION_NOT_TYPE:
+ case MessageSysFlag.TRANSACTION_COMMIT_TYPE:
+ DefaultMessageStore.this.putMessagePositionInfo(request);
+ break;
+ case MessageSysFlag.TRANSACTION_PREPARED_TYPE:
+ case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:
+ break;
+ }
+ }
+ }
+
+ class CommitLogDispatcherBuildIndex implements CommitLogDispatcher {
+
+ @Override
+ public void dispatch(DispatchRequest request) {
+ if (DefaultMessageStore.this.messageStoreConfig.isMessageIndexEnable()) {
+ DefaultMessageStore.this.indexService.buildIndex(request);
+ }
+ }
+ }
+
class CleanCommitLogService {
private final static int MAX_MANUAL_DELETE_FILE_TIMES = 20;
@@ -1695,7 +1750,8 @@ public class DefaultMessageStore implements MessageStore {
&& DefaultMessageStore.this.brokerConfig.isLongPollingEnable()) {
DefaultMessageStore.this.messageArrivingListener.arriving(dispatchRequest.getTopic(),
dispatchRequest.getQueueId(), dispatchRequest.getConsumeQueueOffset() + 1,
- dispatchRequest.getTagsCode());
+ dispatchRequest.getTagsCode(), dispatchRequest.getStoreTimestamp(),
+ dispatchRequest.getBitMap(), dispatchRequest.getPropertiesMap());
}
// FIXED BUG By shijia
this.reputFromOffset += size;
diff --git a/store/src/main/java/org/apache/rocketmq/store/DispatchRequest.java b/store/src/main/java/org/apache/rocketmq/store/DispatchRequest.java
index 33529dae2c..3d33eaf363 100644
--- a/store/src/main/java/org/apache/rocketmq/store/DispatchRequest.java
+++ b/store/src/main/java/org/apache/rocketmq/store/DispatchRequest.java
@@ -16,6 +16,8 @@
*/
package org.apache.rocketmq.store;
+import java.util.Map;
+
public class DispatchRequest {
private final String topic;
private final int queueId;
@@ -30,6 +32,8 @@ public class DispatchRequest {
private final int sysFlag;
private final long preparedTransactionOffset;
+ private final Map propertiesMap;
+ private byte[] bitMap;
public DispatchRequest(
final String topic,
@@ -42,7 +46,8 @@ public class DispatchRequest {
final String keys,
final String uniqKey,
final int sysFlag,
- final long preparedTransactionOffset
+ final long preparedTransactionOffset,
+ final Map propertiesMap
) {
this.topic = topic;
this.queueId = queueId;
@@ -57,6 +62,7 @@ public class DispatchRequest {
this.sysFlag = sysFlag;
this.preparedTransactionOffset = preparedTransactionOffset;
this.success = true;
+ this.propertiesMap = propertiesMap;
}
public DispatchRequest(int size) {
@@ -81,6 +87,7 @@ public class DispatchRequest {
this.sysFlag = 0;
this.preparedTransactionOffset = 0;
this.success = false;
+ this.propertiesMap = null;
}
public DispatchRequest(int size, boolean success) {
@@ -105,6 +112,7 @@ public class DispatchRequest {
this.sysFlag = 0;
this.preparedTransactionOffset = 0;
this.success = success;
+ this.propertiesMap = null;
}
public String getTopic() {
@@ -155,4 +163,15 @@ public class DispatchRequest {
return uniqKey;
}
+ public Map getPropertiesMap() {
+ return propertiesMap;
+ }
+
+ public byte[] getBitMap() {
+ return bitMap;
+ }
+
+ public void setBitMap(byte[] bitMap) {
+ this.bitMap = bitMap;
+ }
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/MappedFile.java b/store/src/main/java/org/apache/rocketmq/store/MappedFile.java
index 550e578c21..a9a00a857c 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MappedFile.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MappedFile.java
@@ -244,6 +244,31 @@ public class MappedFile extends ReferenceResource {
return false;
}
+ /**
+ * Content of data from offset to offset + length will be wrote to file.
+ *
+ * @param data
+ * @param offset The offset of the subarray to be used.
+ * @param length The length of the subarray to be used.
+ * @return
+ */
+ public boolean appendMessage(final byte[] data, final int offset, final int length) {
+ int currentPos = this.wrotePosition.get();
+
+ if ((currentPos + length) <= this.fileSize) {
+ try {
+ this.fileChannel.position(currentPos);
+ this.fileChannel.write(ByteBuffer.wrap(data, offset, length));
+ } catch (Throwable e) {
+ log.error("Error occurred when append message to mappedFile.", e);
+ }
+ this.wrotePosition.addAndGet(length);
+ return true;
+ }
+
+ return false;
+ }
+
/**
* @param flushLeastPages
* @return The current flushed position
diff --git a/store/src/main/java/org/apache/rocketmq/store/MappedFileQueue.java b/store/src/main/java/org/apache/rocketmq/store/MappedFileQueue.java
index 5c6c62c661..a8fa36485e 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MappedFileQueue.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MappedFileQueue.java
@@ -121,7 +121,7 @@ public class MappedFileQueue {
this.deleteExpiredFile(willRemoveFiles);
}
- private void deleteExpiredFile(List files) {
+ void deleteExpiredFile(List files) {
if (!files.isEmpty()) {
diff --git a/store/src/main/java/org/apache/rocketmq/store/MessageArrivingListener.java b/store/src/main/java/org/apache/rocketmq/store/MessageArrivingListener.java
index 2523c1ac95..dee1bc7527 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MessageArrivingListener.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MessageArrivingListener.java
@@ -17,6 +17,9 @@
package org.apache.rocketmq.store;
+import java.util.Map;
+
public interface MessageArrivingListener {
- void arriving(String topic, int queueId, long logicOffset, long tagsCode);
+ void arriving(String topic, int queueId, long logicOffset, long tagsCode,
+ long msgStoreTime, byte[] filterBitMap, Map properties);
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/MessageFilter.java b/store/src/main/java/org/apache/rocketmq/store/MessageFilter.java
index 859ce99461..6b34758b43 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MessageFilter.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MessageFilter.java
@@ -16,8 +16,30 @@
*/
package org.apache.rocketmq.store;
-import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import java.nio.ByteBuffer;
+import java.util.Map;
public interface MessageFilter {
- boolean isMessageMatched(final SubscriptionData subscriptionData, final Long tagsCode);
+ /**
+ * match by tags code or filter bit map which is calculated when message received
+ * and stored in consume queue ext.
+ *
+ * @param tagsCode tagsCode
+ * @param cqExtUnit extend unit of consume queue
+ * @return
+ */
+ boolean isMatchedByConsumeQueue(final Long tagsCode,
+ final ConsumeQueueExt.CqExtUnit cqExtUnit);
+
+ /**
+ * match by message content which are stored in commit log.
+ * {@code msgBuffer} and {@code properties} are not all null.If invoked in store,
+ * {@code properties} is null;If invoked in {@code PullRequestHoldService}, {@code msgBuffer} is null.
+ *
+ * @param msgBuffer message buffer in commit log, may be null if not invoked in store.
+ * @param properties message properties, should decode from buffer if null by yourself.
+ * @return
+ */
+ boolean isMatchedByCommitLog(final ByteBuffer msgBuffer,
+ final Map properties);
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/MessageStore.java b/store/src/main/java/org/apache/rocketmq/store/MessageStore.java
index 65c546b706..e841c083b5 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MessageStore.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MessageStore.java
@@ -17,10 +17,10 @@
package org.apache.rocketmq.store;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.Set;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageExtBatch;
-import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
public interface MessageStore {
@@ -37,7 +37,7 @@ public interface MessageStore {
PutMessageResult putMessages(final MessageExtBatch messageExtBatch);
GetMessageResult getMessage(final String group, final String topic, final int queueId,
- final long offset, final int maxMsgNums, final SubscriptionData subscriptionData);
+ final long offset, final int maxMsgNums, final MessageFilter messageFilter);
long getMaxOffsetInQuque(final String topic, final int queueId);
@@ -105,4 +105,8 @@ public interface MessageStore {
long lockTimeMills();
boolean isTransientStorePoolDeficient();
+
+ LinkedList getDispatcherList();
+
+ ConsumeQueue getConsumeQueue(String topic, int queueId);
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java b/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java
index 7ae2ab5c37..29f800c205 100644
--- a/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java
+++ b/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java
@@ -34,6 +34,13 @@ public class MessageStoreConfig {
private int mapedFileSizeCommitLog = 1024 * 1024 * 1024;
// ConsumeQueue file size,default is 30W
private int mapedFileSizeConsumeQueue = 300000 * ConsumeQueue.CQ_STORE_UNIT_SIZE;
+ // enable consume queue ext
+ private boolean enableConsumeQueueExt = false;
+ // ConsumeQueue extend file size, 48M
+ private int mappedFileSizeConsumeQueueExt = 48 * 1024 * 1024;
+ // Bit count of filter bit map.
+ // this will be set by pipe of calculate filter bit map.
+ private int bitMapLengthConsumeQueueExt = 64;
// CommitLog flush interval
// flush data to disk
@@ -191,6 +198,30 @@ public class MessageStoreConfig {
this.mapedFileSizeConsumeQueue = mapedFileSizeConsumeQueue;
}
+ public boolean isEnableConsumeQueueExt() {
+ return enableConsumeQueueExt;
+ }
+
+ public void setEnableConsumeQueueExt(boolean enableConsumeQueueExt) {
+ this.enableConsumeQueueExt = enableConsumeQueueExt;
+ }
+
+ public int getMappedFileSizeConsumeQueueExt() {
+ return mappedFileSizeConsumeQueueExt;
+ }
+
+ public void setMappedFileSizeConsumeQueueExt(int mappedFileSizeConsumeQueueExt) {
+ this.mappedFileSizeConsumeQueueExt = mappedFileSizeConsumeQueueExt;
+ }
+
+ public int getBitMapLengthConsumeQueueExt() {
+ return bitMapLengthConsumeQueueExt;
+ }
+
+ public void setBitMapLengthConsumeQueueExt(int bitMapLengthConsumeQueueExt) {
+ this.bitMapLengthConsumeQueueExt = bitMapLengthConsumeQueueExt;
+ }
+
public int getFlushIntervalCommitLog() {
return flushIntervalCommitLog;
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/config/StorePathConfigHelper.java b/store/src/main/java/org/apache/rocketmq/store/config/StorePathConfigHelper.java
index aebebafa31..ef1d670a2b 100644
--- a/store/src/main/java/org/apache/rocketmq/store/config/StorePathConfigHelper.java
+++ b/store/src/main/java/org/apache/rocketmq/store/config/StorePathConfigHelper.java
@@ -24,6 +24,10 @@ public class StorePathConfigHelper {
return rootDir + File.separator + "consumequeue";
}
+ public static String getStorePathConsumeQueueExt(final String rootDir) {
+ return rootDir + File.separator + "consumequeue_ext";
+ }
+
public static String getStorePathIndex(final String rootDir) {
return rootDir + File.separator + "index";
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java b/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java
index e08a6f54d0..d45b994a3b 100644
--- a/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java
+++ b/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java
@@ -32,6 +32,7 @@ import org.apache.rocketmq.common.message.MessageDecoder;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.running.RunningStats;
import org.apache.rocketmq.store.ConsumeQueue;
+import org.apache.rocketmq.store.ConsumeQueueExt;
import org.apache.rocketmq.store.DefaultMessageStore;
import org.apache.rocketmq.store.MessageExtBrokerInner;
import org.apache.rocketmq.store.PutMessageResult;
@@ -248,11 +249,24 @@ public class ScheduleMessageService extends ConfigManager {
try {
long nextOffset = offset;
int i = 0;
+ ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
for (; i < bufferCQ.getSize(); i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = bufferCQ.getByteBuffer().getLong();
int sizePy = bufferCQ.getByteBuffer().getInt();
long tagsCode = bufferCQ.getByteBuffer().getLong();
+ if (cq.isExtAddr(tagsCode)) {
+ if (cq.getExt(tagsCode, cqExtUnit)) {
+ tagsCode = cqExtUnit.getTagsCode();
+ } else {
+ //can't find ext content.So re compute tags code.
+ log.error("[BUG] can't find consume queue extend file content!addr={}, offsetPy={}, sizePy={}",
+ tagsCode, offsetPy, sizePy);
+ long msgStoreTime = defaultMessageStore.getCommitLog().pickupStoreTimestamp(offsetPy, sizePy);
+ tagsCode = computeDeliverTimestamp(delayLevel, msgStoreTime);
+ }
+ }
+
long now = System.currentTimeMillis();
long deliverTimestamp = this.correctDeliverTimestamp(now, tagsCode);
diff --git a/store/src/test/java/org/apache/rocketmq/store/ConsumeQueueExtTest.java b/store/src/test/java/org/apache/rocketmq/store/ConsumeQueueExtTest.java
new file mode 100644
index 0000000000..5dbc5848ad
--- /dev/null
+++ b/store/src/test/java/org/apache/rocketmq/store/ConsumeQueueExtTest.java
@@ -0,0 +1,251 @@
+/*
+ * 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.store;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Random;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ConsumeQueueExtTest {
+
+ private static final String topic = "abc";
+ private static final int queueId = 0;
+ private static final String storePath = "." + File.separator + "unit_test_store";
+ private static final int bitMapLength = 64;
+ private static final int unitSizeWithBitMap = ConsumeQueueExt.CqExtUnit.MIN_EXT_UNIT_SIZE + bitMapLength / Byte.SIZE;
+ private static final int cqExtFileSize = 10 * unitSizeWithBitMap;
+ private static final int unitCount = 20;
+
+
+ protected ConsumeQueueExt genExt() {
+ return new ConsumeQueueExt(
+ topic, queueId, storePath, cqExtFileSize, bitMapLength
+ );
+ }
+
+ protected byte[] genBitMap(int bitMapLength) {
+ byte[] bytes = new byte[bitMapLength / Byte.SIZE];
+
+ Random random = new Random(System.currentTimeMillis());
+ random.nextBytes(bytes);
+
+ return bytes;
+ }
+
+ protected ConsumeQueueExt.CqExtUnit genUnit(boolean hasBitMap) {
+ ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
+
+ cqExtUnit.setTagsCode(Math.abs((new Random(System.currentTimeMillis())).nextInt()));
+ cqExtUnit.setMsgStoreTime(System.currentTimeMillis());
+ if (hasBitMap) {
+ cqExtUnit.setFilterBitMap(genBitMap(bitMapLength));
+ }
+
+ return cqExtUnit;
+ }
+
+ protected void deleteDirectory(String rootPath) {
+ File file = new File(rootPath);
+ deleteFile(file);
+ }
+
+ protected void deleteFile(File file) {
+ File[] subFiles = file.listFiles();
+ if (subFiles != null) {
+ for (File sub : subFiles) {
+ deleteFile(sub);
+ }
+ }
+
+ file.delete();
+ }
+
+ protected void putSth(ConsumeQueueExt consumeQueueExt, boolean getAfterPut,
+ boolean unitSameSize, int unitCount) {
+ for (int i = 0; i < unitCount; i++) {
+ ConsumeQueueExt.CqExtUnit putUnit =
+ unitSameSize ? genUnit(true) : genUnit(i % 2 == 0);
+
+ long addr = consumeQueueExt.put(putUnit);
+ assertThat(addr).isLessThan(0);
+
+ if (getAfterPut) {
+ ConsumeQueueExt.CqExtUnit getUnit = consumeQueueExt.get(addr);
+
+ assertThat(getUnit).isNotNull();
+ assertThat(putUnit).isEqualTo(getUnit);
+ }
+
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ assertThat(false).isTrue();
+ }
+ }
+ }
+
+ @Test
+ public void testPut() {
+ ConsumeQueueExt consumeQueueExt = genExt();
+
+ try {
+ putSth(consumeQueueExt, true, false, unitCount);
+ } finally {
+ consumeQueueExt.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+
+ @Test
+ public void testGet() {
+ ConsumeQueueExt consumeQueueExt = genExt();
+
+ putSth(consumeQueueExt, false, false, unitCount);
+
+ try {
+ // from start.
+ long addr = consumeQueueExt.decorate(0);
+
+ ConsumeQueueExt.CqExtUnit unit = new ConsumeQueueExt.CqExtUnit();
+ while (true) {
+ boolean ret = consumeQueueExt.get(addr, unit);
+
+ if (!ret) {
+ break;
+ }
+
+ assertThat(unit.getSize()).isGreaterThanOrEqualTo(ConsumeQueueExt.CqExtUnit.MIN_EXT_UNIT_SIZE);
+
+ addr += unit.getSize();
+ }
+ } finally {
+ consumeQueueExt.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+
+ @Test
+ public void testGet_invalidAddress() {
+ ConsumeQueueExt consumeQueueExt = genExt();
+
+ putSth(consumeQueueExt, false, true, unitCount);
+
+ try {
+ ConsumeQueueExt.CqExtUnit unit = consumeQueueExt.get(0);
+
+ assertThat(unit).isNull();
+
+ long addr = (cqExtFileSize / unitSizeWithBitMap) * unitSizeWithBitMap;
+ addr += unitSizeWithBitMap;
+
+ unit = consumeQueueExt.get(addr);
+ assertThat(unit).isNull();
+ } finally {
+ consumeQueueExt.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+
+ @Test
+ public void testRecovery() {
+ ConsumeQueueExt putCqExt = genExt();
+
+ putSth(putCqExt, false, true, unitCount);
+
+ ConsumeQueueExt loadCqExt = genExt();
+
+ loadCqExt.load();
+
+ loadCqExt.recover();
+
+ try {
+ assertThat(loadCqExt.getMinAddress()).isEqualTo(Long.MIN_VALUE);
+
+ // same unit size.
+ int countPerFile = (cqExtFileSize - ConsumeQueueExt.END_BLANK_DATA_LENGTH) / unitSizeWithBitMap;
+
+ int lastFileUnitCount = unitCount % countPerFile;
+
+ int fileCount = unitCount / countPerFile + 1;
+ if (lastFileUnitCount == 0) {
+ fileCount -= 1;
+ }
+
+ if (lastFileUnitCount == 0) {
+ assertThat(loadCqExt.unDecorate(loadCqExt.getMaxAddress()) % cqExtFileSize).isEqualTo(0);
+ } else {
+ assertThat(loadCqExt.unDecorate(loadCqExt.getMaxAddress()))
+ .isEqualTo(lastFileUnitCount * unitSizeWithBitMap + (fileCount - 1) * cqExtFileSize);
+ }
+ } finally {
+ putCqExt.destroy();
+ loadCqExt.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+
+ @Test
+ public void testTruncateByMinOffset() {
+ ConsumeQueueExt consumeQueueExt = genExt();
+
+ putSth(consumeQueueExt, false, true, unitCount * 2);
+
+ try {
+ // truncate first one file.
+ long address = consumeQueueExt.decorate((long) (cqExtFileSize * 1.5));
+
+ long expectMinAddress = consumeQueueExt.decorate(cqExtFileSize);
+
+ consumeQueueExt.truncateByMinAddress(address);
+
+ long minAddress = consumeQueueExt.getMinAddress();
+
+ assertThat(expectMinAddress).isEqualTo(minAddress);
+ } finally {
+ consumeQueueExt.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+
+ @Test
+ public void testTruncateByMaxOffset() {
+ ConsumeQueueExt consumeQueueExt = genExt();
+
+ putSth(consumeQueueExt, false, true, unitCount * 2);
+
+ try {
+ // truncate, only first 3 files exist.
+ long address = consumeQueueExt.decorate(cqExtFileSize * 2 + unitSizeWithBitMap);
+
+ long expectMaxAddress = address + unitSizeWithBitMap;
+
+ consumeQueueExt.truncateByMaxAddress(address);
+
+ long maxAddress = consumeQueueExt.getMaxAddress();
+
+ assertThat(expectMaxAddress).isEqualTo(maxAddress);
+ } finally {
+ consumeQueueExt.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+}
diff --git a/store/src/test/java/org/apache/rocketmq/store/ConsumeQueueTest.java b/store/src/test/java/org/apache/rocketmq/store/ConsumeQueueTest.java
new file mode 100644
index 0000000000..9c42fb91d2
--- /dev/null
+++ b/store/src/test/java/org/apache/rocketmq/store/ConsumeQueueTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.store;
+
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.apache.rocketmq.store.stats.BrokerStatsManager;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ConsumeQueueTest {
+
+ private static final String msg = "Once, there was a chance for me!";
+ private static final byte[] msgBody = msg.getBytes();
+
+ private static final String topic = "abc";
+ private static final int queueId = 0;
+ private static final String storePath = "." + File.separator + "unit_test_store";
+ private static final int commitLogFileSize = 1024 * 8;
+ private static final int cqFileSize = 10 * 20;
+ private static final int cqExtFileSize = 10 * (ConsumeQueueExt.CqExtUnit.MIN_EXT_UNIT_SIZE + 64);
+
+ private static SocketAddress BornHost;
+
+ private static SocketAddress StoreHost;
+
+ static {
+ try {
+ StoreHost = new InetSocketAddress(InetAddress.getLocalHost(), 8123);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ try {
+ BornHost = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public MessageExtBrokerInner buildMessage() {
+ MessageExtBrokerInner msg = new MessageExtBrokerInner();
+ msg.setTopic(topic);
+ msg.setTags("TAG1");
+ msg.setKeys("Hello");
+ msg.setBody(msgBody);
+ msg.setKeys(String.valueOf(System.currentTimeMillis()));
+ msg.setQueueId(queueId);
+ msg.setSysFlag(0);
+ msg.setBornTimestamp(System.currentTimeMillis());
+ msg.setStoreHost(StoreHost);
+ msg.setBornHost(BornHost);
+ for (int i = 0; i < 1; i++) {
+ msg.putUserProperty(String.valueOf(i), "imagoodperson" + i);
+ }
+ msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));
+
+ return msg;
+ }
+
+
+ public MessageStoreConfig buildStoreConfig(int commitLogFileSize, int cqFileSize,
+ boolean enableCqExt, int cqExtFileSize) {
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+ messageStoreConfig.setMapedFileSizeCommitLog(commitLogFileSize);
+ messageStoreConfig.setMapedFileSizeConsumeQueue(cqFileSize);
+ messageStoreConfig.setMappedFileSizeConsumeQueueExt(cqExtFileSize);
+ messageStoreConfig.setMessageIndexEnable(false);
+ messageStoreConfig.setEnableConsumeQueueExt(enableCqExt);
+
+ messageStoreConfig.setStorePathRootDir(storePath);
+ messageStoreConfig.setStorePathCommitLog(storePath + File.separator + "commitlog");
+
+ return messageStoreConfig;
+ }
+
+ protected DefaultMessageStore gen() throws Exception {
+ MessageStoreConfig messageStoreConfig = buildStoreConfig(
+ commitLogFileSize, cqFileSize, true, cqExtFileSize
+ );
+
+ BrokerConfig brokerConfig = new BrokerConfig();
+
+ DefaultMessageStore master = new DefaultMessageStore(
+ messageStoreConfig,
+ new BrokerStatsManager(brokerConfig.getBrokerClusterName()),
+ new MessageArrivingListener() {
+ @Override
+ public void arriving(String topic, int queueId, long logicOffset, long tagsCode,
+ long msgStoreTime, byte[] filterBitMap, Map properties) {
+ }
+ }
+ , brokerConfig);
+
+ assertThat(master.load()).isTrue();
+
+ master.start();
+
+ return master;
+ }
+
+ protected void putMsg(DefaultMessageStore master) throws Exception {
+ long totalMsgs = 200;
+
+ for (long i = 0; i < totalMsgs; i++) {
+ master.putMessage(buildMessage());
+ }
+ }
+
+ protected void deleteDirectory(String rootPath) {
+ File file = new File(rootPath);
+ deleteFile(file);
+ }
+
+ protected void deleteFile(File file) {
+ File[] subFiles = file.listFiles();
+ if (subFiles != null) {
+ for (File sub : subFiles) {
+ deleteFile(sub);
+ }
+ }
+
+ file.delete();
+ }
+
+ @Test
+ public void testConsumeQueueWithExtendData() {
+ DefaultMessageStore master = null;
+ try {
+ master = gen();
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ master.getDispatcherList().addFirst(new CommitLogDispatcher() {
+
+ @Override
+ public void dispatch(DispatchRequest request) {
+ runCount++;
+ }
+
+ private int runCount = 0;
+ });
+
+ try {
+ try {
+ putMsg(master);
+ // wait build consume queue
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ assertThat(Boolean.FALSE).isTrue();
+ }
+
+ ConsumeQueue cq = master.getConsumeQueueTable().get(topic).get(queueId);
+
+ assertThat(cq).isNotNull();
+
+ long index = 0;
+
+ while (index < cq.getMaxOffsetInQueue()) {
+ SelectMappedBufferResult bufferResult = cq.getIndexBuffer(index);
+
+ assertThat(bufferResult).isNotNull();
+
+ ByteBuffer buffer = bufferResult.getByteBuffer();
+
+ assertThat(buffer).isNotNull();
+ try {
+ ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
+ for (int i = 0; i < bufferResult.getSize(); i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
+ long phyOffset = buffer.getLong();
+ int size = buffer.getInt();
+ long tagsCode = buffer.getLong();
+
+ assertThat(phyOffset).isGreaterThanOrEqualTo(0);
+ assertThat(size).isGreaterThan(0);
+ assertThat(tagsCode).isLessThan(0);
+
+ boolean ret = cq.getExt(tagsCode, cqExtUnit);
+
+ assertThat(ret).isTrue();
+ assertThat(cqExtUnit).isNotNull();
+ assertThat(cqExtUnit.getSize()).isGreaterThan((short) 0);
+ assertThat(cqExtUnit.getMsgStoreTime()).isGreaterThan(0);
+ assertThat(cqExtUnit.getTagsCode()).isGreaterThan(0);
+ }
+
+ } finally {
+ bufferResult.release();
+ }
+
+ index += cqFileSize / ConsumeQueue.CQ_STORE_UNIT_SIZE;
+ }
+ } finally {
+ master.shutdown();
+ master.destroy();
+ deleteDirectory(storePath);
+ }
+ }
+}
diff --git a/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java b/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java
index 5c9c46f7eb..75f1de9412 100644
--- a/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java
+++ b/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java
@@ -20,6 +20,7 @@ package org.apache.rocketmq.store;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.rocketmq.common.BrokerConfig;
import org.apache.rocketmq.store.config.FlushDiskType;
@@ -124,7 +125,8 @@ public class DefaultMessageStoreTest {
private class MyMessageArrivingListener implements MessageArrivingListener {
@Override
- public void arriving(String topic, int queueId, long logicOffset, long tagsCode) {
+ public void arriving(String topic, int queueId, long logicOffset, long tagsCode, long msgStoreTime,
+ byte[] filterBitMap, Map properties) {
}
}
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java
index 19bff895b0..409ea3322f 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java
@@ -41,6 +41,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
+import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
@@ -469,4 +470,12 @@ public class DefaultMQAdminExt extends ClientConfig implements MQAdminExt {
UnsupportedEncodingException {
return this.defaultMQAdminExtImpl.getNameServerConfig(nameServers);
}
+
+ @Override
+ public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic, int queueId, long index, int count, String consumerGroup)
+ throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
+ return this.defaultMQAdminExtImpl.queryConsumeQueue(
+ brokerAddr, topic, queueId, index, count, consumerGroup
+ );
+ }
}
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 a31b69d927..157ae21eba 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
@@ -63,6 +63,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
+import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
@@ -955,4 +956,11 @@ public class DefaultMQAdminExtImpl implements MQAdminExt, MQAdminExtInner {
return this.mqClientInstance.getMQClientAPIImpl().getNameServerConfig(nameServers, timeoutMillis);
}
+ @Override
+ public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic, int queueId, long index, int count, String consumerGroup)
+ throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
+ return this.mqClientInstance.getMQClientAPIImpl().queryConsumeQueue(
+ brokerAddr, topic, queueId, index, count, consumerGroup, timeoutMillis
+ );
+ }
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java b/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java
index 493cf5400d..82add92b2d 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java
@@ -39,6 +39,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
+import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
@@ -241,4 +242,25 @@ public interface MQAdminExt extends MQAdmin {
Map getNameServerConfig(final List nameServers) throws InterruptedException,
RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException,
MQClientException, UnsupportedEncodingException;
+
+ /**
+ * query consume queue data
+ *
+ * @param brokerAddr broker ip address
+ * @param topic topic
+ * @param queueId id of queue
+ * @param index start offset
+ * @param count how many
+ * @param consumerGroup group
+ * @return
+ * @throws InterruptedException
+ * @throws RemotingTimeoutException
+ * @throws RemotingSendRequestException
+ * @throws RemotingConnectException
+ * @throws MQClientException
+ */
+ QueryConsumeQueueResponseBody queryConsumeQueue(final String brokerAddr,
+ final String topic, final int queueId,
+ final long index, final int count, final String consumerGroup)
+ throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException;
}
diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/MQAdminStartup.java b/tools/src/main/java/org/apache/rocketmq/tools/command/MQAdminStartup.java
index 9bd37e846d..6398291a06 100644
--- a/tools/src/main/java/org/apache/rocketmq/tools/command/MQAdminStartup.java
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/MQAdminStartup.java
@@ -59,6 +59,7 @@ import org.apache.rocketmq.tools.command.namesrv.UpdateNamesrvConfigCommand;
import org.apache.rocketmq.tools.command.namesrv.WipeWritePermSubCommand;
import org.apache.rocketmq.tools.command.offset.CloneGroupOffsetCommand;
import org.apache.rocketmq.tools.command.offset.ResetOffsetByTimeCommand;
+import org.apache.rocketmq.tools.command.queue.QueryConsumeQueueCommand;
import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand;
import org.apache.rocketmq.tools.command.topic.AllocateMQSubCommand;
import org.apache.rocketmq.tools.command.topic.DeleteTopicSubCommand;
@@ -189,6 +190,8 @@ public class MQAdminStartup {
initCommand(new GetNamesrvConfigCommand());
initCommand(new UpdateNamesrvConfigCommand());
initCommand(new GetBrokerConfigCommand());
+
+ initCommand(new QueryConsumeQueueCommand());
}
private static void initLogback() throws JoranException {
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
new file mode 100644
index 0000000000..611addd545
--- /dev/null
+++ b/tools/src/main/java/org/apache/rocketmq/tools/command/queue/QueryConsumeQueueCommand.java
@@ -0,0 +1,159 @@
+/*
+ * 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.command.queue;
+
+import com.alibaba.fastjson.JSON;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.PosixParser;
+import org.apache.rocketmq.common.protocol.body.ConsumeQueueData;
+import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
+import org.apache.rocketmq.common.protocol.route.TopicRouteData;
+import org.apache.rocketmq.remoting.RPCHook;
+import org.apache.rocketmq.srvutil.ServerUtil;
+import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
+import org.apache.rocketmq.tools.command.SubCommand;
+
+public class QueryConsumeQueueCommand implements SubCommand {
+
+ public static void main(String[] args) {
+ QueryConsumeQueueCommand cmd = new QueryConsumeQueueCommand();
+
+ Options options = ServerUtil.buildCommandlineOptions(new Options());
+ String[] subargs = new String[]{"-t TopicTest", "-q 0", "-i 6447", "-b 100.81.165.119:10911"};
+ final CommandLine commandLine =
+ ServerUtil.parseCmdLine("mqadmin " + cmd.commandName(), subargs, cmd.buildCommandlineOptions(options),
+ new PosixParser());
+ cmd.execute(commandLine, options, null);
+ }
+
+ @Override
+ public String commandName() {
+ return "queryCq";
+ }
+
+ @Override
+ public String commandDesc() {
+ return "Query cq command.";
+ }
+
+ @Override
+ public Options buildCommandlineOptions(Options options) {
+ Option opt = new Option("t", "topic", true, "topic name");
+ opt.setRequired(true);
+ options.addOption(opt);
+
+ opt = new Option("q", "queue", true, "queue num, ie. 1");
+ opt.setRequired(true);
+ options.addOption(opt);
+
+ opt = new Option("i", "index", true, "start queue index.");
+ opt.setRequired(true);
+ options.addOption(opt);
+
+ opt = new Option("c", "count", true, "how many.");
+ opt.setRequired(false);
+ options.addOption(opt);
+
+ opt = new Option("b", "broker", true, "broker addr.");
+ opt.setRequired(false);
+ options.addOption(opt);
+
+ opt = new Option("g", "consumer", true, "consumer group.");
+ opt.setRequired(false);
+ options.addOption(opt);
+
+ return options;
+ }
+
+ @Override
+ public void execute(CommandLine commandLine, Options options, RPCHook rpcHook) {
+ DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
+
+ defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
+
+ try {
+ defaultMQAdminExt.start();
+
+ String topic = commandLine.getOptionValue("t").trim();
+ int queueId = Integer.valueOf(commandLine.getOptionValue("q").trim());
+ long index = Long.valueOf(commandLine.getOptionValue("i").trim());
+ int count = Integer.valueOf(commandLine.getOptionValue("c", "10").trim());
+ String broker = null;
+ if (commandLine.hasOption("b")) {
+ broker = commandLine.getOptionValue("b").trim();
+ }
+ String consumerGroup = null;
+ if (commandLine.hasOption("g")) {
+ consumerGroup = commandLine.getOptionValue("g").trim();
+ }
+
+ if (broker == null || broker == "") {
+ TopicRouteData topicRouteData = defaultMQAdminExt.examineTopicRouteInfo(topic);
+
+ if (topicRouteData == null || topicRouteData.getBrokerDatas() == null
+ || topicRouteData.getBrokerDatas().isEmpty()) {
+ throw new Exception("No topic route data!");
+ }
+
+ broker = topicRouteData.getBrokerDatas().get(0).getBrokerAddrs().get(0L);
+ }
+
+ QueryConsumeQueueResponseBody queryConsumeQueueResponseBody = defaultMQAdminExt.queryConsumeQueue(
+ broker, topic, queueId, index, count, consumerGroup
+ );
+
+ if (queryConsumeQueueResponseBody.getSubscriptionData() != null) {
+ System.out.printf("Subscription data: \n%s\n", JSON.toJSONString(queryConsumeQueueResponseBody.getSubscriptionData(), true));
+ System.out.print("======================================\n");
+ }
+
+ if (queryConsumeQueueResponseBody.getFilterData() != null) {
+ System.out.printf("Filter data: \n%s\n", queryConsumeQueueResponseBody.getFilterData());
+ System.out.print("======================================\n");
+ }
+
+ System.out.printf("Queue data: \nmax: %d, min: %d\n", queryConsumeQueueResponseBody.getMaxQueueIndex(),
+ queryConsumeQueueResponseBody.getMinQueueIndex());
+ System.out.print("======================================\n");
+
+ if (queryConsumeQueueResponseBody.getQueueData() != null) {
+
+ long i = index;
+ for (ConsumeQueueData queueData : queryConsumeQueueResponseBody.getQueueData()) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("idx: " + i + "\n");
+
+ stringBuilder.append(queueData.toString() + "\n");
+
+ stringBuilder.append("======================================\n");
+
+ System.out.print(stringBuilder.toString());
+ i++;
+ }
+
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ defaultMQAdminExt.shutdown();
+ }
+ }
+}
From 1e4307e7da13db9c4f28796436eb67efc052ccb6 Mon Sep 17 00:00:00 2001
From: vsair
Date: Fri, 21 Apr 2017 18:21:35 +0800
Subject: [PATCH 35/74] [ROCKETMQ-179] Fix errors of IT test cases closes
apache/incubator-rocketmq#94
---
pom.xml | 4 --
.../test/clientinterface/MQCollector.java | 20 +++++++---
.../rmq/concurrent/RMQNormalListner.java | 4 +-
.../apache/rocketmq/test/util/MQAdmin.java | 1 -
.../test/base/IntegrationTestBase.java | 39 +++++++++++++------
.../TagMessageWithSameGroupConsumerIT.java | 10 ++---
.../async/AsyncSendWithMessageQueueIT.java | 5 +--
.../AsyncSendWithMessageQueueSelectorIT.java | 1 -
.../rocketmq/test/delay/NormalMsgDelayIT.java | 3 +-
9 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/pom.xml b/pom.xml
index feb8b14845..6fd59ac5bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -461,10 +461,6 @@
@{failsafeArgLine}**/NormalMsgDelayIT.java
- **/BroadCastNormalMsgNotRecvIT.java
- **/TagMessageWithSameGroupConsumerIT.java
- **/AsyncSendWithMessageQueueSelectorIT.java
- **/AsyncSendWithMessageQueueIT.java
diff --git a/test/src/main/java/org/apache/rocketmq/test/clientinterface/MQCollector.java b/test/src/main/java/org/apache/rocketmq/test/clientinterface/MQCollector.java
index 42d4b629b4..7ccf92a7c3 100644
--- a/test/src/main/java/org/apache/rocketmq/test/clientinterface/MQCollector.java
+++ b/test/src/main/java/org/apache/rocketmq/test/clientinterface/MQCollector.java
@@ -91,11 +91,21 @@ public abstract class MQCollector {
}
public void clearMsg() {
- msgBodys.resetData();
- originMsgs.resetData();
- errorMsgs.resetData();
- originMsgIndex.clear();
- msgRTs.resetData();
+ if (msgBodys != null) {
+ msgBodys.resetData();
+ }
+ if (originMsgs != null) {
+ originMsgs.resetData();
+ }
+ if (originMsgs != null) {
+ errorMsgs.resetData();
+ }
+ if (originMsgIndex != null) {
+ originMsgIndex.clear();
+ }
+ if (msgRTs != null) {
+ msgRTs.resetData();
+ }
}
public void lockCollectors() {
diff --git a/test/src/main/java/org/apache/rocketmq/test/listener/rmq/concurrent/RMQNormalListner.java b/test/src/main/java/org/apache/rocketmq/test/listener/rmq/concurrent/RMQNormalListner.java
index 0d408810a1..471fb4817e 100644
--- a/test/src/main/java/org/apache/rocketmq/test/listener/rmq/concurrent/RMQNormalListner.java
+++ b/test/src/main/java/org/apache/rocketmq/test/listener/rmq/concurrent/RMQNormalListner.java
@@ -63,7 +63,9 @@ public class RMQNormalListner extends AbstractListener implements MessageListene
msgBodys.addData(new String(msg.getBody()));
originMsgs.addData(msg);
- originMsgIndex.put(new String(msg.getBody()), msg);
+ if (originMsgIndex != null) {
+ originMsgIndex.put(new String(msg.getBody()), msg);
+ }
}
return consumeStatus;
}
diff --git a/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java b/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java
index 680780aeb5..bd151d0561 100644
--- a/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java
+++ b/test/src/main/java/org/apache/rocketmq/test/util/MQAdmin.java
@@ -45,7 +45,6 @@ public class MQAdmin {
mqAdminExt.start();
mqAdminExt.createTopic(clusterName, topic, queueNum);
} catch (Exception e) {
- e.printStackTrace();
}
long startTime = System.currentTimeMillis();
diff --git a/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java b/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java
index 5329991a3e..9805eba7c4 100644
--- a/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java
+++ b/test/src/test/java/org/apache/rocketmq/test/base/IntegrationTestBase.java
@@ -46,6 +46,8 @@ public class IntegrationTestBase {
protected static final List BROKER_CONTROLLERS = new ArrayList<>();
protected static final List NAMESRV_CONTROLLERS = new ArrayList<>();
protected static int topicCreateTime = 30 * 1000;
+ protected static final int COMMIT_LOG_SIZE = 1024 * 1024 * 256;
+ protected static final int INDEX_NUM = 1000;
protected static Random random = new Random();
@@ -53,18 +55,30 @@ public class IntegrationTestBase {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override public void run() {
- for (NamesrvController namesrvController : NAMESRV_CONTROLLERS) {
- if (namesrvController != null) {
- namesrvController.shutdown();
+ try {
+ for (BrokerController brokerController : BROKER_CONTROLLERS) {
+ if (brokerController != null) {
+ brokerController.shutdown();
+ }
}
- }
- for (BrokerController brokerController : BROKER_CONTROLLERS) {
- if (brokerController != null) {
- brokerController.shutdown();
+
+ // should destroy message store, otherwise could not delete the temp files.
+ for (BrokerController brokerController : BROKER_CONTROLLERS) {
+ if (brokerController != null) {
+ brokerController.getMessageStore().destroy();
+ }
}
- }
- for (File file : TMPE_FILES) {
- deleteFile(file);
+
+ for (NamesrvController namesrvController : NAMESRV_CONTROLLERS) {
+ if (namesrvController != null) {
+ namesrvController.shutdown();
+ }
+ }
+ for (File file : TMPE_FILES) {
+ deleteFile(file);
+ }
+ } catch (Exception e){
+ logger.error("Shutdown error", e);
}
}
});
@@ -75,7 +89,7 @@ public class IntegrationTestBase {
String baseDir = System.getProperty("user.home") + SEP + "unitteststore-" + UUID.randomUUID();
final File file = new File(baseDir);
if (file.exists()) {
- logger.info(String.format("[%s] has already existed, please bake up and remove it for integration tests", baseDir));
+ logger.info(String.format("[%s] has already existed, please back up and remove it for integration tests", baseDir));
System.exit(1);
}
TMPE_FILES.add(file);
@@ -116,6 +130,9 @@ public class IntegrationTestBase {
storeConfig.setStorePathRootDir(baseDir);
storeConfig.setStorePathCommitLog(baseDir + SEP + "commitlog");
storeConfig.setHaListenPort(8000 + random.nextInt(1000));
+ storeConfig.setMapedFileSizeCommitLog(COMMIT_LOG_SIZE);
+ storeConfig.setMaxIndexNum(INDEX_NUM);
+ storeConfig.setMaxHashSlotNum(INDEX_NUM * 4);
nettyServerConfig.setListenPort(10000 + random.nextInt(1000));
BrokerController brokerController = new BrokerController(brokerConfig, nettyServerConfig, nettyClientConfig, storeConfig);
try {
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java
index 4cf8161e39..135cbec8bd 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithSameGroupConsumerIT.java
@@ -36,6 +36,7 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
private static Logger logger = Logger.getLogger(TagMessageWith1ConsumerIT.class);
private RMQNormalProducer producer = null;
private String topic = null;
+ private String tag = "tag";
@Before
public void setUp() {
@@ -51,13 +52,12 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
@Test
public void testTwoConsumerWithSameGroup() {
- String tag = "jueyin";
int msgSize = 20;
String originMsgDCName = RandomUtils.getStringByUUID();
String msgBodyDCName = RandomUtils.getStringByUUID();
RMQNormalConsumer consumer1 = getConsumer(nsAddr, topic, tag,
new RMQNormalListner(originMsgDCName, msgBodyDCName));
- RMQNormalConsumer consumer2 = getConsumer(nsAddr, consumer1.getConsumerGroup(), tag,
+ getConsumer(nsAddr, consumer1.getConsumerGroup(), tag,
new RMQNormalListner(originMsgDCName, msgBodyDCName));
producer.send(tag, msgSize);
Assert.assertEquals("Not all are sent", msgSize, producer.getAllUndupMsgBody().size());
@@ -70,7 +70,6 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
@Test
public void testConsumerStartWithInterval() {
- String tag = "jueyin";
int msgSize = 100;
String originMsgDCName = RandomUtils.getStringByUUID();
String msgBodyDCName = RandomUtils.getStringByUUID();
@@ -79,7 +78,7 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
new RMQNormalListner(originMsgDCName, msgBodyDCName));
producer.send(tag, msgSize, 100);
TestUtils.waitForMoment(5);
- RMQNormalConsumer consumer2 = getConsumer(nsAddr, consumer1.getConsumerGroup(), tag,
+ getConsumer(nsAddr, consumer1.getConsumerGroup(), tag,
new RMQNormalListner(originMsgDCName, msgBodyDCName));
TestUtils.waitForMoment(5);
@@ -90,8 +89,7 @@ public class TagMessageWithSameGroupConsumerIT extends BaseConf {
}
@Test
- public void testConsumerStartTwoAndCrashOnsAfterWhile() {
- String tag = "jueyin";
+ public void testConsumerStartTwoAndCrashOneAfterWhile() {
int msgSize = 100;
String originMsgDCName = RandomUtils.getStringByUUID();
String msgBodyDCName = RandomUtils.getStringByUUID();
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueIT.java b/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueIT.java
index 53a992c323..24a75473ad 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueIT.java
@@ -33,7 +33,6 @@ import static com.google.common.truth.Truth.assertThat;
public class AsyncSendWithMessageQueueIT extends BaseConf {
private static Logger logger = Logger.getLogger(TagMessageWith1ConsumerIT.class);
- private static boolean sendFail = false;
private RMQAsyncSendProducer producer = null;
private String topic = null;
@@ -57,7 +56,7 @@ public class AsyncSendWithMessageQueueIT extends BaseConf {
MessageQueue mq = new MessageQueue(topic, broker1Name, queueId);
producer.asyncSend(msgSize, mq);
- producer.waitForResponse(5 * 1000);
+ producer.waitForResponse(10 * 1000);
assertThat(producer.getSuccessMsgCount()).isEqualTo(msgSize);
consumer.getListner().waitForMessageConsume(producer.getAllMsgBody(), consumeTime);
@@ -72,7 +71,7 @@ public class AsyncSendWithMessageQueueIT extends BaseConf {
mq = new MessageQueue(topic, broker2Name, queueId);
producer.asyncSend(msgSize, mq);
- producer.waitForResponse(5 * 1000);
+ producer.waitForResponse(10 * 1000);
assertThat(producer.getSuccessMsgCount()).isEqualTo(msgSize);
consumer.getListner().waitForMessageConsume(producer.getAllMsgBody(), consumeTime);
diff --git a/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueSelectorIT.java b/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueSelectorIT.java
index 68c2b0e2dc..843441d3f3 100644
--- a/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueSelectorIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/client/producer/async/AsyncSendWithMessageQueueSelectorIT.java
@@ -36,7 +36,6 @@ import static com.google.common.truth.Truth.assertThat;
public class AsyncSendWithMessageQueueSelectorIT extends BaseConf {
private static Logger logger = Logger.getLogger(TagMessageWith1ConsumerIT.class);
- private static boolean sendFail = false;
private RMQAsyncSendProducer producer = null;
private String topic = null;
diff --git a/test/src/test/java/org/apache/rocketmq/test/delay/NormalMsgDelayIT.java b/test/src/test/java/org/apache/rocketmq/test/delay/NormalMsgDelayIT.java
index 5206dcb6e1..dc5f230253 100644
--- a/test/src/test/java/org/apache/rocketmq/test/delay/NormalMsgDelayIT.java
+++ b/test/src/test/java/org/apache/rocketmq/test/delay/NormalMsgDelayIT.java
@@ -24,7 +24,6 @@ import org.apache.rocketmq.test.client.rmq.RMQNormalConsumer;
import org.apache.rocketmq.test.client.rmq.RMQNormalProducer;
import org.apache.rocketmq.test.factory.MQMessageFactory;
import org.apache.rocketmq.test.listener.rmq.concurrent.RMQDelayListner;
-import org.apache.rocketmq.test.listener.rmq.order.RMQOrderListener;
import org.apache.rocketmq.test.util.VerifyUtils;
import org.junit.After;
import org.junit.Assert;
@@ -43,7 +42,7 @@ public class NormalMsgDelayIT extends DelayConf {
topic = initTopic();
logger.info(String.format("use topic: %s;", topic));
producer = getProducer(nsAddr, topic);
- consumer = getConsumer(nsAddr, topic, "*", new RMQOrderListener());
+ consumer = getConsumer(nsAddr, topic, "*", new RMQDelayListner());
}
@After
From 9d983ead6583a5ef60f87f747b1a3e25597704c9 Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Tue, 25 Apr 2017 22:58:14 +0800
Subject: [PATCH 36/74] Add javadoc to NettyRemotingAbstract class and several
other trivial clean up.
---
.../remoting/netty/NettyRemotingAbstract.java | 91 +++++++++++++++++--
.../remoting/netty/NettyRemotingClient.java | 19 ++--
.../remoting/netty/NettyRemotingServer.java | 22 ++---
3 files changed, 105 insertions(+), 27 deletions(-)
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
index 83eeb02da4..cddab3d738 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
@@ -48,32 +48,84 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class NettyRemotingAbstract {
+
+ /**
+ * Remoting logger instance.
+ */
private static final Logger PLOG = LoggerFactory.getLogger(RemotingHelper.ROCKETMQ_REMOTING);
+ /**
+ * Semaphore to limit maximum number of on-going one-way requests, which protects system memory footprint.
+ */
protected final Semaphore semaphoreOneway;
+ /**
+ * Semaphore to limit maximum number of on-going asynchronous requests, which protects system memory footprint.
+ */
protected final Semaphore semaphoreAsync;
+ /**
+ * This map caches all on-going requests.
+ */
protected final ConcurrentHashMap responseTable =
new ConcurrentHashMap(256);
+ /**
+ * This container holds all processors per request code, aka, for each incoming request, we may look up the
+ * responding processor in this map to handle the request.
+ */
protected final HashMap> processorTable =
new HashMap>(64);
- protected final NettyEventExecuter nettyEventExecuter = new NettyEventExecuter();
+ /**
+ * Executor to feed netty events to user defined {@link ChannelEventListener}.
+ */
+ protected final NettyEventExecutor nettyEventExecutor = new NettyEventExecutor();
+
+ /**
+ * The default request processor to use in case there is no exact match in {@link #processorTable} per request code.
+ */
protected Pair defaultRequestProcessor;
+ /**
+ * Constructor, specifying capacity of one-way and asynchronous semaphores.
+ * @param permitsOneway Number of permits for one-way requests.
+ * @param permitsAsync Number of permits for asynchronous requests.
+ */
public NettyRemotingAbstract(final int permitsOneway, final int permitsAsync) {
this.semaphoreOneway = new Semaphore(permitsOneway, true);
this.semaphoreAsync = new Semaphore(permitsAsync, true);
}
+ /**
+ * Custom channel event listener.
+ * @return custom channel event listener if defined; null otherwise.
+ */
public abstract ChannelEventListener getChannelEventListener();
+ /**
+ * Put a netty event to the executor.
+ * @param event Netty event instance.
+ */
public void putNettyEvent(final NettyEvent event) {
- this.nettyEventExecuter.putNettyEvent(event);
+ this.nettyEventExecutor.putNettyEvent(event);
}
+ /**
+ * Entry of incoming command processing.
+ *
+ *
+ * Note:
+ * The incoming remoting command may be
+ *
+ *
An inquiry request from a remote peer component;
+ *
A response to a previous request issued by this very participant.
+ *
+ *
+ * @param ctx Channel handler context.
+ * @param msg incoming remoting command.
+ * @throws Exception if there were any error while processing the incoming command.
+ */
public void processMessageReceived(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
final RemotingCommand cmd = msg;
if (cmd != null) {
@@ -90,6 +142,11 @@ public abstract class NettyRemotingAbstract {
}
}
+ /**
+ * Process incoming request command issued by remote peer.
+ * @param ctx channel handler context.
+ * @param cmd request command.
+ */
public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {
final Pair matched = this.processorTable.get(cmd.getCode());
final Pair pair = null == matched ? this.defaultRequestProcessor : matched;
@@ -175,6 +232,11 @@ public abstract class NettyRemotingAbstract {
}
}
+ /**
+ * Process response from remote peer to the previous issued requests.
+ * @param ctx channel handler context.
+ * @param cmd response command instance.
+ */
public void processResponseCommand(ChannelHandlerContext ctx, RemotingCommand cmd) {
final int opaque = cmd.getOpaque();
final ResponseFuture responseFuture = responseTable.get(opaque);
@@ -196,7 +258,10 @@ public abstract class NettyRemotingAbstract {
}
}
- //execute callback in callback executor. If callback executor is null, run directly in current thread
+ /**
+ * Execute callback in callback executor. If callback executor is null, run directly in current thread
+ * @param responseFuture
+ */
private void executeInvokeCallback(final ResponseFuture responseFuture) {
boolean runInThisThread = false;
ExecutorService executor = this.getCallbackExecutor();
@@ -229,10 +294,24 @@ public abstract class NettyRemotingAbstract {
}
}
+ /**
+ * Custom RPC hook.
+ * @return RPC hook if specified; null otherwise.
+ */
public abstract RPCHook getRPCHook();
- abstract public ExecutorService getCallbackExecutor();
+ /**
+ * This method specifies thread pool to use while invoking callback methods.
+ * @return Dedicated thread pool instance if specified; or null if the callback is supposed to be executed in the
+ * netty event-loop thread.
+ */
+ public abstract ExecutorService getCallbackExecutor();
+ /**
+ *
+ * This method is periodically invoked to scan and expire deprecated request.
+ *
+ */
public void scanResponseTable() {
final List rfList = new LinkedList();
Iterator> it = this.responseTable.entrySet().iterator();
@@ -386,7 +465,7 @@ public abstract class NettyRemotingAbstract {
}
}
- class NettyEventExecuter extends ServiceThread {
+ class NettyEventExecutor extends ServiceThread {
private final LinkedBlockingQueue eventQueue = new LinkedBlockingQueue();
private final int maxSize = 10000;
@@ -436,7 +515,7 @@ public abstract class NettyRemotingAbstract {
@Override
public String getServiceName() {
- return NettyEventExecuter.class.getSimpleName();
+ return NettyEventExecutor.class.getSimpleName();
}
}
}
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 26088aa5a5..52ca47e6c2 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
@@ -172,7 +172,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
}, 1000 * 3, 1000);
if (this.channelEventListener != null) {
- this.nettyEventExecuter.start();
+ this.nettyEventExecutor.start();
}
}
@@ -189,8 +189,8 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
this.eventLoopGroupWorker.shutdownGracefully();
- if (this.nettyEventExecuter != null) {
- this.nettyEventExecuter.shutdown();
+ if (this.nettyEventExecutor != null) {
+ this.nettyEventExecutor.shutdown();
}
if (this.defaultEventExecutorGroup != null) {
@@ -586,7 +586,6 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
processMessageReceived(ctx, msg);
-
}
}
@@ -594,8 +593,8 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
- final String local = localAddress == null ? "UNKNOWN" : localAddress.toString();
- final String remote = remoteAddress == null ? "UNKNOWN" : remoteAddress.toString();
+ final String local = localAddress == null ? "UNKNOWN" : RemotingHelper.parseSocketAddressAddr(localAddress);
+ final String remote = remoteAddress == null ? "UNKNOWN" : RemotingHelper.parseSocketAddressAddr(remoteAddress);
log.info("NETTY CLIENT PIPELINE: CONNECT {} => {}", local, remote);
super.connect(ctx, remoteAddress, localAddress, promise);
@@ -613,7 +612,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
super.disconnect(ctx, promise);
if (NettyRemotingClient.this.channelEventListener != null) {
- NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress.toString(), ctx.channel()));
+ NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress, ctx.channel()));
}
}
@@ -625,7 +624,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
super.close(ctx, promise);
if (NettyRemotingClient.this.channelEventListener != null) {
- NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress.toString(), ctx.channel()));
+ NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress, ctx.channel()));
}
}
@@ -639,7 +638,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
closeChannel(ctx.channel());
if (NettyRemotingClient.this.channelEventListener != null) {
NettyRemotingClient.this
- .putNettyEvent(new NettyEvent(NettyEventType.IDLE, remoteAddress.toString(), ctx.channel()));
+ .putNettyEvent(new NettyEvent(NettyEventType.IDLE, remoteAddress, ctx.channel()));
}
}
}
@@ -654,7 +653,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti
log.warn("NETTY CLIENT PIPELINE: exceptionCaught exception.", cause);
closeChannel(ctx.channel());
if (NettyRemotingClient.this.channelEventListener != null) {
- NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.EXCEPTION, remoteAddress.toString(), ctx.channel()));
+ NettyRemotingClient.this.putNettyEvent(new NettyEvent(NettyEventType.EXCEPTION, remoteAddress, ctx.channel()));
}
}
}
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
index 6a6df374b2..d8d9b6518b 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
@@ -160,7 +160,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
new NettyEncoder(),
new NettyDecoder(),
new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
- new NettyConnetManageHandler(),
+ new NettyConnectManageHandler(),
new NettyServerHandler());
}
});
@@ -178,7 +178,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
}
if (this.channelEventListener != null) {
- this.nettyEventExecuter.start();
+ this.nettyEventExecutor.start();
}
this.timer.scheduleAtFixedRate(new TimerTask() {
@@ -205,8 +205,8 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
this.eventLoopGroupSelector.shutdownGracefully();
- if (this.nettyEventExecuter != null) {
- this.nettyEventExecuter.shutdown();
+ if (this.nettyEventExecutor != null) {
+ this.nettyEventExecutor.shutdown();
}
if (this.defaultEventExecutorGroup != null) {
@@ -297,7 +297,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
}
}
- class NettyConnetManageHandler extends ChannelDuplexHandler {
+ class NettyConnectManageHandler extends ChannelDuplexHandler {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
@@ -319,7 +319,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
super.channelActive(ctx);
if (NettyRemotingServer.this.channelEventListener != null) {
- NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.CONNECT, remoteAddress.toString(), ctx.channel()));
+ NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.CONNECT, remoteAddress, ctx.channel()));
}
}
@@ -330,21 +330,21 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
super.channelInactive(ctx);
if (NettyRemotingServer.this.channelEventListener != null) {
- NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress.toString(), ctx.channel()));
+ NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.CLOSE, remoteAddress, ctx.channel()));
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
- IdleStateEvent evnet = (IdleStateEvent) evt;
- if (evnet.state().equals(IdleState.ALL_IDLE)) {
+ IdleStateEvent event = (IdleStateEvent) evt;
+ if (event.state().equals(IdleState.ALL_IDLE)) {
final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
log.warn("NETTY SERVER PIPELINE: IDLE exception [{}]", remoteAddress);
RemotingUtil.closeChannel(ctx.channel());
if (NettyRemotingServer.this.channelEventListener != null) {
NettyRemotingServer.this
- .putNettyEvent(new NettyEvent(NettyEventType.IDLE, remoteAddress.toString(), ctx.channel()));
+ .putNettyEvent(new NettyEvent(NettyEventType.IDLE, remoteAddress, ctx.channel()));
}
}
}
@@ -359,7 +359,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
log.warn("NETTY SERVER PIPELINE: exceptionCaught exception.", cause);
if (NettyRemotingServer.this.channelEventListener != null) {
- NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.EXCEPTION, remoteAddress.toString(), ctx.channel()));
+ NettyRemotingServer.this.putNettyEvent(new NettyEvent(NettyEventType.EXCEPTION, remoteAddress, ctx.channel()));
}
RemotingUtil.closeChannel(ctx.channel());
From 07b3f41b275776cdd4f43c1ff0f26cf817f332cb Mon Sep 17 00:00:00 2001
From: shroman
Date: Thu, 4 May 2017 20:07:10 +0900
Subject: [PATCH 37/74] Removed unnecessary semicolon.
---
.../apache/rocketmq/filter/expression/UnaryInExpression.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
index 7d9083c09f..a6cf173008 100644
--- a/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
@@ -58,4 +58,4 @@ abstract public class UnaryInExpression extends UnaryExpression implements Boole
public void setInList(Collection inList) {
this.inList = inList;
}
-};
+}
From 0224f93d0201411be72735336f1f0e25809ffe6f Mon Sep 17 00:00:00 2001
From: shroman
Date: Thu, 4 May 2017 20:45:11 +0900
Subject: [PATCH 38/74] Removed author info and formatted.
---
.../common/MessageEncodeDecodeTest.java | 30 ++++++++-----------
1 file changed, 12 insertions(+), 18 deletions(-)
diff --git a/common/src/test/java/org/apache/rocketmq/common/MessageEncodeDecodeTest.java b/common/src/test/java/org/apache/rocketmq/common/MessageEncodeDecodeTest.java
index a219edac48..42d3909d99 100644
--- a/common/src/test/java/org/apache/rocketmq/common/MessageEncodeDecodeTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/MessageEncodeDecodeTest.java
@@ -6,39 +6,33 @@
* (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
+ * 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.
+ * 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;
-import org.apache.rocketmq.common.message.Message;
-import org.apache.rocketmq.common.message.MessageDecoder;
-import org.junit.Test;
-
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.junit.Test;
import static org.junit.Assert.assertTrue;
-/**
- * Created by liuzhendong on 16/12/21.
- */
public class MessageEncodeDecodeTest {
-
@Test
- public void testEncodeDecodeSingle() throws Exception{
+ public void testEncodeDecodeSingle() throws Exception {
Message message = new Message("topic", "body".getBytes());
message.setFlag(12);
- message.putUserProperty("key","value");
+ message.putUserProperty("key", "value");
byte[] bytes = MessageDecoder.encodeMessage(message);
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
@@ -59,7 +53,7 @@ public class MessageEncodeDecodeTest {
message.putUserProperty("key", "value" + i);
messages.add(message);
}
- byte[] bytes = MessageDecoder.encodeMessages(messages);
+ byte[] bytes = MessageDecoder.encodeMessages(messages);
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
From 06416b00f8f1a1871d234cde7129d373b3e3de5b Mon Sep 17 00:00:00 2001
From: shroman
Date: Thu, 4 May 2017 21:03:38 +0900
Subject: [PATCH 39/74] Changed list creation at DynaCode() to a singleton.
---
.../java/org/apache/rocketmq/filtersrv/filter/DynaCode.java | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/filter/DynaCode.java b/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/filter/DynaCode.java
index a57b57f38c..e0a94d70af 100644
--- a/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/filter/DynaCode.java
+++ b/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/filter/DynaCode.java
@@ -28,7 +28,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -71,9 +71,8 @@ public class DynaCode {
private String target;
- @SuppressWarnings("unchecked")
public DynaCode(String code) {
- this(Thread.currentThread().getContextClassLoader(), Arrays.asList(code));
+ this(Thread.currentThread().getContextClassLoader(), Collections.singletonList(code));
}
public DynaCode(ClassLoader parentClassLoader, List codeStrs) {
From 482def9e6a9032111d259f2ac82d37e5111d9b8b Mon Sep 17 00:00:00 2001
From: Zhanhui Li
Date: Sun, 7 May 2017 23:07:03 +0800
Subject: [PATCH 40/74] Add javadoc to message store.
---
.../broker/client/net/Broker2Client.java | 2 +-
.../longpolling/PullRequestHoldService.java | 4 +-
.../broker/offset/ConsumerOffsetManager.java | 4 +-
.../plugin/AbstractPluginMessageStore.java | 20 +-
.../processor/AdminBrokerProcessor.java | 16 +-
.../processor/ConsumerManageProcessor.java | 2 +-
.../rocketmq/store/DefaultMessageStore.java | 16 +-
.../apache/rocketmq/store/MessageStore.java | 227 +++++++++++++++++-
.../schedule/ScheduleMessageService.java | 2 +-
9 files changed, 253 insertions(+), 40 deletions(-)
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java b/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java
index c00898c3c4..863da627e7 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java
@@ -149,7 +149,7 @@ public class Broker2Client {
long timeStampOffset;
if (timeStamp == -1) {
- timeStampOffset = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, i);
+ timeStampOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
} else {
timeStampOffset = this.brokerController.getMessageStore().getOffsetInQueueByTime(topic, i, timeStamp);
}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
index 1a53db198b..71f56a4bd3 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
@@ -98,7 +98,7 @@ public class PullRequestHoldService extends ServiceThread {
if (2 == kArray.length) {
String topic = kArray[0];
int queueId = Integer.parseInt(kArray[1]);
- final long offset = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, queueId);
+ final long offset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId);
try {
this.notifyMessageArriving(topic, queueId, offset);
} catch (Throwable e) {
@@ -124,7 +124,7 @@ public class PullRequestHoldService extends ServiceThread {
for (PullRequest request : requestList) {
long newestOffset = maxOffset;
if (newestOffset <= request.getPullFromThisOffset()) {
- newestOffset = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, queueId);
+ newestOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId);
}
if (newestOffset > request.getPullFromThisOffset()) {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java b/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java
index bdcf30c40e..769c4ad0fb 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java
@@ -73,7 +73,7 @@ public class ConsumerOffsetManager extends ConfigManager {
while (it.hasNext() && result) {
Entry next = it.next();
- long minOffsetInStore = this.brokerController.getMessageStore().getMinOffsetInQuque(topic, next.getKey());
+ long minOffsetInStore = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, next.getKey());
long offsetInPersist = next.getValue();
result = offsetInPersist <= minOffsetInStore;
}
@@ -201,7 +201,7 @@ public class ConsumerOffsetManager extends ConfigManager {
String[] topicGroupArr = topicGroup.split(TOPIC_GROUP_SEPARATOR);
if (topic.equals(topicGroupArr[0])) {
for (Entry entry : offSetEntry.getValue().entrySet()) {
- long minOffset = this.brokerController.getMessageStore().getMinOffsetInQuque(topic, entry.getKey());
+ long minOffset = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, entry.getKey());
if (entry.getValue() >= minOffset) {
Long offset = queueMinOffset.get(entry.getKey());
if (offset == null) {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
index 8ded973e0e..690f70bfc8 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
@@ -92,18 +92,18 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
}
@Override
- public long getMaxOffsetInQuque(String topic, int queueId) {
- return next.getMaxOffsetInQuque(topic, queueId);
+ public long getMaxOffsetInQueue(String topic, int queueId) {
+ return next.getMaxOffsetInQueue(topic, queueId);
}
@Override
- public long getMinOffsetInQuque(String topic, int queueId) {
- return next.getMinOffsetInQuque(topic, queueId);
+ public long getMinOffsetInQueue(String topic, int queueId) {
+ return next.getMinOffsetInQueue(topic, queueId);
}
@Override
- public long getCommitLogOffsetInQueue(String topic, int queueId, long cqOffset) {
- return next.getCommitLogOffsetInQueue(topic, queueId, cqOffset);
+ public long getCommitLogOffsetInQueue(String topic, int queueId, long consumeQueueOffset) {
+ return next.getCommitLogOffsetInQueue(topic, queueId, consumeQueueOffset);
}
@Override
@@ -152,8 +152,8 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
}
@Override
- public long getMessageStoreTimeStamp(String topic, int queueId, long offset) {
- return next.getMessageStoreTimeStamp(topic, queueId, offset);
+ public long getMessageStoreTimeStamp(String topic, int queueId, long consumeQueueOffset) {
+ return next.getMessageStoreTimeStamp(topic, queueId, consumeQueueOffset);
}
@Override
@@ -172,8 +172,8 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
}
@Override
- public void excuteDeleteFilesManualy() {
- next.excuteDeleteFilesManualy();
+ public void executeDeleteFilesManually() {
+ next.executeDeleteFilesManually();
}
@Override
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 daea53c7de..f59d2952e1 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
@@ -376,7 +376,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
final GetMaxOffsetRequestHeader requestHeader =
(GetMaxOffsetRequestHeader) request.decodeCommandCustomHeader(GetMaxOffsetRequestHeader.class);
- long offset = this.brokerController.getMessageStore().getMaxOffsetInQuque(requestHeader.getTopic(), requestHeader.getQueueId());
+ long offset = this.brokerController.getMessageStore().getMaxOffsetInQueue(requestHeader.getTopic(), requestHeader.getQueueId());
responseHeader.setOffset(offset);
@@ -391,7 +391,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
final GetMinOffsetRequestHeader requestHeader =
(GetMinOffsetRequestHeader) request.decodeCommandCustomHeader(GetMinOffsetRequestHeader.class);
- long offset = this.brokerController.getMessageStore().getMinOffsetInQuque(requestHeader.getTopic(), requestHeader.getQueueId());
+ long offset = this.brokerController.getMessageStore().getMinOffsetInQueue(requestHeader.getTopic(), requestHeader.getQueueId());
responseHeader.setOffset(offset);
response.setCode(ResponseCode.SUCCESS);
@@ -537,11 +537,11 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
mq.setQueueId(i);
TopicOffset topicOffset = new TopicOffset();
- long min = this.brokerController.getMessageStore().getMinOffsetInQuque(topic, i);
+ long min = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, i);
if (min < 0)
min = 0;
- long max = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, i);
+ long max = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
if (max < 0)
max = 0;
@@ -679,7 +679,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
OffsetWrapper offsetWrapper = new OffsetWrapper();
- long brokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, i);
+ long brokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
if (brokerOffset < 0)
brokerOffset = 0;
@@ -862,7 +862,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
long minTime = this.brokerController.getMessageStore().getEarliestMessageTime(topic, i);
timeSpan.setMinTimeStamp(minTime);
- long max = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, i);
+ long max = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
long maxTime = this.brokerController.getMessageStore().getMessageStoreTimeStamp(topic, i, max - 1);
timeSpan.setMaxTimeStamp(maxTime);
@@ -876,7 +876,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
}
timeSpan.setConsumeTimeStamp(consumeTime);
- long maxBrokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQuque(requestHeader.getTopic(), i);
+ long maxBrokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(requestHeader.getTopic(), i);
if (consumerOffset < maxBrokerOffset) {
long nextTime = this.brokerController.getMessageStore().getMessageStoreTimeStamp(topic, i, consumerOffset);
timeSpan.setDelayTime(System.currentTimeMillis() - nextTime);
@@ -1126,7 +1126,7 @@ public class AdminBrokerProcessor implements NettyRequestProcessor {
mq.setBrokerName(this.brokerController.getBrokerConfig().getBrokerName());
mq.setQueueId(i);
OffsetWrapper offsetWrapper = new OffsetWrapper();
- long brokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQuque(topic, i);
+ long brokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
if (brokerOffset < 0)
brokerOffset = 0;
long consumerOffset = this.brokerController.getConsumerOffsetManager().queryOffset(//
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java
index 2c1029c555..bb427050d9 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java
@@ -135,7 +135,7 @@ public class ConsumerManageProcessor implements NettyRequestProcessor {
response.setRemark(null);
} else {
long minOffset =
- this.brokerController.getMessageStore().getMinOffsetInQuque(requestHeader.getTopic(),
+ this.brokerController.getMessageStore().getMinOffsetInQueue(requestHeader.getTopic(),
requestHeader.getQueueId());
if (minOffset <= 0
&& !this.brokerController.getMessageStore().checkInDiskByConsumeOffset(
diff --git a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
index 7bed62c7c5..931edc7656 100644
--- a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
+++ b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java
@@ -580,7 +580,7 @@ public class DefaultMessageStore implements MessageStore {
/**
*/
- public long getMaxOffsetInQuque(String topic, int queueId) {
+ public long getMaxOffsetInQueue(String topic, int queueId) {
ConsumeQueue logic = this.findConsumeQueue(topic, queueId);
if (logic != null) {
long offset = logic.getMaxOffsetInQueue();
@@ -593,7 +593,7 @@ public class DefaultMessageStore implements MessageStore {
/**
*/
- public long getMinOffsetInQuque(String topic, int queueId) {
+ public long getMinOffsetInQueue(String topic, int queueId) {
ConsumeQueue logic = this.findConsumeQueue(topic, queueId);
if (logic != null) {
return logic.getMinOffsetInQueue();
@@ -603,10 +603,10 @@ public class DefaultMessageStore implements MessageStore {
}
@Override
- public long getCommitLogOffsetInQueue(String topic, int queueId, long cqOffset) {
+ public long getCommitLogOffsetInQueue(String topic, int queueId, long consumeQueueOffset) {
ConsumeQueue consumeQueue = findConsumeQueue(topic, queueId);
if (consumeQueue != null) {
- SelectMappedBufferResult bufferConsumeQueue = consumeQueue.getIndexBuffer(cqOffset);
+ SelectMappedBufferResult bufferConsumeQueue = consumeQueue.getIndexBuffer(consumeQueueOffset);
if (bufferConsumeQueue != null) {
try {
long offsetPy = bufferConsumeQueue.getByteBuffer().getLong();
@@ -740,10 +740,10 @@ public class DefaultMessageStore implements MessageStore {
}
@Override
- public long getMessageStoreTimeStamp(String topic, int queueId, long offset) {
+ public long getMessageStoreTimeStamp(String topic, int queueId, long consumeQueueOffset) {
ConsumeQueue logicQueue = this.findConsumeQueue(topic, queueId);
if (logicQueue != null) {
- SelectMappedBufferResult result = logicQueue.getIndexBuffer(offset);
+ SelectMappedBufferResult result = logicQueue.getIndexBuffer(consumeQueueOffset);
if (result != null) {
try {
final long phyOffset = result.getByteBuffer().getLong();
@@ -798,7 +798,7 @@ public class DefaultMessageStore implements MessageStore {
}
@Override
- public void excuteDeleteFilesManualy() {
+ public void executeDeleteFilesManually() {
this.cleanCommitLogService.excuteDeleteFilesManualy();
}
@@ -1434,7 +1434,7 @@ public class DefaultMessageStore implements MessageStore {
public void excuteDeleteFilesManualy() {
this.manualDeleteFileSeveralTimes = MAX_MANUAL_DELETE_FILE_TIMES;
- DefaultMessageStore.log.info("excuteDeleteFilesManualy was invoked");
+ DefaultMessageStore.log.info("executeDeleteFilesManually was invoked");
}
public void run() {
diff --git a/store/src/main/java/org/apache/rocketmq/store/MessageStore.java b/store/src/main/java/org/apache/rocketmq/store/MessageStore.java
index e841c083b5..55572ce10f 100644
--- a/store/src/main/java/org/apache/rocketmq/store/MessageStore.java
+++ b/store/src/main/java/org/apache/rocketmq/store/MessageStore.java
@@ -22,91 +22,304 @@ import java.util.Set;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageExtBatch;
+/**
+ * This class defines contracting interfaces to implement, allowing third-party vendor to use customized message store.
+ */
public interface MessageStore {
+ /**
+ * Load previously stored messages.
+ * @return true if success; false otherwise.
+ */
boolean load();
+ /**
+ * Launch this message store.
+ * @throws Exception if there is any error.
+ */
void start() throws Exception;
+ /**
+ * Shutdown this message store.
+ */
void shutdown();
+ /**
+ * Destroy this message store. Generally, all persistent files should be removed after invocation.
+ */
void destroy();
+ /**
+ * Store a message into store.
+ * @param msg Message instance to store
+ * @return result of store operation.
+ */
PutMessageResult putMessage(final MessageExtBrokerInner msg);
+ /**
+ * Store a batch of messages.
+ * @param messageExtBatch Message batch.
+ * @return result of storing batch messages.
+ */
PutMessageResult putMessages(final MessageExtBatch messageExtBatch);
+ /**
+ * Query at most maxMsgNums messages belonging to topic at queueId starting
+ * from given offset. Resulting messages will further be screened using provided message filter.
+ *
+ * @param group Consumer group that launches this query.
+ * @param topic Topic to query.
+ * @param queueId Queue ID to query.
+ * @param offset Logical offset to start from.
+ * @param maxMsgNums Maximum count of messages to query.
+ * @param messageFilter Message filter used to screen desired messages.
+ * @return Matched messages.
+ */
GetMessageResult getMessage(final String group, final String topic, final int queueId,
final long offset, final int maxMsgNums, final MessageFilter messageFilter);
- long getMaxOffsetInQuque(final String topic, final int queueId);
+ /**
+ * Get maximum offset of the topic queue.
+ * @param topic Topic name.
+ * @param queueId Queue ID.
+ * @return Maximum offset at present.
+ */
+ long getMaxOffsetInQueue(final String topic, final int queueId);
- long getMinOffsetInQuque(final String topic, final int queueId);
+ /**
+ * Get the minimum offset of the topic queue.
+ * @param topic Topic name.
+ * @param queueId Queue ID.
+ * @return Minimum offset at present.
+ */
+ long getMinOffsetInQueue(final String topic, final int queueId);
- long getCommitLogOffsetInQueue(final String topic, final int queueId, final long cqOffset);
+ /**
+ * Get the offset of the message in the commit log, which is also known as physical offset.
+ * @param topic Topic of the message to lookup.
+ * @param queueId Queue ID.
+ * @param consumeQueueOffset offset of consume queue.
+ * @return physical offset.
+ */
+ long getCommitLogOffsetInQueue(final String topic, final int queueId, final long consumeQueueOffset);
+ /**
+ * Look up the physical offset of the message whose store timestamp is as specified.
+ * @param topic Topic of the message.
+ * @param queueId Queue ID.
+ * @param timestamp Timestamp to look up.
+ * @return physical offset which matches.
+ */
long getOffsetInQueueByTime(final String topic, final int queueId, final long timestamp);
+ /**
+ * Look up the message by given commit log offset.
+ * @param commitLogOffset physical offset.
+ * @return Message whose physical offset is as specified.
+ */
MessageExt lookMessageByOffset(final long commitLogOffset);
+ /**
+ * Get one message from the specified commit log offset.
+ * @param commitLogOffset commit log offset.
+ * @return wrapped result of the message.
+ */
SelectMappedBufferResult selectOneMessageByOffset(final long commitLogOffset);
+ /**
+ * Get one message from the specified commit log offset.
+ * @param commitLogOffset commit log offset.
+ * @param msgSize message size.
+ * @return wrapped result of the message.
+ */
SelectMappedBufferResult selectOneMessageByOffset(final long commitLogOffset, final int msgSize);
+ /**
+ * Get the running information of this store.
+ * @return message store running info.
+ */
String getRunningDataInfo();
+ /**
+ * Message store runtime information, which should generally contains various statistical information.
+ * @return runtime information of the message store in format of key-value pairs.
+ */
HashMap getRuntimeInfo();
+ /**
+ * Get the maximum commit log offset.
+ * @return maximum commit log offset.
+ */
long getMaxPhyOffset();
+ /**
+ * Get the minimum commit log offset.
+ * @return minimum commit log offset.
+ */
long getMinPhyOffset();
+ /**
+ * Get the store time of the earliest message in the given queue.
+ * @param topic Topic of the messages to query.
+ * @param queueId Queue ID to find.
+ * @return store time of the earliest message.
+ */
long getEarliestMessageTime(final String topic, final int queueId);
+ /**
+ * Get the store time of the earliest message in this store.
+ * @return timestamp of the earliest message in this store.
+ */
long getEarliestMessageTime();
- long getMessageStoreTimeStamp(final String topic, final int queueId, final long offset);
+ /**
+ * Get the store time of the message specified.
+ * @param topic message topic.
+ * @param queueId queue ID.
+ * @param consumeQueueOffset consume queue offset.
+ * @return store timestamp of the message.
+ */
+ long getMessageStoreTimeStamp(final String topic, final int queueId, final long consumeQueueOffset);
+ /**
+ * Get the total number of the messages in the specified queue.
+ * @param topic Topic
+ * @param queueId Queue ID.
+ * @return total number.
+ */
long getMessageTotalInQueue(final String topic, final int queueId);
+ /**
+ * Get the raw commit log data starting from the given offset, which should used for replication purpose.
+ * @param offset starting offset.
+ * @return commit log data.
+ */
SelectMappedBufferResult getCommitLogData(final long offset);
+ /**
+ * Append data to commit log.
+ * @param startOffset starting offset.
+ * @param data data to append.
+ * @return true if success; false otherwise.
+ */
boolean appendToCommitLog(final long startOffset, final byte[] data);
- void excuteDeleteFilesManualy();
+ /**
+ * Execute file deletion manually.
+ */
+ void executeDeleteFilesManually();
- QueryMessageResult queryMessage(final String topic, final String key, final int maxNum,
- final long begin, final long end);
+ /**
+ * Query messages by given key.
+ * @param topic topic of the message.
+ * @param key message key.
+ * @param maxNum maximum number of the messages possible.
+ * @param begin begin timestamp.
+ * @param end end timestamp.
+ * @return
+ */
+ QueryMessageResult queryMessage(final String topic, final String key, final int maxNum, final long begin,
+ final long end);
+ /**
+ * Update HA master address.
+ * @param newAddr new address.
+ */
void updateHaMasterAddress(final String newAddr);
+ /**
+ * Return how much the slave falls behind.
+ * @return number of bytes that slave falls behind.
+ */
long slaveFallBehindMuch();
+ /**
+ * Return the current timestamp of the store.
+ * @return current time in milliseconds since 1970-01-01.
+ */
long now();
+ /**
+ * Clean unused topics.
+ * @param topics all valid topics.
+ * @return number of the topics deleted.
+ */
int cleanUnusedTopic(final Set topics);
+ /**
+ * Clean expired consume queues.
+ */
void cleanExpiredConsumerQueue();
+ /**
+ * Check if the given message has been swapped out of the memory.
+ * @param topic topic.
+ * @param queueId queue ID.
+ * @param consumeOffset consume queue offset.
+ * @return true if the message is no longer in memory; false otherwise.
+ */
boolean checkInDiskByConsumeOffset(final String topic, final int queueId, long consumeOffset);
+ /**
+ * Get number of the bytes that have been stored in commit log and not yet dispatched to consume queue.
+ * @return number of the bytes to dispatch.
+ */
long dispatchBehindBytes();
+ /**
+ * Flush the message store to persist all data.
+ * @return maximum offset flushed to persistent storage device.
+ */
long flush();
+ /**
+ * Reset written offset.
+ * @param phyOffset new offset.
+ * @return true if success; false otherwise.
+ */
boolean resetWriteOffset(long phyOffset);
+ /**
+ * Get confirm offset.
+ * @return confirm offset.
+ */
long getConfirmOffset();
+ /**
+ * Set confirm offset.
+ * @param phyOffset confirm offset to set.
+ */
void setConfirmOffset(long phyOffset);
+ /**
+ * Check if the operation system page cache is busy or not.
+ * @return true if the OS page cache is busy; false otherwise.
+ */
boolean isOSPageCacheBusy();
+ /**
+ * Get lock time in milliseconds of the store by far.
+ * @return lock time in milliseconds.
+ */
long lockTimeMills();
+ /**
+ * Check if the transient store pool is deficient.
+ * @return true if the transient store pool is running out; false otherwise.
+ */
boolean isTransientStorePoolDeficient();
+ /**
+ * Get the dispatcher list.
+ * @return list of the dispatcher.
+ */
LinkedList getDispatcherList();
+ /**
+ * Get consume queue of the topic/queue.
+ * @param topic Topic.
+ * @param queueId Queue ID.
+ * @return Consume queue.
+ */
ConsumeQueue getConsumeQueue(String topic, int queueId);
}
diff --git a/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java b/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java
index d45b994a3b..501876ed29 100644
--- a/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java
+++ b/store/src/main/java/org/apache/rocketmq/store/schedule/ScheduleMessageService.java
@@ -79,7 +79,7 @@ public class ScheduleMessageService extends ConfigManager {
Entry next = it.next();
int queueId = delayLevel2QueueId(next.getKey());
long delayOffset = next.getValue();
- long maxOffset = this.defaultMessageStore.getMaxOffsetInQuque(SCHEDULE_TOPIC, queueId);
+ long maxOffset = this.defaultMessageStore.getMaxOffsetInQueue(SCHEDULE_TOPIC, queueId);
String value = String.format("%d,%d", delayOffset, maxOffset);
String key = String.format("%s_%d", RunningStats.scheduleMessageOffset.name(), next.getKey());
stats.put(key, value);
From 4f7fc91c3dee9ff7674c138b1d9d6c2f7da340b1 Mon Sep 17 00:00:00 2001
From: dongeforever
Date: Tue, 9 May 2017 23:38:56 +0800
Subject: [PATCH 41/74] [ROCKETMQ-187] Measure the code coverage for
Integration Tests, and add sonar-apache profile, closes
apache/incubator-rocketmq#96
---
.travis.yml | 2 +-
pom.xml | 13 ++++++++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 916cac5765..2bc2296681 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,4 +40,4 @@ script:
after_success:
- mvn clean install -Pit-test
- - mvn sonar:sonar
+ - mvn sonar:sonar -Psonar-apache
diff --git a/pom.xml b/pom.xml
index 6fd59ac5bc..05ead63974 100644
--- a/pom.xml
+++ b/pom.xml
@@ -161,10 +161,10 @@
1.71.7jacoco
-
- https://builds.apache.org/analysis
- file:**/generated-sources/**
+ ${project.basedir}/../test/target/jacoco-it.exec
+ file:**/generated-sources/**,**/test/**
+
@@ -475,6 +475,13 @@
+
+ sonar-apache
+
+
+ https://builds.apache.org/analysis
+
+
From 59807cbe0c310b1284a211f10cb89b837780a6e3 Mon Sep 17 00:00:00 2001
From: Li Zhanhui
Date: Wed, 10 May 2017 10:44:34 +0800
Subject: [PATCH 42/74] [ROCKETMQ-191] Fix socket options
---
.../remoting/netty/NettyRemotingServer.java | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
index d8d9b6518b..a9a55abae0 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java
@@ -26,7 +26,9 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
@@ -105,8 +107,7 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
}
});
- if (RemotingUtil.isLinuxPlatform() //
- && nettyServerConfig.isUseEpollNativeSelector()) {
+ if (useEpoll()) {
this.eventLoopGroupSelector = new EpollEventLoopGroup(nettyServerConfig.getServerSelectorThreads(), new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
private int threadTotal = nettyServerConfig.getServerSelectorThreads();
@@ -129,6 +130,12 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
}
}
+ private boolean useEpoll() {
+ return RemotingUtil.isLinuxPlatform()
+ && nettyServerConfig.isUseEpollNativeSelector()
+ && Epoll.isAvailable();
+ }
+
@Override
public void start() {
this.defaultEventExecutorGroup = new DefaultEventExecutorGroup(
@@ -144,13 +151,14 @@ public class NettyRemotingServer extends NettyRemotingAbstract implements Remoti
});
ServerBootstrap childHandler =
- this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector).channel(NioServerSocketChannel.class)
+ this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
+ .channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_KEEPALIVE, false)
.childOption(ChannelOption.TCP_NODELAY, true)
- .option(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize())
- .option(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize())
+ .childOption(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize())
+ .childOption(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize())
.localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort()))
.childHandler(new ChannelInitializer() {
@Override
From 2a5a6d6543f63744f122a06348371dc48a0a97c2 Mon Sep 17 00:00:00 2001
From: Li Zhanhui
Date: Tue, 16 May 2017 14:34:56 +0800
Subject: [PATCH 43/74] This is to close issue ROCKETMQ-198: Add language code
for Go and PHP Thanks to StyleTang
---
.../org/apache/rocketmq/remoting/protocol/LanguageCode.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/LanguageCode.java b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/LanguageCode.java
index 669a383e2c..17ce919003 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/LanguageCode.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/protocol/LanguageCode.java
@@ -26,7 +26,9 @@ public enum LanguageCode {
ERLANG((byte) 5),
RUBY((byte) 6),
OTHER((byte) 7),
- HTTP((byte) 8);
+ HTTP((byte) 8),
+ GO((byte) 9),
+ PHP((byte) 10);
private byte code;
From c8e84adf5a2911c21faea80320d3fd5fd012f4a9 Mon Sep 17 00:00:00 2001
From: yukon
Date: Wed, 24 May 2017 16:50:51 +0800
Subject: [PATCH 44/74] [ROCKETMQ-186] Implement the OpenMessaging
specification 0.1.0-alpha version
---
distribution/release-client.xml | 1 +
distribution/release.xml | 1 +
example/pom.xml | 9 +
.../example/openmessaging/SimpleProducer.java | 76 ++++++
.../openmessaging/SimplePullConsumer.java | 58 +++++
.../openmessaging/SimplePushConsumer.java | 59 +++++
openmessaging/pom.xml | 42 ++++
.../rocketmq/MessagingAccessPointImpl.java | 132 ++++++++++
.../rocketmq/config/ClientConfig.java | 194 +++++++++++++++
.../rocketmq/consumer/LocalMessageCache.java | 213 ++++++++++++++++
.../rocketmq/consumer/PullConsumerImpl.java | 166 +++++++++++++
.../rocketmq/consumer/PushConsumerImpl.java | 181 ++++++++++++++
.../rocketmq/domain/BytesMessageImpl.java | 108 +++++++++
.../rocketmq/domain/ConsumeRequest.java | 55 +++++
.../rocketmq/domain/NonStandardKeys.java | 30 +++
.../rocketmq/domain/SendResultImpl.java | 40 +++
.../producer/AbstractOMSProducer.java | 138 +++++++++++
.../rocketmq/producer/ProducerImpl.java | 124 ++++++++++
.../producer/SequenceProducerImpl.java | 95 ++++++++
.../rocketmq/promise/DefaultPromise.java | 227 ++++++++++++++++++
.../rocketmq/promise/FutureState.java | 51 ++++
.../rocketmq/utils/BeanUtils.java | 185 ++++++++++++++
.../openmessaging/rocketmq/utils/OMSUtil.java | 182 ++++++++++++++
.../consumer/LocalMessageCacheTest.java | 89 +++++++
.../consumer/PullConsumerImplTest.java | 96 ++++++++
.../consumer/PushConsumerImplTest.java | 87 +++++++
.../rocketmq/producer/ProducerImplTest.java | 101 ++++++++
.../producer/SequenceProducerImplTest.java | 86 +++++++
.../rocketmq/promise/DefaultPromiseTest.java | 136 +++++++++++
.../rocketmq/utils/BeanUtilsTest.java | 110 +++++++++
pom.xml | 6 +
31 files changed, 3078 insertions(+)
create mode 100644 example/src/main/java/org/apache/rocketmq/example/openmessaging/SimpleProducer.java
create mode 100644 example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePullConsumer.java
create mode 100644 example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePushConsumer.java
create mode 100644 openmessaging/pom.xml
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/MessagingAccessPointImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/config/ClientConfig.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/LocalMessageCache.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PullConsumerImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PushConsumerImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/domain/BytesMessageImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/domain/ConsumeRequest.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/domain/NonStandardKeys.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/domain/SendResultImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/producer/AbstractOMSProducer.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/producer/ProducerImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/producer/SequenceProducerImpl.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/promise/DefaultPromise.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/promise/FutureState.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/utils/BeanUtils.java
create mode 100644 openmessaging/src/main/java/io/openmessaging/rocketmq/utils/OMSUtil.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/LocalMessageCacheTest.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PullConsumerImplTest.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PushConsumerImplTest.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/producer/ProducerImplTest.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/producer/SequenceProducerImplTest.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/promise/DefaultPromiseTest.java
create mode 100644 openmessaging/src/test/java/io/openmessaging/rocketmq/utils/BeanUtilsTest.java
diff --git a/distribution/release-client.xml b/distribution/release-client.xml
index 46563eb449..84d33a0170 100644
--- a/distribution/release-client.xml
+++ b/distribution/release-client.xml
@@ -47,6 +47,7 @@
trueorg.apache.rocketmq:rocketmq-client
+ org.apache.rocketmq:rocketmq-openmessaging./
diff --git a/distribution/release.xml b/distribution/release.xml
index 9e4ef2a017..c67d23e13b 100644
--- a/distribution/release.xml
+++ b/distribution/release.xml
@@ -68,6 +68,7 @@
org.apache.rocketmq:rocketmq-filtersrvorg.apache.rocketmq:rocketmq-exampleorg.apache.rocketmq:rocketmq-filter
+ org.apache.rocketmq:rocketmq-openmessaginglib/
diff --git a/example/pom.xml b/example/pom.xml
index 785a4ca856..840fa36f16 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -48,5 +48,14 @@
org.javassistjavassist
+
+ io.openmessaging
+ openmessaging-api
+
+
+ org.apache.rocketmq
+ rocketmq-openmessaging
+ 4.1.0-incubating-SNAPSHOT
+
diff --git a/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimpleProducer.java b/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimpleProducer.java
new file mode 100644
index 0000000000..9d162ac146
--- /dev/null
+++ b/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimpleProducer.java
@@ -0,0 +1,76 @@
+/*
+ * 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.example.openmessaging;
+
+import io.openmessaging.Message;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.Producer;
+import io.openmessaging.Promise;
+import io.openmessaging.PromiseListener;
+import io.openmessaging.SendResult;
+import java.nio.charset.Charset;
+
+public class SimpleProducer {
+ public static void main(String[] args) {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+
+ final Producer producer = messagingAccessPoint.createProducer();
+
+ messagingAccessPoint.startup();
+ System.out.printf("MessagingAccessPoint startup OK%n");
+
+ producer.startup();
+ System.out.printf("Producer startup OK%n");
+
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ @Override
+ public void run() {
+ producer.shutdown();
+ messagingAccessPoint.shutdown();
+ }
+ }));
+
+ {
+ Message message = producer.createBytesMessageToTopic("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8")));
+ SendResult sendResult = producer.send(message);
+ //final Void aVoid = result.get(3000L);
+ System.out.printf("Send async message OK, msgId: %s%n", sendResult.messageId());
+ }
+
+ {
+ final Promise result = producer.sendAsync(producer.createBytesMessageToTopic("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8"))));
+ result.addListener(new PromiseListener() {
+ @Override
+ public void operationCompleted(Promise promise) {
+ System.out.printf("Send async message OK, msgId: %s%n", promise.get().messageId());
+ }
+
+ @Override
+ public void operationFailed(Promise promise) {
+ System.out.printf("Send async message Failed, error: %s%n", promise.getThrowable().getMessage());
+ }
+ });
+ }
+
+ {
+ producer.sendOneway(producer.createBytesMessageToTopic("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8"))));
+ System.out.printf("Send oneway message OK%n");
+ }
+ }
+}
diff --git a/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePullConsumer.java b/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePullConsumer.java
new file mode 100644
index 0000000000..8e067724de
--- /dev/null
+++ b/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePullConsumer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.example.openmessaging;
+
+import io.openmessaging.Message;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.OMS;
+import io.openmessaging.PullConsumer;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+
+public class SimplePullConsumer {
+ public static void main(String[] args) {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+
+ final PullConsumer consumer = messagingAccessPoint.createPullConsumer("OMS_HELLO_TOPIC",
+ OMS.newKeyValue().put(NonStandardKeys.CONSUMER_GROUP, "OMS_CONSUMER"));
+
+ messagingAccessPoint.startup();
+ System.out.printf("MessagingAccessPoint startup OK%n");
+
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ @Override
+ public void run() {
+ consumer.shutdown();
+ messagingAccessPoint.shutdown();
+ }
+ }));
+
+ consumer.startup();
+ System.out.printf("Consumer startup OK%n");
+
+ while (true) {
+ Message message = consumer.poll();
+ if (message != null) {
+ String msgId = message.headers().getString(MessageHeader.MESSAGE_ID);
+ System.out.printf("Received one message: %s%n", msgId);
+ consumer.ack(msgId);
+ }
+ }
+ }
+}
diff --git a/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePushConsumer.java b/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePushConsumer.java
new file mode 100644
index 0000000000..b0935d4c74
--- /dev/null
+++ b/example/src/main/java/org/apache/rocketmq/example/openmessaging/SimplePushConsumer.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.example.openmessaging;
+
+import io.openmessaging.Message;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.MessageListener;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.OMS;
+import io.openmessaging.PushConsumer;
+import io.openmessaging.ReceivedMessageContext;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+
+public class SimplePushConsumer {
+ public static void main(String[] args) {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+
+ final PushConsumer consumer = messagingAccessPoint.
+ createPushConsumer(OMS.newKeyValue().put(NonStandardKeys.CONSUMER_GROUP, "OMS_CONSUMER"));
+
+ messagingAccessPoint.startup();
+ System.out.printf("MessagingAccessPoint startup OK%n");
+
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ @Override
+ public void run() {
+ consumer.shutdown();
+ messagingAccessPoint.shutdown();
+ }
+ }));
+
+ consumer.attachQueue("OMS_HELLO_TOPIC", new MessageListener() {
+ @Override
+ public void onMessage(final Message message, final ReceivedMessageContext context) {
+ System.out.printf("Received one message: %s%n", message.headers().getString(MessageHeader.MESSAGE_ID));
+ context.ack();
+ }
+ });
+
+ consumer.startup();
+ System.out.printf("Consumer startup OK%n");
+ }
+}
diff --git a/openmessaging/pom.xml b/openmessaging/pom.xml
new file mode 100644
index 0000000000..e853642d23
--- /dev/null
+++ b/openmessaging/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+ rocketmq-all
+ org.apache.rocketmq
+ 4.1.0-incubating-SNAPSHOT
+
+ 4.0.0
+
+ rocketmq-openmessaging
+ rocketmq-openmessaging ${project.version}
+
+
+
+ io.openmessaging
+ openmessaging-api
+
+
+ org.apache.rocketmq
+ rocketmq-client
+
+
+
\ No newline at end of file
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/MessagingAccessPointImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/MessagingAccessPointImpl.java
new file mode 100644
index 0000000000..65caf84084
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/MessagingAccessPointImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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 io.openmessaging.rocketmq;
+
+import io.openmessaging.IterableConsumer;
+import io.openmessaging.KeyValue;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.Producer;
+import io.openmessaging.PullConsumer;
+import io.openmessaging.PushConsumer;
+import io.openmessaging.ResourceManager;
+import io.openmessaging.SequenceProducer;
+import io.openmessaging.ServiceEndPoint;
+import io.openmessaging.exception.OMSNotSupportedException;
+import io.openmessaging.observer.Observer;
+import io.openmessaging.rocketmq.consumer.PullConsumerImpl;
+import io.openmessaging.rocketmq.consumer.PushConsumerImpl;
+import io.openmessaging.rocketmq.producer.ProducerImpl;
+import io.openmessaging.rocketmq.producer.SequenceProducerImpl;
+import io.openmessaging.rocketmq.utils.OMSUtil;
+
+public class MessagingAccessPointImpl implements MessagingAccessPoint {
+ private final KeyValue accessPointProperties;
+
+ public MessagingAccessPointImpl(final KeyValue accessPointProperties) {
+ this.accessPointProperties = accessPointProperties;
+ }
+
+ @Override
+ public KeyValue properties() {
+ return accessPointProperties;
+ }
+
+ @Override
+ public Producer createProducer() {
+ return new ProducerImpl(this.accessPointProperties);
+ }
+
+ @Override
+ public Producer createProducer(KeyValue properties) {
+ return new ProducerImpl(OMSUtil.buildKeyValue(this.accessPointProperties, properties));
+ }
+
+ @Override
+ public SequenceProducer createSequenceProducer() {
+ return new SequenceProducerImpl(this.accessPointProperties);
+ }
+
+ @Override
+ public SequenceProducer createSequenceProducer(KeyValue properties) {
+ return new SequenceProducerImpl(OMSUtil.buildKeyValue(this.accessPointProperties, properties));
+ }
+
+ @Override
+ public PushConsumer createPushConsumer() {
+ return new PushConsumerImpl(accessPointProperties);
+ }
+
+ @Override
+ public PushConsumer createPushConsumer(KeyValue properties) {
+ return new PushConsumerImpl(OMSUtil.buildKeyValue(this.accessPointProperties, properties));
+ }
+
+ @Override
+ public PullConsumer createPullConsumer(String queueName) {
+ return new PullConsumerImpl(queueName, accessPointProperties);
+ }
+
+ @Override
+ public PullConsumer createPullConsumer(String queueName, KeyValue properties) {
+ return new PullConsumerImpl(queueName, OMSUtil.buildKeyValue(this.accessPointProperties, properties));
+ }
+
+ @Override
+ public IterableConsumer createIterableConsumer(String queueName) {
+ throw new OMSNotSupportedException("-1", "IterableConsumer is not supported in current version");
+ }
+
+ @Override
+ public IterableConsumer createIterableConsumer(String queueName, KeyValue properties) {
+ throw new OMSNotSupportedException("-1", "IterableConsumer is not supported in current version");
+ }
+
+ @Override
+ public ResourceManager getResourceManager() {
+ throw new OMSNotSupportedException("-1", "ResourceManager is not supported in current version.");
+ }
+
+ @Override
+ public ServiceEndPoint createServiceEndPoint() {
+ throw new OMSNotSupportedException("-1", "ServiceEndPoint is not supported in current version.");
+ }
+
+ @Override
+ public ServiceEndPoint createServiceEndPoint(KeyValue properties) {
+ throw new OMSNotSupportedException("-1", "ServiceEndPoint is not supported in current version.");
+ }
+
+ @Override
+ public void addObserver(Observer observer) {
+ //Ignore
+ }
+
+ @Override
+ public void deleteObserver(Observer observer) {
+ //Ignore
+ }
+
+ @Override
+ public void startup() {
+ //Ignore
+ }
+
+ @Override
+ public void shutdown() {
+ //Ignore
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/config/ClientConfig.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/config/ClientConfig.java
new file mode 100644
index 0000000000..7077c6dc99
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/config/ClientConfig.java
@@ -0,0 +1,194 @@
+/*
+ * 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 io.openmessaging.rocketmq.config;
+
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+
+public class ClientConfig implements PropertyKeys, NonStandardKeys {
+ private String omsDriverImpl;
+ private String omsAccessPoints;
+ private String omsNamespace;
+ private String omsProducerId;
+ private String omsConsumerId;
+ private int omsOperationTimeout = 5000;
+ private String omsRoutingName;
+ private String omsOperatorName;
+ private String omsDstQueue;
+ private String omsSrcTopic;
+ private String rmqConsumerGroup;
+ private String rmqProducerGroup = "__OMS_PRODUCER_DEFAULT_GROUP";
+ private int rmqMaxRedeliveryTimes = 16;
+ private int rmqMessageConsumeTimeout = 15; //In minutes
+ private int rmqMaxConsumeThreadNums = 64;
+ private int rmqMinConsumeThreadNums = 20;
+ private String rmqMessageDestination;
+ private int rmqPullMessageBatchNums = 32;
+ private int rmqPullMessageCacheCapacity = 1000;
+
+ public String getOmsDriverImpl() {
+ return omsDriverImpl;
+ }
+
+ public void setOmsDriverImpl(final String omsDriverImpl) {
+ this.omsDriverImpl = omsDriverImpl;
+ }
+
+ public String getOmsAccessPoints() {
+ return omsAccessPoints;
+ }
+
+ public void setOmsAccessPoints(final String omsAccessPoints) {
+ this.omsAccessPoints = omsAccessPoints;
+ }
+
+ public String getOmsNamespace() {
+ return omsNamespace;
+ }
+
+ public void setOmsNamespace(final String omsNamespace) {
+ this.omsNamespace = omsNamespace;
+ }
+
+ public String getOmsProducerId() {
+ return omsProducerId;
+ }
+
+ public void setOmsProducerId(final String omsProducerId) {
+ this.omsProducerId = omsProducerId;
+ }
+
+ public String getOmsConsumerId() {
+ return omsConsumerId;
+ }
+
+ public void setOmsConsumerId(final String omsConsumerId) {
+ this.omsConsumerId = omsConsumerId;
+ }
+
+ public int getOmsOperationTimeout() {
+ return omsOperationTimeout;
+ }
+
+ public void setOmsOperationTimeout(final int omsOperationTimeout) {
+ this.omsOperationTimeout = omsOperationTimeout;
+ }
+
+ public String getOmsRoutingName() {
+ return omsRoutingName;
+ }
+
+ public void setOmsRoutingName(final String omsRoutingName) {
+ this.omsRoutingName = omsRoutingName;
+ }
+
+ public String getOmsOperatorName() {
+ return omsOperatorName;
+ }
+
+ public void setOmsOperatorName(final String omsOperatorName) {
+ this.omsOperatorName = omsOperatorName;
+ }
+
+ public String getOmsDstQueue() {
+ return omsDstQueue;
+ }
+
+ public void setOmsDstQueue(final String omsDstQueue) {
+ this.omsDstQueue = omsDstQueue;
+ }
+
+ public String getOmsSrcTopic() {
+ return omsSrcTopic;
+ }
+
+ public void setOmsSrcTopic(final String omsSrcTopic) {
+ this.omsSrcTopic = omsSrcTopic;
+ }
+
+ public String getRmqConsumerGroup() {
+ return rmqConsumerGroup;
+ }
+
+ public void setRmqConsumerGroup(final String rmqConsumerGroup) {
+ this.rmqConsumerGroup = rmqConsumerGroup;
+ }
+
+ public String getRmqProducerGroup() {
+ return rmqProducerGroup;
+ }
+
+ public void setRmqProducerGroup(final String rmqProducerGroup) {
+ this.rmqProducerGroup = rmqProducerGroup;
+ }
+
+ public int getRmqMaxRedeliveryTimes() {
+ return rmqMaxRedeliveryTimes;
+ }
+
+ public void setRmqMaxRedeliveryTimes(final int rmqMaxRedeliveryTimes) {
+ this.rmqMaxRedeliveryTimes = rmqMaxRedeliveryTimes;
+ }
+
+ public int getRmqMessageConsumeTimeout() {
+ return rmqMessageConsumeTimeout;
+ }
+
+ public void setRmqMessageConsumeTimeout(final int rmqMessageConsumeTimeout) {
+ this.rmqMessageConsumeTimeout = rmqMessageConsumeTimeout;
+ }
+
+ public int getRmqMaxConsumeThreadNums() {
+ return rmqMaxConsumeThreadNums;
+ }
+
+ public void setRmqMaxConsumeThreadNums(final int rmqMaxConsumeThreadNums) {
+ this.rmqMaxConsumeThreadNums = rmqMaxConsumeThreadNums;
+ }
+
+ public int getRmqMinConsumeThreadNums() {
+ return rmqMinConsumeThreadNums;
+ }
+
+ public void setRmqMinConsumeThreadNums(final int rmqMinConsumeThreadNums) {
+ this.rmqMinConsumeThreadNums = rmqMinConsumeThreadNums;
+ }
+
+ public String getRmqMessageDestination() {
+ return rmqMessageDestination;
+ }
+
+ public void setRmqMessageDestination(final String rmqMessageDestination) {
+ this.rmqMessageDestination = rmqMessageDestination;
+ }
+
+ public int getRmqPullMessageBatchNums() {
+ return rmqPullMessageBatchNums;
+ }
+
+ public void setRmqPullMessageBatchNums(final int rmqPullMessageBatchNums) {
+ this.rmqPullMessageBatchNums = rmqPullMessageBatchNums;
+ }
+
+ public int getRmqPullMessageCacheCapacity() {
+ return rmqPullMessageCacheCapacity;
+ }
+
+ public void setRmqPullMessageCacheCapacity(final int rmqPullMessageCacheCapacity) {
+ this.rmqPullMessageCacheCapacity = rmqPullMessageCacheCapacity;
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/LocalMessageCache.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/LocalMessageCache.java
new file mode 100644
index 0000000000..90f9e03ed3
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/LocalMessageCache.java
@@ -0,0 +1,213 @@
+/*
+ * 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 io.openmessaging.rocketmq.consumer;
+
+import io.openmessaging.KeyValue;
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.ServiceLifecycle;
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.ConsumeRequest;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.impl.consumer.ProcessQueue;
+import org.apache.rocketmq.client.log.ClientLogger;
+import org.apache.rocketmq.common.ThreadFactoryImpl;
+import org.apache.rocketmq.common.message.MessageAccessor;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.utils.ThreadUtils;
+import org.slf4j.Logger;
+
+class LocalMessageCache implements ServiceLifecycle {
+ private final BlockingQueue consumeRequestCache;
+ private final Map consumedRequest;
+ private final ConcurrentHashMap pullOffsetTable;
+ private final DefaultMQPullConsumer rocketmqPullConsumer;
+ private final ClientConfig clientConfig;
+ private final ScheduledExecutorService cleanExpireMsgExecutors;
+
+ private final static Logger log = ClientLogger.getLog();
+
+ LocalMessageCache(final DefaultMQPullConsumer rocketmqPullConsumer, final ClientConfig clientConfig) {
+ consumeRequestCache = new LinkedBlockingQueue<>(clientConfig.getRmqPullMessageCacheCapacity());
+ this.consumedRequest = new ConcurrentHashMap<>();
+ this.pullOffsetTable = new ConcurrentHashMap<>();
+ this.rocketmqPullConsumer = rocketmqPullConsumer;
+ this.clientConfig = clientConfig;
+ this.cleanExpireMsgExecutors = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
+ "OMS_CleanExpireMsgScheduledThread_"));
+ }
+
+ int nextPullBatchNums() {
+ return Math.min(clientConfig.getRmqPullMessageBatchNums(), consumeRequestCache.remainingCapacity());
+ }
+
+ long nextPullOffset(MessageQueue remoteQueue) {
+ if (!pullOffsetTable.containsKey(remoteQueue)) {
+ try {
+ pullOffsetTable.putIfAbsent(remoteQueue,
+ rocketmqPullConsumer.fetchConsumeOffset(remoteQueue, false));
+ } catch (MQClientException e) {
+ log.error("A error occurred in fetch consume offset process.", e);
+ }
+ }
+ return pullOffsetTable.get(remoteQueue);
+ }
+
+ void updatePullOffset(MessageQueue remoteQueue, long nextPullOffset) {
+ pullOffsetTable.put(remoteQueue, nextPullOffset);
+ }
+
+ void submitConsumeRequest(ConsumeRequest consumeRequest) {
+ try {
+ consumeRequestCache.put(consumeRequest);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ MessageExt poll() {
+ return poll(clientConfig.getOmsOperationTimeout());
+ }
+
+ MessageExt poll(final KeyValue properties) {
+ int currentPollTimeout = clientConfig.getOmsOperationTimeout();
+ if (properties.containsKey(PropertyKeys.OPERATION_TIMEOUT)) {
+ currentPollTimeout = properties.getInt(PropertyKeys.OPERATION_TIMEOUT);
+ }
+ return poll(currentPollTimeout);
+ }
+
+ private MessageExt poll(long timeout) {
+ try {
+ ConsumeRequest consumeRequest = consumeRequestCache.poll(timeout, TimeUnit.MILLISECONDS);
+ if (consumeRequest != null) {
+ MessageExt messageExt = consumeRequest.getMessageExt();
+ consumeRequest.setStartConsumeTimeMillis(System.currentTimeMillis());
+ MessageAccessor.setConsumeStartTimeStamp(messageExt, String.valueOf(consumeRequest.getStartConsumeTimeMillis()));
+ consumedRequest.put(messageExt.getMsgId(), consumeRequest);
+ return messageExt;
+ }
+ } catch (InterruptedException ignore) {
+ }
+ return null;
+ }
+
+ void ack(final String messageId) {
+ ConsumeRequest consumeRequest = consumedRequest.remove(messageId);
+ if (consumeRequest != null) {
+ long offset = consumeRequest.getProcessQueue().removeMessage(Collections.singletonList(consumeRequest.getMessageExt()));
+ try {
+ rocketmqPullConsumer.updateConsumeOffset(consumeRequest.getMessageQueue(), offset);
+ } catch (MQClientException e) {
+ log.error("A error occurred in update consume offset process.", e);
+ }
+ }
+ }
+
+ void ack(final MessageQueue messageQueue, final ProcessQueue processQueue, final MessageExt messageExt) {
+ consumedRequest.remove(messageExt.getMsgId());
+ long offset = processQueue.removeMessage(Collections.singletonList(messageExt));
+ try {
+ rocketmqPullConsumer.updateConsumeOffset(messageQueue, offset);
+ } catch (MQClientException e) {
+ log.error("A error occurred in update consume offset process.", e);
+ }
+ }
+
+ @Override
+ public void startup() {
+ this.cleanExpireMsgExecutors.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ cleanExpireMsg();
+ }
+ }, clientConfig.getRmqMessageConsumeTimeout(), clientConfig.getRmqMessageConsumeTimeout(), TimeUnit.MINUTES);
+ }
+
+ @Override
+ public void shutdown() {
+ ThreadUtils.shutdownGracefully(cleanExpireMsgExecutors, 5000, TimeUnit.MILLISECONDS);
+ }
+
+ private void cleanExpireMsg() {
+ for (final Map.Entry next : rocketmqPullConsumer.getDefaultMQPullConsumerImpl()
+ .getRebalanceImpl().getProcessQueueTable().entrySet()) {
+ ProcessQueue pq = next.getValue();
+ MessageQueue mq = next.getKey();
+ ReadWriteLock lockTreeMap = getLockInProcessQueue(pq);
+ if (lockTreeMap == null) {
+ log.error("Gets tree map lock in process queue error, may be has compatibility issue");
+ return;
+ }
+
+ TreeMap msgTreeMap = pq.getMsgTreeMap();
+
+ int loop = msgTreeMap.size();
+ for (int i = 0; i < loop; i++) {
+ MessageExt msg = null;
+ try {
+ lockTreeMap.readLock().lockInterruptibly();
+ try {
+ if (!msgTreeMap.isEmpty()) {
+ msg = msgTreeMap.firstEntry().getValue();
+ if (System.currentTimeMillis() - Long.parseLong(MessageAccessor.getConsumeStartTimeStamp(msg))
+ > clientConfig.getRmqMessageConsumeTimeout() * 60 * 1000) {
+ //Expired, ack and remove it.
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } finally {
+ lockTreeMap.readLock().unlock();
+ }
+ } catch (InterruptedException e) {
+ log.error("Gets expired message exception", e);
+ }
+
+ try {
+ rocketmqPullConsumer.sendMessageBack(msg, 3);
+ log.info("Send expired msg back. topic={}, msgId={}, storeHost={}, queueId={}, queueOffset={}",
+ msg.getTopic(), msg.getMsgId(), msg.getStoreHost(), msg.getQueueId(), msg.getQueueOffset());
+ ack(mq, pq, msg);
+ } catch (Exception e) {
+ log.error("Send back expired msg exception", e);
+ }
+ }
+ }
+ }
+
+ private ReadWriteLock getLockInProcessQueue(ProcessQueue pq) {
+ try {
+ return (ReadWriteLock) FieldUtils.readDeclaredField(pq, "lockTreeMap", true);
+ } catch (IllegalAccessException e) {
+ return null;
+ }
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PullConsumerImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PullConsumerImpl.java
new file mode 100644
index 0000000000..8d396d43c5
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PullConsumerImpl.java
@@ -0,0 +1,166 @@
+/*
+ * 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 io.openmessaging.rocketmq.consumer;
+
+import io.openmessaging.KeyValue;
+import io.openmessaging.Message;
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.PullConsumer;
+import io.openmessaging.exception.OMSRuntimeException;
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.ConsumeRequest;
+import io.openmessaging.rocketmq.utils.BeanUtils;
+import io.openmessaging.rocketmq.utils.OMSUtil;
+import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
+import org.apache.rocketmq.client.consumer.MQPullConsumer;
+import org.apache.rocketmq.client.consumer.MQPullConsumerScheduleService;
+import org.apache.rocketmq.client.consumer.PullResult;
+import org.apache.rocketmq.client.consumer.PullTaskCallback;
+import org.apache.rocketmq.client.consumer.PullTaskContext;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.impl.consumer.ProcessQueue;
+import org.apache.rocketmq.client.log.ClientLogger;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.slf4j.Logger;
+
+public class PullConsumerImpl implements PullConsumer {
+ private final DefaultMQPullConsumer rocketmqPullConsumer;
+ private final KeyValue properties;
+ private boolean started = false;
+ private String targetQueueName;
+ private final MQPullConsumerScheduleService pullConsumerScheduleService;
+ private final LocalMessageCache localMessageCache;
+ private final ClientConfig clientConfig;
+
+ final static Logger log = ClientLogger.getLog();
+
+ public PullConsumerImpl(final String queueName, final KeyValue properties) {
+ this.properties = properties;
+ this.targetQueueName = queueName;
+
+ this.clientConfig = BeanUtils.populate(properties, ClientConfig.class);
+
+ String consumerGroup = clientConfig.getRmqConsumerGroup();
+ if (null == consumerGroup || consumerGroup.isEmpty()) {
+ throw new OMSRuntimeException("-1", "Consumer Group is necessary for RocketMQ, please set it.");
+ }
+ pullConsumerScheduleService = new MQPullConsumerScheduleService(consumerGroup);
+
+ this.rocketmqPullConsumer = pullConsumerScheduleService.getDefaultMQPullConsumer();
+
+ String accessPoints = clientConfig.getOmsAccessPoints();
+ if (accessPoints == null || accessPoints.isEmpty()) {
+ throw new OMSRuntimeException("-1", "OMS AccessPoints is null or empty.");
+ }
+ this.rocketmqPullConsumer.setNamesrvAddr(accessPoints.replace(',', ';'));
+
+ this.rocketmqPullConsumer.setConsumerGroup(consumerGroup);
+
+ int maxReDeliveryTimes = clientConfig.getRmqMaxRedeliveryTimes();
+ this.rocketmqPullConsumer.setMaxReconsumeTimes(maxReDeliveryTimes);
+
+ String consumerId = OMSUtil.buildInstanceName();
+ this.rocketmqPullConsumer.setInstanceName(consumerId);
+ properties.put(PropertyKeys.CONSUMER_ID, consumerId);
+
+ this.localMessageCache = new LocalMessageCache(this.rocketmqPullConsumer, clientConfig);
+ }
+
+ @Override
+ public KeyValue properties() {
+ return properties;
+ }
+
+ @Override
+ public Message poll() {
+ MessageExt rmqMsg = localMessageCache.poll();
+ return rmqMsg == null ? null : OMSUtil.msgConvert(rmqMsg);
+ }
+
+ @Override
+ public Message poll(final KeyValue properties) {
+ MessageExt rmqMsg = localMessageCache.poll(properties);
+ return rmqMsg == null ? null : OMSUtil.msgConvert(rmqMsg);
+ }
+
+ @Override
+ public void ack(final String messageId) {
+ localMessageCache.ack(messageId);
+ }
+
+ @Override
+ public void ack(final String messageId, final KeyValue properties) {
+ localMessageCache.ack(messageId);
+ }
+
+ @Override
+ public synchronized void startup() {
+ if (!started) {
+ try {
+ registerPullTaskCallback();
+ this.pullConsumerScheduleService.start();
+ this.localMessageCache.startup();
+ } catch (MQClientException e) {
+ throw new OMSRuntimeException("-1", e);
+ }
+ }
+ this.started = true;
+ }
+
+ private void registerPullTaskCallback() {
+ this.pullConsumerScheduleService.registerPullTaskCallback(targetQueueName, new PullTaskCallback() {
+ @Override
+ public void doPullTask(final MessageQueue mq, final PullTaskContext context) {
+ MQPullConsumer consumer = context.getPullConsumer();
+ try {
+ long offset = localMessageCache.nextPullOffset(mq);
+
+ PullResult pullResult = consumer.pull(mq, "*",
+ offset, localMessageCache.nextPullBatchNums());
+ ProcessQueue pq = rocketmqPullConsumer.getDefaultMQPullConsumerImpl().getRebalanceImpl()
+ .getProcessQueueTable().get(mq);
+ switch (pullResult.getPullStatus()) {
+ case FOUND:
+ if (pq != null) {
+ pq.putMessage(pullResult.getMsgFoundList());
+ for (final MessageExt messageExt : pullResult.getMsgFoundList()) {
+ localMessageCache.submitConsumeRequest(new ConsumeRequest(messageExt, mq, pq));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ localMessageCache.updatePullOffset(mq, pullResult.getNextBeginOffset());
+ } catch (Exception e) {
+ log.error("A error occurred in pull message process.", e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public synchronized void shutdown() {
+ if (this.started) {
+ this.localMessageCache.shutdown();
+ this.pullConsumerScheduleService.shutdown();
+ this.rocketmqPullConsumer.shutdown();
+ }
+ this.started = false;
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PushConsumerImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PushConsumerImpl.java
new file mode 100644
index 0000000000..f9b8058e04
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/consumer/PushConsumerImpl.java
@@ -0,0 +1,181 @@
+/*
+ * 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 io.openmessaging.rocketmq.consumer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.KeyValue;
+import io.openmessaging.MessageListener;
+import io.openmessaging.OMS;
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.PushConsumer;
+import io.openmessaging.ReceivedMessageContext;
+import io.openmessaging.exception.OMSRuntimeException;
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+import io.openmessaging.rocketmq.utils.BeanUtils;
+import io.openmessaging.rocketmq.utils.OMSUtil;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
+import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.message.MessageExt;
+
+public class PushConsumerImpl implements PushConsumer {
+ private final DefaultMQPushConsumer rocketmqPushConsumer;
+ private final KeyValue properties;
+ private boolean started = false;
+ private final Map subscribeTable = new ConcurrentHashMap<>();
+ private final ClientConfig clientConfig;
+
+ public PushConsumerImpl(final KeyValue properties) {
+ this.rocketmqPushConsumer = new DefaultMQPushConsumer();
+ this.properties = properties;
+ this.clientConfig = BeanUtils.populate(properties, ClientConfig.class);
+
+ String accessPoints = clientConfig.getOmsAccessPoints();
+ if (accessPoints == null || accessPoints.isEmpty()) {
+ throw new OMSRuntimeException("-1", "OMS AccessPoints is null or empty.");
+ }
+ this.rocketmqPushConsumer.setNamesrvAddr(accessPoints.replace(',', ';'));
+
+ String consumerGroup = clientConfig.getRmqConsumerGroup();
+ if (null == consumerGroup || consumerGroup.isEmpty()) {
+ throw new OMSRuntimeException("-1", "Consumer Group is necessary for RocketMQ, please set it.");
+ }
+ this.rocketmqPushConsumer.setConsumerGroup(consumerGroup);
+ this.rocketmqPushConsumer.setMaxReconsumeTimes(clientConfig.getRmqMaxRedeliveryTimes());
+ this.rocketmqPushConsumer.setConsumeTimeout(clientConfig.getRmqMessageConsumeTimeout());
+ this.rocketmqPushConsumer.setConsumeThreadMax(clientConfig.getRmqMaxConsumeThreadNums());
+ this.rocketmqPushConsumer.setConsumeThreadMin(clientConfig.getRmqMinConsumeThreadNums());
+
+ String consumerId = OMSUtil.buildInstanceName();
+ this.rocketmqPushConsumer.setInstanceName(consumerId);
+ properties.put(PropertyKeys.CONSUMER_ID, consumerId);
+
+ this.rocketmqPushConsumer.registerMessageListener(new MessageListenerImpl());
+ }
+
+ @Override
+ public KeyValue properties() {
+ return properties;
+ }
+
+ @Override
+ public void resume() {
+ this.rocketmqPushConsumer.resume();
+ }
+
+ @Override
+ public void suspend() {
+ this.rocketmqPushConsumer.suspend();
+ }
+
+ @Override
+ public boolean isSuspended() {
+ return this.rocketmqPushConsumer.getDefaultMQPushConsumerImpl().isPause();
+ }
+
+ @Override
+ public PushConsumer attachQueue(final String queueName, final MessageListener listener) {
+ this.subscribeTable.put(queueName, listener);
+ try {
+ this.rocketmqPushConsumer.subscribe(queueName, "*");
+ } catch (MQClientException e) {
+ throw new OMSRuntimeException("-1", String.format("RocketMQ push consumer can't attach to %s.", queueName));
+ }
+ return this;
+ }
+
+ @Override
+ public synchronized void startup() {
+ if (!started) {
+ try {
+ this.rocketmqPushConsumer.start();
+ } catch (MQClientException e) {
+ throw new OMSRuntimeException("-1", e);
+ }
+ }
+ this.started = true;
+ }
+
+ @Override
+ public synchronized void shutdown() {
+ if (this.started) {
+ this.rocketmqPushConsumer.shutdown();
+ }
+ this.started = false;
+ }
+
+ class MessageListenerImpl implements MessageListenerConcurrently {
+
+ @Override
+ public ConsumeConcurrentlyStatus consumeMessage(List rmqMsgList,
+ ConsumeConcurrentlyContext contextRMQ) {
+ MessageExt rmqMsg = rmqMsgList.get(0);
+ BytesMessage omsMsg = OMSUtil.msgConvert(rmqMsg);
+
+ MessageListener listener = PushConsumerImpl.this.subscribeTable.get(rmqMsg.getTopic());
+
+ if (listener == null) {
+ throw new OMSRuntimeException("-1",
+ String.format("The topic/queue %s isn't attached to this consumer", rmqMsg.getTopic()));
+ }
+
+ final KeyValue contextProperties = OMS.newKeyValue();
+ final CountDownLatch sync = new CountDownLatch(1);
+
+ contextProperties.put(NonStandardKeys.MESSAGE_CONSUME_STATUS, ConsumeConcurrentlyStatus.RECONSUME_LATER.name());
+
+ ReceivedMessageContext context = new ReceivedMessageContext() {
+ @Override
+ public KeyValue properties() {
+ return contextProperties;
+ }
+
+ @Override
+ public void ack() {
+ sync.countDown();
+ contextProperties.put(NonStandardKeys.MESSAGE_CONSUME_STATUS,
+ ConsumeConcurrentlyStatus.CONSUME_SUCCESS.name());
+ }
+
+ @Override
+ public void ack(final KeyValue properties) {
+ sync.countDown();
+ contextProperties.put(NonStandardKeys.MESSAGE_CONSUME_STATUS,
+ properties.getString(NonStandardKeys.MESSAGE_CONSUME_STATUS));
+ }
+ };
+ long begin = System.currentTimeMillis();
+ listener.onMessage(omsMsg, context);
+ long costs = System.currentTimeMillis() - begin;
+ long timeoutMills = clientConfig.getRmqMessageConsumeTimeout() * 60 * 1000;
+ try {
+ sync.await(Math.max(0, timeoutMills - costs), TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ignore) {
+ }
+
+ return ConsumeConcurrentlyStatus.valueOf(contextProperties.getString(NonStandardKeys.MESSAGE_CONSUME_STATUS));
+ }
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/BytesMessageImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/BytesMessageImpl.java
new file mode 100644
index 0000000000..43f80ae5be
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/BytesMessageImpl.java
@@ -0,0 +1,108 @@
+/*
+ * 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 io.openmessaging.rocketmq.domain;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.KeyValue;
+import io.openmessaging.Message;
+import io.openmessaging.OMS;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+public class BytesMessageImpl implements BytesMessage {
+ private KeyValue headers;
+ private KeyValue properties;
+ private byte[] body;
+
+ public BytesMessageImpl() {
+ this.headers = OMS.newKeyValue();
+ this.properties = OMS.newKeyValue();
+ }
+
+ @Override
+ public byte[] getBody() {
+ return body;
+ }
+
+ @Override
+ public BytesMessage setBody(final byte[] body) {
+ this.body = body;
+ return this;
+ }
+
+ @Override
+ public KeyValue headers() {
+ return headers;
+ }
+
+ @Override
+ public KeyValue properties() {
+ return properties;
+ }
+
+ @Override
+ public Message putHeaders(final String key, final int value) {
+ headers.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putHeaders(final String key, final long value) {
+ headers.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putHeaders(final String key, final double value) {
+ headers.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putHeaders(final String key, final String value) {
+ headers.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putProperties(final String key, final int value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putProperties(final String key, final long value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putProperties(final String key, final double value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Message putProperties(final String key, final String value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this);
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/ConsumeRequest.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/ConsumeRequest.java
new file mode 100644
index 0000000000..7ce4a9b423
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/ConsumeRequest.java
@@ -0,0 +1,55 @@
+/*
+ * 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 io.openmessaging.rocketmq.domain;
+
+import org.apache.rocketmq.client.impl.consumer.ProcessQueue;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+
+public class ConsumeRequest {
+ private final MessageExt messageExt;
+ private final MessageQueue messageQueue;
+ private final ProcessQueue processQueue;
+ private long startConsumeTimeMillis;
+
+ public ConsumeRequest(final MessageExt messageExt, final MessageQueue messageQueue,
+ final ProcessQueue processQueue) {
+ this.messageExt = messageExt;
+ this.messageQueue = messageQueue;
+ this.processQueue = processQueue;
+ }
+
+ public MessageExt getMessageExt() {
+ return messageExt;
+ }
+
+ public MessageQueue getMessageQueue() {
+ return messageQueue;
+ }
+
+ public ProcessQueue getProcessQueue() {
+ return processQueue;
+ }
+
+ public long getStartConsumeTimeMillis() {
+ return startConsumeTimeMillis;
+ }
+
+ public void setStartConsumeTimeMillis(final long startConsumeTimeMillis) {
+ this.startConsumeTimeMillis = startConsumeTimeMillis;
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/NonStandardKeys.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/NonStandardKeys.java
new file mode 100644
index 0000000000..3639a3f836
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/NonStandardKeys.java
@@ -0,0 +1,30 @@
+/*
+ * 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 io.openmessaging.rocketmq.domain;
+
+public interface NonStandardKeys {
+ String CONSUMER_GROUP = "rmq.consumer.group";
+ String PRODUCER_GROUP = "rmq.producer.group";
+ String MAX_REDELIVERY_TIMES = "rmq.max.redelivery.times";
+ String MESSAGE_CONSUME_TIMEOUT = "rmq.message.consume.timeout";
+ String MAX_CONSUME_THREAD_NUMS = "rmq.max.consume.thread.nums";
+ String MIN_CONSUME_THREAD_NUMS = "rmq.min.consume.thread.nums";
+ String MESSAGE_CONSUME_STATUS = "rmq.message.consume.status";
+ String MESSAGE_DESTINATION = "rmq.message.destination";
+ String PULL_MESSAGE_BATCH_NUMS = "rmq.pull.message.batch.nums";
+ String PULL_MESSAGE_CACHE_CAPACITY = "rmq.pull.message.cache.capacity";
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/SendResultImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/SendResultImpl.java
new file mode 100644
index 0000000000..228a9f0b59
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/domain/SendResultImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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 io.openmessaging.rocketmq.domain;
+
+import io.openmessaging.KeyValue;
+import io.openmessaging.SendResult;
+
+public class SendResultImpl implements SendResult {
+ private String messageId;
+ private KeyValue properties;
+
+ public SendResultImpl(final String messageId, final KeyValue properties) {
+ this.messageId = messageId;
+ this.properties = properties;
+ }
+
+ @Override
+ public String messageId() {
+ return messageId;
+ }
+
+ @Override
+ public KeyValue properties() {
+ return properties;
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/AbstractOMSProducer.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/AbstractOMSProducer.java
new file mode 100644
index 0000000000..8246bcd294
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/AbstractOMSProducer.java
@@ -0,0 +1,138 @@
+/*
+ * 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 io.openmessaging.rocketmq.producer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.KeyValue;
+import io.openmessaging.Message;
+import io.openmessaging.MessageFactory;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.ServiceLifecycle;
+import io.openmessaging.exception.OMSMessageFormatException;
+import io.openmessaging.exception.OMSNotSupportedException;
+import io.openmessaging.exception.OMSRuntimeException;
+import io.openmessaging.exception.OMSTimeOutException;
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.BytesMessageImpl;
+import io.openmessaging.rocketmq.utils.BeanUtils;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.log.ClientLogger;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.remoting.exception.RemotingConnectException;
+import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
+import org.slf4j.Logger;
+
+import static io.openmessaging.rocketmq.utils.OMSUtil.buildInstanceName;
+
+abstract class AbstractOMSProducer implements ServiceLifecycle, MessageFactory {
+ final static Logger log = ClientLogger.getLog();
+ final KeyValue properties;
+ final DefaultMQProducer rocketmqProducer;
+ private boolean started = false;
+ final ClientConfig clientConfig;
+
+ AbstractOMSProducer(final KeyValue properties) {
+ this.properties = properties;
+ this.rocketmqProducer = new DefaultMQProducer();
+ this.clientConfig = BeanUtils.populate(properties, ClientConfig.class);
+
+ String accessPoints = clientConfig.getOmsAccessPoints();
+ if (accessPoints == null || accessPoints.isEmpty()) {
+ throw new OMSRuntimeException("-1", "OMS AccessPoints is null or empty.");
+ }
+ this.rocketmqProducer.setNamesrvAddr(accessPoints.replace(',', ';'));
+ this.rocketmqProducer.setProducerGroup(clientConfig.getRmqProducerGroup());
+
+ String producerId = buildInstanceName();
+ this.rocketmqProducer.setSendMsgTimeout(clientConfig.getOmsOperationTimeout());
+ this.rocketmqProducer.setInstanceName(producerId);
+ this.rocketmqProducer.setMaxMessageSize(1024 * 1024 * 4);
+ properties.put(PropertyKeys.PRODUCER_ID, producerId);
+ }
+
+ @Override
+ public synchronized void startup() {
+ if (!started) {
+ try {
+ this.rocketmqProducer.start();
+ } catch (MQClientException e) {
+ throw new OMSRuntimeException("-1", e);
+ }
+ }
+ this.started = true;
+ }
+
+ @Override
+ public synchronized void shutdown() {
+ if (this.started) {
+ this.rocketmqProducer.shutdown();
+ }
+ this.started = false;
+ }
+
+ OMSRuntimeException checkProducerException(String topic, String msgId, Throwable e) {
+ if (e instanceof MQClientException) {
+ if (e.getCause() != null) {
+ if (e.getCause() instanceof RemotingTimeoutException) {
+ return new OMSTimeOutException("-1", String.format("Send message to broker timeout, %dms, Topic=%s, msgId=%s",
+ this.rocketmqProducer.getSendMsgTimeout(), topic, msgId), e);
+ } else if (e.getCause() instanceof MQBrokerException || e.getCause() instanceof RemotingConnectException) {
+ MQBrokerException brokerException = (MQBrokerException) e.getCause();
+ return new OMSRuntimeException("-1", String.format("Received a broker exception, Topic=%s, msgId=%s, %s",
+ topic, msgId, brokerException.getErrorMessage()), e);
+ }
+ }
+ // Exception thrown by local.
+ else {
+ MQClientException clientException = (MQClientException) e;
+ if (-1 == clientException.getResponseCode()) {
+ return new OMSRuntimeException("-1", String.format("Topic does not exist, Topic=%s, msgId=%s",
+ topic, msgId), e);
+ } else if (ResponseCode.MESSAGE_ILLEGAL == clientException.getResponseCode()) {
+ return new OMSMessageFormatException("-1", String.format("A illegal message for RocketMQ, Topic=%s, msgId=%s",
+ topic, msgId), e);
+ }
+ }
+ }
+ return new OMSRuntimeException("-1", "Send message to RocketMQ broker failed.", e);
+ }
+
+ protected void checkMessageType(Message message) {
+ if (!(message instanceof BytesMessage)) {
+ throw new OMSNotSupportedException("-1", "Only BytesMessage is supported.");
+ }
+ }
+
+ @Override
+ public BytesMessage createBytesMessageToTopic(final String topic, final byte[] body) {
+ BytesMessage bytesMessage = new BytesMessageImpl();
+ bytesMessage.setBody(body);
+ bytesMessage.headers().put(MessageHeader.TOPIC, topic);
+ return bytesMessage;
+ }
+
+ @Override
+ public BytesMessage createBytesMessageToQueue(final String queue, final byte[] body) {
+ BytesMessage bytesMessage = new BytesMessageImpl();
+ bytesMessage.setBody(body);
+ bytesMessage.headers().put(MessageHeader.QUEUE, queue);
+ return bytesMessage;
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/ProducerImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/ProducerImpl.java
new file mode 100644
index 0000000000..2c00c60ebc
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/ProducerImpl.java
@@ -0,0 +1,124 @@
+/*
+ * 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 io.openmessaging.rocketmq.producer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.KeyValue;
+import io.openmessaging.Message;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.Producer;
+import io.openmessaging.Promise;
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.SendResult;
+import io.openmessaging.exception.OMSRuntimeException;
+import io.openmessaging.rocketmq.promise.DefaultPromise;
+import io.openmessaging.rocketmq.utils.OMSUtil;
+import org.apache.rocketmq.client.producer.SendCallback;
+import org.apache.rocketmq.client.producer.SendStatus;
+
+import static io.openmessaging.rocketmq.utils.OMSUtil.msgConvert;
+
+public class ProducerImpl extends AbstractOMSProducer implements Producer {
+
+ public ProducerImpl(final KeyValue properties) {
+ super(properties);
+ }
+
+ @Override
+ public KeyValue properties() {
+ return properties;
+ }
+
+ @Override
+ public SendResult send(final Message message) {
+ return send(message, this.rocketmqProducer.getSendMsgTimeout());
+ }
+
+ @Override
+ public SendResult send(final Message message, final KeyValue properties) {
+ long timeout = properties.containsKey(PropertyKeys.OPERATION_TIMEOUT)
+ ? properties.getInt(PropertyKeys.OPERATION_TIMEOUT) : this.rocketmqProducer.getSendMsgTimeout();
+ return send(message, timeout);
+ }
+
+ private SendResult send(final Message message, long timeout) {
+ checkMessageType(message);
+ org.apache.rocketmq.common.message.Message rmqMessage = msgConvert((BytesMessage) message);
+ try {
+ org.apache.rocketmq.client.producer.SendResult rmqResult = this.rocketmqProducer.send(rmqMessage, timeout);
+ if (!rmqResult.getSendStatus().equals(SendStatus.SEND_OK)) {
+ log.error(String.format("Send message to RocketMQ failed, %s", message));
+ throw new OMSRuntimeException("-1", "Send message to RocketMQ broker failed.");
+ }
+ message.headers().put(MessageHeader.MESSAGE_ID, rmqResult.getMsgId());
+ return OMSUtil.sendResultConvert(rmqResult);
+ } catch (Exception e) {
+ log.error(String.format("Send message to RocketMQ failed, %s", message), e);
+ throw checkProducerException(rmqMessage.getTopic(), message.headers().getString(MessageHeader.MESSAGE_ID), e);
+ }
+ }
+
+ @Override
+ public Promise sendAsync(final Message message) {
+ return sendAsync(message, this.rocketmqProducer.getSendMsgTimeout());
+ }
+
+ @Override
+ public Promise sendAsync(final Message message, final KeyValue properties) {
+ long timeout = properties.containsKey(PropertyKeys.OPERATION_TIMEOUT)
+ ? properties.getInt(PropertyKeys.OPERATION_TIMEOUT) : this.rocketmqProducer.getSendMsgTimeout();
+ return sendAsync(message, timeout);
+ }
+
+ private Promise sendAsync(final Message message, long timeout) {
+ checkMessageType(message);
+ org.apache.rocketmq.common.message.Message rmqMessage = msgConvert((BytesMessage) message);
+ final Promise promise = new DefaultPromise<>();
+ try {
+ this.rocketmqProducer.send(rmqMessage, new SendCallback() {
+ @Override
+ public void onSuccess(final org.apache.rocketmq.client.producer.SendResult rmqResult) {
+ message.headers().put(MessageHeader.MESSAGE_ID, rmqResult.getMsgId());
+ promise.set(OMSUtil.sendResultConvert(rmqResult));
+ }
+
+ @Override
+ public void onException(final Throwable e) {
+ promise.setFailure(e);
+ }
+ }, timeout);
+ } catch (Exception e) {
+ promise.setFailure(e);
+ }
+ return promise;
+ }
+
+ @Override
+ public void sendOneway(final Message message) {
+ checkMessageType(message);
+ org.apache.rocketmq.common.message.Message rmqMessage = msgConvert((BytesMessage) message);
+ try {
+ this.rocketmqProducer.sendOneway(rmqMessage);
+ } catch (Exception ignore) { //Ignore the oneway exception.
+ }
+ }
+
+ @Override
+ public void sendOneway(final Message message, final KeyValue properties) {
+ sendOneway(message);
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/SequenceProducerImpl.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/SequenceProducerImpl.java
new file mode 100644
index 0000000000..05225cc505
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/SequenceProducerImpl.java
@@ -0,0 +1,95 @@
+/*
+ * 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 io.openmessaging.rocketmq.producer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.KeyValue;
+import io.openmessaging.Message;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.SequenceProducer;
+import io.openmessaging.rocketmq.utils.OMSUtil;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.apache.rocketmq.client.Validators;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.SendResult;
+
+public class SequenceProducerImpl extends AbstractOMSProducer implements SequenceProducer {
+
+ private BlockingQueue msgCacheQueue;
+
+ public SequenceProducerImpl(final KeyValue properties) {
+ super(properties);
+ this.msgCacheQueue = new LinkedBlockingQueue<>();
+ }
+
+ @Override
+ public KeyValue properties() {
+ return properties;
+ }
+
+ @Override
+ public void send(final Message message) {
+ checkMessageType(message);
+ org.apache.rocketmq.common.message.Message rmqMessage = OMSUtil.msgConvert((BytesMessage) message);
+ try {
+ Validators.checkMessage(rmqMessage, this.rocketmqProducer);
+ } catch (MQClientException e) {
+ throw checkProducerException(rmqMessage.getTopic(), message.headers().getString(MessageHeader.MESSAGE_ID), e);
+ }
+ msgCacheQueue.add(message);
+ }
+
+ @Override
+ public void send(final Message message, final KeyValue properties) {
+ send(message);
+ }
+
+ @Override
+ public synchronized void commit() {
+ List messages = new ArrayList<>();
+ msgCacheQueue.drainTo(messages);
+
+ List rmqMessages = new ArrayList<>();
+
+ for (Message message : messages) {
+ rmqMessages.add(OMSUtil.msgConvert((BytesMessage) message));
+ }
+
+ if (rmqMessages.size() == 0) {
+ return;
+ }
+
+ try {
+ SendResult sendResult = this.rocketmqProducer.send(rmqMessages);
+ String[] msgIdArray = sendResult.getMsgId().split(",");
+ for (int i = 0; i < messages.size(); i++) {
+ Message message = messages.get(i);
+ message.headers().put(MessageHeader.MESSAGE_ID, msgIdArray[i]);
+ }
+ } catch (Exception e) {
+ throw checkProducerException("", "", e);
+ }
+ }
+
+ @Override
+ public synchronized void rollback() {
+ msgCacheQueue.clear();
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/promise/DefaultPromise.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/promise/DefaultPromise.java
new file mode 100644
index 0000000000..c863ccf6ad
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/promise/DefaultPromise.java
@@ -0,0 +1,227 @@
+/*
+ * 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 io.openmessaging.rocketmq.promise;
+
+import io.openmessaging.Promise;
+import io.openmessaging.PromiseListener;
+import io.openmessaging.exception.OMSRuntimeException;
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultPromise implements Promise {
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultPromise.class);
+ private final Object lock = new Object();
+ private volatile FutureState state = FutureState.DOING;
+ private V result = null;
+ private long timeout;
+ private long createTime;
+ private Throwable exception = null;
+ private List> promiseListenerList;
+
+ public DefaultPromise() {
+ createTime = System.currentTimeMillis();
+ promiseListenerList = new ArrayList<>();
+ timeout = 5000;
+ }
+
+ @Override
+ public boolean cancel(final boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return state.isCancelledState();
+ }
+
+ @Override
+ public boolean isDone() {
+ return state.isDoneState();
+ }
+
+ @Override
+ public V get() {
+ return result;
+ }
+
+ @Override
+ public V get(final long timeout) {
+ synchronized (lock) {
+ if (!isDoing()) {
+ return getValueOrThrowable();
+ }
+
+ if (timeout <= 0) {
+ try {
+ lock.wait();
+ } catch (Exception e) {
+ cancel(e);
+ }
+ return getValueOrThrowable();
+ } else {
+ long waitTime = timeout - (System.currentTimeMillis() - createTime);
+ if (waitTime > 0) {
+ for (;; ) {
+ try {
+ lock.wait(waitTime);
+ } catch (InterruptedException e) {
+ LOG.error("promise get value interrupted,excepiton:{}", e.getMessage());
+ }
+
+ if (!isDoing()) {
+ break;
+ } else {
+ waitTime = timeout - (System.currentTimeMillis() - createTime);
+ if (waitTime <= 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (isDoing()) {
+ timeoutSoCancel();
+ }
+ }
+ return getValueOrThrowable();
+ }
+ }
+
+ @Override
+ public boolean set(final V value) {
+ if (value == null)
+ return false;
+ this.result = value;
+ return done();
+ }
+
+ @Override
+ public boolean setFailure(final Throwable cause) {
+ if (cause == null)
+ return false;
+ this.exception = cause;
+ return done();
+ }
+
+ @Override
+ public void addListener(final PromiseListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("FutureListener is null");
+ }
+
+ boolean notifyNow = false;
+ synchronized (lock) {
+ if (!isDoing()) {
+ notifyNow = true;
+ } else {
+ if (promiseListenerList == null) {
+ promiseListenerList = new ArrayList<>();
+ }
+ promiseListenerList.add(listener);
+ }
+ }
+
+ if (notifyNow) {
+ notifyListener(listener);
+ }
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return exception;
+ }
+
+ private void notifyListeners() {
+ if (promiseListenerList != null) {
+ for (PromiseListener listener : promiseListenerList) {
+ notifyListener(listener);
+ }
+ }
+ }
+
+ private boolean isSuccess() {
+ return isDone() && (exception == null);
+ }
+
+ private void timeoutSoCancel() {
+ synchronized (lock) {
+ if (!isDoing()) {
+ return;
+ }
+ state = FutureState.CANCELLED;
+ exception = new RuntimeException("Get request result is timeout or interrupted");
+ lock.notifyAll();
+ }
+ notifyListeners();
+ }
+
+ private V getValueOrThrowable() {
+ if (exception != null) {
+ Throwable e = exception.getCause() != null ? exception.getCause() : exception;
+ throw new OMSRuntimeException("-1", e);
+ }
+ notifyListeners();
+ return result;
+ }
+
+ private boolean isDoing() {
+ return state.isDoingState();
+ }
+
+ private boolean done() {
+ synchronized (lock) {
+ if (!isDoing()) {
+ return false;
+ }
+
+ state = FutureState.DONE;
+ lock.notifyAll();
+ }
+
+ notifyListeners();
+ return true;
+ }
+
+ private void notifyListener(final PromiseListener listener) {
+ try {
+ if (exception != null)
+ listener.operationFailed(this);
+ else
+ listener.operationCompleted(this);
+ } catch (Throwable t) {
+ LOG.error("notifyListener {} Error:{}", listener.getClass().getSimpleName(), t);
+ }
+ }
+
+ private boolean cancel(Exception e) {
+ synchronized (lock) {
+ if (!isDoing()) {
+ return false;
+ }
+
+ state = FutureState.CANCELLED;
+ exception = e;
+ lock.notifyAll();
+ }
+
+ notifyListeners();
+ return true;
+ }
+}
+
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/promise/FutureState.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/promise/FutureState.java
new file mode 100644
index 0000000000..84b6c2d74e
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/promise/FutureState.java
@@ -0,0 +1,51 @@
+/*
+ * 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 io.openmessaging.rocketmq.promise;
+
+public enum FutureState {
+ /**
+ * the task is doing
+ **/
+ DOING(0),
+ /**
+ * the task is done
+ **/
+ DONE(1),
+ /**
+ * ths task is cancelled
+ **/
+ CANCELLED(2);
+
+ public final int value;
+
+ private FutureState(int value) {
+ this.value = value;
+ }
+
+ public boolean isCancelledState() {
+ return this == CANCELLED;
+ }
+
+ public boolean isDoneState() {
+ return this == DONE;
+ }
+
+ public boolean isDoingState() {
+ return this == DOING;
+ }
+}
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/utils/BeanUtils.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/utils/BeanUtils.java
new file mode 100644
index 0000000000..104d3d964f
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/utils/BeanUtils.java
@@ -0,0 +1,185 @@
+/*
+ * 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 io.openmessaging.rocketmq.utils;
+
+import io.openmessaging.KeyValue;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.client.log.ClientLogger;
+import org.slf4j.Logger;
+
+public final class BeanUtils {
+ final static Logger log = ClientLogger.getLog();
+
+ /**
+ * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}.
+ */
+ private static Map, Class>> primitiveWrapperMap = new HashMap, Class>>();
+
+ static {
+ primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
+ primitiveWrapperMap.put(Byte.TYPE, Byte.class);
+ primitiveWrapperMap.put(Character.TYPE, Character.class);
+ primitiveWrapperMap.put(Short.TYPE, Short.class);
+ primitiveWrapperMap.put(Integer.TYPE, Integer.class);
+ primitiveWrapperMap.put(Long.TYPE, Long.class);
+ primitiveWrapperMap.put(Double.TYPE, Double.class);
+ primitiveWrapperMap.put(Float.TYPE, Float.class);
+ primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
+ }
+
+ private static Map, Class>> wrapperMap = new HashMap, Class>>();
+
+ static {
+ for (final Class> primitiveClass : primitiveWrapperMap.keySet()) {
+ final Class> wrapperClass = primitiveWrapperMap.get(primitiveClass);
+ if (!primitiveClass.equals(wrapperClass)) {
+ wrapperMap.put(wrapperClass, primitiveClass);
+ }
+ }
+ wrapperMap.put(String.class, String.class);
+ }
+
+ /**
+ *
Populate the JavaBeans properties of the specified bean, based on
+ * the specified name/value pairs. This method uses Java reflection APIs
+ * to identify corresponding "property setter" method names, and deals
+ * with setter arguments of type String, boolean,
+ * int, long, float, and
+ * double.
+ *
+ *
The particular setter method to be called for each property is
+ * determined using the usual JavaBeans introspection mechanisms. Thus,
+ * you may identify custom setter methods using a BeanInfo class that is
+ * associated with the class of the bean itself. If no such BeanInfo
+ * class is available, the standard method name conversion ("set" plus
+ * the capitalized name of the property in question) is used.
+ *
+ *
NOTE: It is contrary to the JavaBeans Specification
+ * to have more than one setter method (with different argument
+ * signatures) for the same property.
+ *
+ * @param clazz JavaBean class whose properties are being populated
+ * @param properties Map keyed by property name, with the corresponding (String or String[]) value(s) to be set
+ * @param Class type
+ * @return Class instance
+ */
+ public static T populate(final Properties properties, final Class clazz) {
+ T obj = null;
+ try {
+ obj = clazz.newInstance();
+ return populate(properties, obj);
+ } catch (Throwable e) {
+ log.warn("Error occurs !", e);
+ }
+ return obj;
+ }
+
+ public static T populate(final KeyValue properties, final Class clazz) {
+ T obj = null;
+ try {
+ obj = clazz.newInstance();
+ return populate(properties, obj);
+ } catch (Throwable e) {
+ log.warn("Error occurs !", e);
+ }
+ return obj;
+ }
+
+ public static Class> getMethodClass(Class> clazz, String methodName) {
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equalsIgnoreCase(methodName)) {
+ return method.getParameterTypes()[0];
+ }
+ }
+ return null;
+ }
+
+ public static void setProperties(Class> clazz, Object obj, String methodName,
+ Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ Class> parameterClass = getMethodClass(clazz, methodName);
+ Method setterMethod = clazz.getMethod(methodName, parameterClass);
+ if (parameterClass == Boolean.TYPE) {
+ setterMethod.invoke(obj, Boolean.valueOf(value.toString()));
+ } else if (parameterClass == Integer.TYPE) {
+ setterMethod.invoke(obj, Integer.valueOf(value.toString()));
+ } else if (parameterClass == Double.TYPE) {
+ setterMethod.invoke(obj, Double.valueOf(value.toString()));
+ } else if (parameterClass == Float.TYPE) {
+ setterMethod.invoke(obj, Float.valueOf(value.toString()));
+ } else if (parameterClass == Long.TYPE) {
+ setterMethod.invoke(obj, Long.valueOf(value.toString()));
+ } else
+ setterMethod.invoke(obj, value);
+ }
+
+ public static T populate(final Properties properties, final T obj) {
+ Class> clazz = obj.getClass();
+ try {
+
+ Set> entries = properties.entrySet();
+ for (Map.Entry entry : entries) {
+ String entryKey = entry.getKey().toString();
+ String[] keyGroup = entryKey.split("\\.");
+ for (int i = 0; i < keyGroup.length; i++) {
+ keyGroup[i] = keyGroup[i].toLowerCase();
+ keyGroup[i] = StringUtils.capitalize(keyGroup[i]);
+ }
+ String beanFieldNameWithCapitalization = StringUtils.join(keyGroup);
+ try {
+ setProperties(clazz, obj, "set" + beanFieldNameWithCapitalization, entry.getValue());
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
+ //ignored...
+ }
+ }
+ } catch (RuntimeException e) {
+ log.warn("Error occurs !", e);
+ }
+ return obj;
+ }
+
+ public static T populate(final KeyValue properties, final T obj) {
+ Class> clazz = obj.getClass();
+ try {
+
+ final Set keySet = properties.keySet();
+ for (String key : keySet) {
+ String[] keyGroup = key.split("\\.");
+ for (int i = 0; i < keyGroup.length; i++) {
+ keyGroup[i] = keyGroup[i].toLowerCase();
+ keyGroup[i] = StringUtils.capitalize(keyGroup[i]);
+ }
+ String beanFieldNameWithCapitalization = StringUtils.join(keyGroup);
+ try {
+ setProperties(clazz, obj, "set" + beanFieldNameWithCapitalization, properties.getString(key));
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
+ //ignored...
+ }
+ }
+ } catch (RuntimeException e) {
+ log.warn("Error occurs !", e);
+ }
+ return obj;
+ }
+}
+
diff --git a/openmessaging/src/main/java/io/openmessaging/rocketmq/utils/OMSUtil.java b/openmessaging/src/main/java/io/openmessaging/rocketmq/utils/OMSUtil.java
new file mode 100644
index 0000000000..60c840813c
--- /dev/null
+++ b/openmessaging/src/main/java/io/openmessaging/rocketmq/utils/OMSUtil.java
@@ -0,0 +1,182 @@
+/*
+ * 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 io.openmessaging.rocketmq.utils;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.KeyValue;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.OMS;
+import io.openmessaging.SendResult;
+import io.openmessaging.rocketmq.domain.BytesMessageImpl;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+import io.openmessaging.rocketmq.domain.SendResultImpl;
+import java.lang.reflect.Field;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.apache.rocketmq.client.producer.SendStatus;
+import org.apache.rocketmq.common.UtilAll;
+import org.apache.rocketmq.common.message.MessageAccessor;
+
+public class OMSUtil {
+
+ /**
+ * Builds a OMS client instance name.
+ *
+ * @return a unique instance name
+ */
+ public static String buildInstanceName() {
+ return Integer.toString(UtilAll.getPid()) + "%OpenMessaging" + "%" + System.nanoTime();
+ }
+
+ public static org.apache.rocketmq.common.message.Message msgConvert(BytesMessage omsMessage) {
+ org.apache.rocketmq.common.message.Message rmqMessage = new org.apache.rocketmq.common.message.Message();
+ rmqMessage.setBody(omsMessage.getBody());
+
+ KeyValue headers = omsMessage.headers();
+ KeyValue properties = omsMessage.properties();
+
+ //All destinations in RocketMQ use Topic
+ if (headers.containsKey(MessageHeader.TOPIC)) {
+ rmqMessage.setTopic(headers.getString(MessageHeader.TOPIC));
+ rmqMessage.putUserProperty(NonStandardKeys.MESSAGE_DESTINATION, "TOPIC");
+ } else {
+ rmqMessage.setTopic(headers.getString(MessageHeader.QUEUE));
+ rmqMessage.putUserProperty(NonStandardKeys.MESSAGE_DESTINATION, "QUEUE");
+ }
+
+ for (String key : properties.keySet()) {
+ MessageAccessor.putProperty(rmqMessage, key, properties.getString(key));
+ }
+
+ //Headers has a high priority
+ for (String key : headers.keySet()) {
+ MessageAccessor.putProperty(rmqMessage, key, headers.getString(key));
+ }
+
+ return rmqMessage;
+ }
+
+ public static BytesMessage msgConvert(org.apache.rocketmq.common.message.MessageExt rmqMsg) {
+ BytesMessage omsMsg = new BytesMessageImpl();
+ omsMsg.setBody(rmqMsg.getBody());
+
+ KeyValue headers = omsMsg.headers();
+ KeyValue properties = omsMsg.properties();
+
+ final Set> entries = rmqMsg.getProperties().entrySet();
+
+ for (final Map.Entry entry : entries) {
+ if (isOMSHeader(entry.getKey())) {
+ headers.put(entry.getKey(), entry.getValue());
+ } else {
+ properties.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ omsMsg.putHeaders(MessageHeader.MESSAGE_ID, rmqMsg.getMsgId());
+ if (!rmqMsg.getProperties().containsKey(NonStandardKeys.MESSAGE_DESTINATION) ||
+ rmqMsg.getProperties().get(NonStandardKeys.MESSAGE_DESTINATION).equals("TOPIC")) {
+ omsMsg.putHeaders(MessageHeader.TOPIC, rmqMsg.getTopic());
+ } else {
+ omsMsg.putHeaders(MessageHeader.QUEUE, rmqMsg.getTopic());
+ }
+ omsMsg.putHeaders(MessageHeader.SEARCH_KEY, rmqMsg.getKeys());
+ omsMsg.putHeaders(MessageHeader.BORN_HOST, String.valueOf(rmqMsg.getBornHost()));
+ omsMsg.putHeaders(MessageHeader.BORN_TIMESTAMP, rmqMsg.getBornTimestamp());
+ omsMsg.putHeaders(MessageHeader.STORE_HOST, String.valueOf(rmqMsg.getStoreHost()));
+ omsMsg.putHeaders(MessageHeader.STORE_TIMESTAMP, rmqMsg.getStoreTimestamp());
+ return omsMsg;
+ }
+
+ public static boolean isOMSHeader(String value) {
+ for (Field field : MessageHeader.class.getDeclaredFields()) {
+ try {
+ if (field.get(MessageHeader.class).equals(value)) {
+ return true;
+ }
+ } catch (IllegalAccessException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Convert a RocketMQ SEND_OK SendResult instance to a OMS SendResult.
+ */
+ public static SendResult sendResultConvert(org.apache.rocketmq.client.producer.SendResult rmqResult) {
+ assert rmqResult.getSendStatus().equals(SendStatus.SEND_OK);
+ return new SendResultImpl(rmqResult.getMsgId(), OMS.newKeyValue());
+ }
+
+ public static KeyValue buildKeyValue(KeyValue... keyValues) {
+ KeyValue keyValue = OMS.newKeyValue();
+ for (KeyValue properties : keyValues) {
+ for (String key : properties.keySet()) {
+ keyValue.put(key, properties.getString(key));
+ }
+ }
+ return keyValue;
+ }
+
+ /**
+ * Returns an iterator that cycles indefinitely over the elements of {@code Iterable}.
+ */
+ public static Iterator cycle(final Iterable iterable) {
+ return new Iterator() {
+ Iterator iterator = new Iterator() {
+ @Override
+ public synchronized boolean hasNext() {
+ return false;
+ }
+
+ @Override
+ public synchronized T next() {
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public synchronized void remove() {
+ //Ignore
+ }
+ };
+
+ @Override
+ public synchronized boolean hasNext() {
+ return iterator.hasNext() || iterable.iterator().hasNext();
+ }
+
+ @Override
+ public synchronized T next() {
+ if (!iterator.hasNext()) {
+ iterator = iterable.iterator();
+ if (!iterator.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ }
+ return iterator.next();
+ }
+
+ @Override
+ public synchronized void remove() {
+ iterator.remove();
+ }
+ };
+ }
+}
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/LocalMessageCacheTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/LocalMessageCacheTest.java
new file mode 100644
index 0000000000..ae4d3ed537
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/LocalMessageCacheTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 io.openmessaging.rocketmq.consumer;
+
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.ConsumeRequest;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+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.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LocalMessageCacheTest {
+ private LocalMessageCache localMessageCache;
+ @Mock
+ private DefaultMQPullConsumer rocketmqPullConsume;
+ @Mock
+ private ConsumeRequest consumeRequest;
+
+ @Before
+ public void init() {
+ ClientConfig clientConfig = new ClientConfig();
+ clientConfig.setRmqPullMessageBatchNums(512);
+ clientConfig.setRmqPullMessageCacheCapacity(1024);
+ localMessageCache = new LocalMessageCache(rocketmqPullConsume, clientConfig);
+ }
+
+ @Test
+ public void testNextPullBatchNums() throws Exception {
+ assertThat(localMessageCache.nextPullBatchNums()).isEqualTo(512);
+ for (int i = 0; i < 513; i++) {
+ localMessageCache.submitConsumeRequest(consumeRequest);
+ }
+ assertThat(localMessageCache.nextPullBatchNums()).isEqualTo(511);
+ }
+
+ @Test
+ public void testNextPullOffset() throws Exception {
+ MessageQueue messageQueue = new MessageQueue();
+ when(rocketmqPullConsume.fetchConsumeOffset(any(MessageQueue.class), anyBoolean()))
+ .thenReturn(123L);
+ assertThat(localMessageCache.nextPullOffset(new MessageQueue())).isEqualTo(123L);
+ }
+
+ @Test
+ public void testUpdatePullOffset() throws Exception {
+ MessageQueue messageQueue = new MessageQueue();
+ localMessageCache.updatePullOffset(messageQueue, 124L);
+ assertThat(localMessageCache.nextPullOffset(messageQueue)).isEqualTo(124L);
+ }
+
+ @Test
+ public void testSubmitConsumeRequest() throws Exception {
+ byte [] body = new byte[]{'1', '2', '3'};
+ MessageExt consumedMsg = new MessageExt();
+ consumedMsg.setMsgId("NewMsgId");
+ consumedMsg.setBody(body);
+ consumedMsg.putUserProperty(NonStandardKeys.MESSAGE_DESTINATION, "TOPIC");
+ consumedMsg.setTopic("HELLO_QUEUE");
+
+ when(consumeRequest.getMessageExt()).thenReturn(consumedMsg);
+ localMessageCache.submitConsumeRequest(consumeRequest);
+ assertThat(localMessageCache.poll()).isEqualTo(consumedMsg);
+ }
+}
\ No newline at end of file
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PullConsumerImplTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PullConsumerImplTest.java
new file mode 100644
index 0000000000..277a5c65f2
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PullConsumerImplTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 io.openmessaging.rocketmq.consumer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.Message;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.OMS;
+import io.openmessaging.PropertyKeys;
+import io.openmessaging.PullConsumer;
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+import java.lang.reflect.Field;
+import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
+import org.apache.rocketmq.common.message.MessageExt;
+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.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PullConsumerImplTest {
+ private PullConsumer consumer;
+ private String queueName = "HELLO_QUEUE";
+
+ @Mock
+ private DefaultMQPullConsumer rocketmqPullConsumer;
+ private LocalMessageCache localMessageCache =
+ spy(new LocalMessageCache(rocketmqPullConsumer, new ClientConfig()));
+
+ @Before
+ public void init() throws NoSuchFieldException, IllegalAccessException {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+ consumer = messagingAccessPoint.createPullConsumer(queueName,
+ OMS.newKeyValue().put(NonStandardKeys.CONSUMER_GROUP, "TestGroup"));
+
+ Field field = PullConsumerImpl.class.getDeclaredField("rocketmqPullConsumer");
+ field.setAccessible(true);
+ field.set(consumer, rocketmqPullConsumer); //Replace
+
+ field = PullConsumerImpl.class.getDeclaredField("localMessageCache");
+ field.setAccessible(true);
+ field.set(consumer, localMessageCache);
+
+ messagingAccessPoint.startup();
+ consumer.startup();
+ }
+
+ @Test
+ public void testPoll() {
+ final byte[] testBody = new byte[] {'a', 'b'};
+ MessageExt consumedMsg = new MessageExt();
+ consumedMsg.setMsgId("NewMsgId");
+ consumedMsg.setBody(testBody);
+ consumedMsg.putUserProperty(NonStandardKeys.MESSAGE_DESTINATION, "TOPIC");
+ consumedMsg.setTopic(queueName);
+
+ when(localMessageCache.poll()).thenReturn(consumedMsg);
+
+ Message message = consumer.poll();
+ assertThat(message.headers().getString(MessageHeader.MESSAGE_ID)).isEqualTo("NewMsgId");
+ assertThat(((BytesMessage) message).getBody()).isEqualTo(testBody);
+ }
+
+ @Test
+ public void testPoll_WithTimeout() {
+ //There is a default timeout value, @see ClientConfig#omsOperationTimeout.
+ Message message = consumer.poll();
+ assertThat(message).isNull();
+
+ message = consumer.poll(OMS.newKeyValue().put(PropertyKeys.OPERATION_TIMEOUT, 100));
+ assertThat(message).isNull();
+ }
+}
\ No newline at end of file
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PushConsumerImplTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PushConsumerImplTest.java
new file mode 100644
index 0000000000..882e57ea5e
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/consumer/PushConsumerImplTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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 io.openmessaging.rocketmq.consumer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.Message;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.MessageListener;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.OMS;
+import io.openmessaging.PushConsumer;
+import io.openmessaging.ReceivedMessageContext;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
+import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
+import org.apache.rocketmq.common.message.MessageExt;
+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.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PushConsumerImplTest {
+ private PushConsumer consumer;
+
+ @Mock
+ private DefaultMQPushConsumer rocketmqPushConsumer;
+
+ @Before
+ public void init() throws NoSuchFieldException, IllegalAccessException {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+ consumer = messagingAccessPoint.createPushConsumer(
+ OMS.newKeyValue().put(NonStandardKeys.CONSUMER_GROUP, "TestGroup"));
+
+ Field field = PushConsumerImpl.class.getDeclaredField("rocketmqPushConsumer");
+ field.setAccessible(true);
+ DefaultMQPushConsumer innerConsumer = (DefaultMQPushConsumer) field.get(consumer);
+ field.set(consumer, rocketmqPushConsumer); //Replace
+
+ when(rocketmqPushConsumer.getMessageListener()).thenReturn(innerConsumer.getMessageListener());
+ messagingAccessPoint.startup();
+ consumer.startup();
+ }
+
+ @Test
+ public void testConsumeMessage() {
+ final byte[] testBody = new byte[] {'a', 'b'};
+
+ MessageExt consumedMsg = new MessageExt();
+ consumedMsg.setMsgId("NewMsgId");
+ consumedMsg.setBody(testBody);
+ consumedMsg.putUserProperty(NonStandardKeys.MESSAGE_DESTINATION, "TOPIC");
+ consumedMsg.setTopic("HELLO_QUEUE");
+ consumer.attachQueue("HELLO_QUEUE", new MessageListener() {
+ @Override
+ public void onMessage(final Message message, final ReceivedMessageContext context) {
+ assertThat(message.headers().getString(MessageHeader.MESSAGE_ID)).isEqualTo("NewMsgId");
+ assertThat(((BytesMessage) message).getBody()).isEqualTo(testBody);
+ context.ack();
+ }
+ });
+ ((MessageListenerConcurrently) rocketmqPushConsumer
+ .getMessageListener()).consumeMessage(Collections.singletonList(consumedMsg), null);
+ }
+}
\ No newline at end of file
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/producer/ProducerImplTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/producer/ProducerImplTest.java
new file mode 100644
index 0000000000..1db80c3efa
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/producer/ProducerImplTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 io.openmessaging.rocketmq.producer;
+
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.Producer;
+import io.openmessaging.exception.OMSRuntimeException;
+import java.lang.reflect.Field;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.client.producer.SendStatus;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+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.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ProducerImplTest {
+ private Producer producer;
+
+ @Mock
+ private DefaultMQProducer rocketmqProducer;
+
+ @Before
+ public void init() throws NoSuchFieldException, IllegalAccessException {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+ producer = messagingAccessPoint.createProducer();
+
+ Field field = AbstractOMSProducer.class.getDeclaredField("rocketmqProducer");
+ field.setAccessible(true);
+ field.set(producer, rocketmqProducer);
+
+ messagingAccessPoint.startup();
+ producer.startup();
+ }
+
+ @Test
+ public void testSend_OK() throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
+ SendResult sendResult = new SendResult();
+ sendResult.setMsgId("TestMsgID");
+ sendResult.setSendStatus(SendStatus.SEND_OK);
+ when(rocketmqProducer.send(any(Message.class), anyLong())).thenReturn(sendResult);
+ io.openmessaging.SendResult omsResult =
+ producer.send(producer.createBytesMessageToTopic("HELLO_TOPIC", new byte[] {'a'}));
+
+ assertThat(omsResult.messageId()).isEqualTo("TestMsgID");
+ }
+
+ @Test
+ public void testSend_Not_OK() throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
+ SendResult sendResult = new SendResult();
+ sendResult.setSendStatus(SendStatus.FLUSH_DISK_TIMEOUT);
+
+ when(rocketmqProducer.send(any(Message.class), anyLong())).thenReturn(sendResult);
+ try {
+ producer.send(producer.createBytesMessageToTopic("HELLO_TOPIC", new byte[] {'a'}));
+ failBecauseExceptionWasNotThrown(OMSRuntimeException.class);
+ } catch (Exception e) {
+ assertThat(e).hasMessageContaining("Send message to RocketMQ broker failed.");
+ }
+ }
+
+ @Test
+ public void testSend_WithException() throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
+ when(rocketmqProducer.send(any(Message.class), anyLong())).thenThrow(MQClientException.class);
+ try {
+ producer.send(producer.createBytesMessageToTopic("HELLO_TOPIC", new byte[] {'a'}));
+ failBecauseExceptionWasNotThrown(OMSRuntimeException.class);
+ } catch (Exception e) {
+ assertThat(e).hasMessageContaining("Send message to RocketMQ broker failed.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/producer/SequenceProducerImplTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/producer/SequenceProducerImplTest.java
new file mode 100644
index 0000000000..823fe015cc
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/producer/SequenceProducerImplTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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 io.openmessaging.rocketmq.producer;
+
+import io.openmessaging.BytesMessage;
+import io.openmessaging.MessageHeader;
+import io.openmessaging.MessagingAccessPoint;
+import io.openmessaging.MessagingAccessPointFactory;
+import io.openmessaging.SequenceProducer;
+import java.lang.reflect.Field;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.client.producer.SendStatus;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SequenceProducerImplTest {
+
+ private SequenceProducer producer;
+
+ @Mock
+ private DefaultMQProducer rocketmqProducer;
+
+ @Before
+ public void init() throws NoSuchFieldException, IllegalAccessException {
+ final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointFactory
+ .getMessagingAccessPoint("openmessaging:rocketmq://IP1:9876,IP2:9876/namespace");
+ producer = messagingAccessPoint.createSequenceProducer();
+
+ Field field = AbstractOMSProducer.class.getDeclaredField("rocketmqProducer");
+ field.setAccessible(true);
+ field.set(producer, rocketmqProducer);
+
+ messagingAccessPoint.startup();
+ producer.startup();
+ }
+
+ @Test
+ public void testSend_WithCommit() throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
+ SendResult sendResult = new SendResult();
+ sendResult.setMsgId("TestMsgID");
+ sendResult.setSendStatus(SendStatus.SEND_OK);
+ when(rocketmqProducer.send(ArgumentMatchers.anyList())).thenReturn(sendResult);
+ when(rocketmqProducer.getMaxMessageSize()).thenReturn(1024);
+ final BytesMessage message = producer.createBytesMessageToTopic("HELLO_TOPIC", new byte[] {'a'});
+ producer.send(message);
+ producer.commit();
+ assertThat(message.headers().getString(MessageHeader.MESSAGE_ID)).isEqualTo("TestMsgID");
+ }
+
+ @Test
+ public void testRollback() {
+ when(rocketmqProducer.getMaxMessageSize()).thenReturn(1024);
+ final BytesMessage message = producer.createBytesMessageToTopic("HELLO_TOPIC", new byte[] {'a'});
+ producer.send(message);
+ producer.rollback();
+ producer.commit(); //Commit nothing.
+ assertThat(message.headers().getString(MessageHeader.MESSAGE_ID)).isEqualTo(null);
+ }
+}
\ No newline at end of file
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/promise/DefaultPromiseTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/promise/DefaultPromiseTest.java
new file mode 100644
index 0000000000..2240ff2dd1
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/promise/DefaultPromiseTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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 io.openmessaging.rocketmq.promise;
+
+import io.openmessaging.Promise;
+import io.openmessaging.PromiseListener;
+import io.openmessaging.exception.OMSRuntimeException;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
+
+public class DefaultPromiseTest {
+ private Promise promise;
+
+ @Before
+ public void init() {
+ promise = new DefaultPromise<>();
+ }
+
+ @Test
+ public void testIsCancelled() throws Exception {
+ assertThat(promise.isCancelled()).isEqualTo(false);
+ }
+
+ @Test
+ public void testIsDone() throws Exception {
+ assertThat(promise.isDone()).isEqualTo(false);
+ promise.set("Done");
+ assertThat(promise.isDone()).isEqualTo(true);
+ }
+
+ @Test
+ public void testGet() throws Exception {
+ promise.set("Done");
+ assertThat(promise.get()).isEqualTo("Done");
+ }
+
+ @Test
+ public void testGet_WithTimeout() throws Exception {
+ try {
+ promise.get(100);
+ failBecauseExceptionWasNotThrown(OMSRuntimeException.class);
+ } catch (OMSRuntimeException e) {
+ assertThat(e).hasMessageContaining("Get request result is timeout or interrupted");
+ }
+ }
+
+ @Test
+ public void testAddListener() throws Exception {
+ promise.addListener(new PromiseListener() {
+ @Override
+ public void operationCompleted(final Promise promise) {
+ assertThat(promise.get()).isEqualTo("Done");
+ }
+
+ @Override
+ public void operationFailed(final Promise promise) {
+
+ }
+ });
+ promise.set("Done");
+ }
+
+ @Test
+ public void testAddListener_ListenerAfterSet() throws Exception {
+ promise.set("Done");
+ promise.addListener(new PromiseListener() {
+ @Override
+ public void operationCompleted(final Promise promise) {
+ assertThat(promise.get()).isEqualTo("Done");
+ }
+
+ @Override
+ public void operationFailed(final Promise promise) {
+
+ }
+ });
+ }
+
+ @Test
+ public void testAddListener_WithException_ListenerAfterSet() throws Exception {
+ final Throwable exception = new OMSRuntimeException("-1", "Test Error");
+ promise.setFailure(exception);
+ promise.addListener(new PromiseListener() {
+ @Override
+ public void operationCompleted(final Promise promise) {
+ }
+
+ @Override
+ public void operationFailed(final Promise promise) {
+ assertThat(promise.getThrowable()).isEqualTo(exception);
+ }
+ });
+ }
+
+ @Test
+ public void testAddListener_WithException() throws Exception {
+ final Throwable exception = new OMSRuntimeException("-1", "Test Error");
+ promise.addListener(new PromiseListener() {
+ @Override
+ public void operationCompleted(final Promise promise) {
+ }
+
+ @Override
+ public void operationFailed(final Promise promise) {
+ assertThat(promise.getThrowable()).isEqualTo(exception);
+ }
+ });
+ promise.setFailure(exception);
+ }
+
+ @Test
+ public void getThrowable() throws Exception {
+ assertThat(promise.getThrowable()).isNull();
+ Throwable exception = new OMSRuntimeException("-1", "Test Error");
+ promise.setFailure(exception);
+ assertThat(promise.getThrowable()).isEqualTo(exception);
+ }
+
+}
\ No newline at end of file
diff --git a/openmessaging/src/test/java/io/openmessaging/rocketmq/utils/BeanUtilsTest.java b/openmessaging/src/test/java/io/openmessaging/rocketmq/utils/BeanUtilsTest.java
new file mode 100644
index 0000000000..71ca11ccfd
--- /dev/null
+++ b/openmessaging/src/test/java/io/openmessaging/rocketmq/utils/BeanUtilsTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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 io.openmessaging.rocketmq.utils;
+
+import io.openmessaging.KeyValue;
+import io.openmessaging.OMS;
+import io.openmessaging.rocketmq.config.ClientConfig;
+import io.openmessaging.rocketmq.domain.NonStandardKeys;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BeanUtilsTest {
+ private KeyValue properties = OMS.newKeyValue();
+
+ public static class CustomizedConfig extends ClientConfig {
+ final static String STRING_TEST = "string.test";
+ String stringTest = "foobar";
+
+ final static String DOUBLE_TEST = "double.test";
+ double doubleTest = 123.0;
+
+ final static String LONG_TEST = "long.test";
+ long longTest = 123L;
+
+ String getStringTest() {
+ return stringTest;
+ }
+
+ public void setStringTest(String stringTest) {
+ this.stringTest = stringTest;
+ }
+
+ double getDoubleTest() {
+ return doubleTest;
+ }
+
+ public void setDoubleTest(final double doubleTest) {
+ this.doubleTest = doubleTest;
+ }
+
+ long getLongTest() {
+ return longTest;
+ }
+
+ public void setLongTest(final long longTest) {
+ this.longTest = longTest;
+ }
+
+ CustomizedConfig() {
+ }
+ }
+
+ @Before
+ public void init() {
+ properties.put(NonStandardKeys.MAX_REDELIVERY_TIMES, 120);
+ properties.put(CustomizedConfig.STRING_TEST, "kaka");
+ properties.put(NonStandardKeys.CONSUMER_GROUP, "Default_Consumer_Group");
+ properties.put(NonStandardKeys.MESSAGE_CONSUME_TIMEOUT, 101);
+
+ properties.put(CustomizedConfig.LONG_TEST, 1234567890L);
+ properties.put(CustomizedConfig.DOUBLE_TEST, 10.234);
+ }
+
+ @Test
+ public void testPopulate() {
+ CustomizedConfig config = BeanUtils.populate(properties, CustomizedConfig.class);
+
+ //RemotingConfig config = BeanUtils.populate(properties, RemotingConfig.class);
+ Assert.assertEquals(config.getRmqMaxRedeliveryTimes(), 120);
+ Assert.assertEquals(config.getStringTest(), "kaka");
+ Assert.assertEquals(config.getRmqConsumerGroup(), "Default_Consumer_Group");
+ Assert.assertEquals(config.getRmqMessageConsumeTimeout(), 101);
+ Assert.assertEquals(config.getLongTest(), 1234567890L);
+ Assert.assertEquals(config.getDoubleTest(), 10.234, 0.000001);
+ }
+
+ @Test
+ public void testPopulate_ExistObj() {
+ CustomizedConfig config = new CustomizedConfig();
+ config.setOmsConsumerId("NewConsumerId");
+
+ Assert.assertEquals(config.getOmsConsumerId(), "NewConsumerId");
+
+ config = BeanUtils.populate(properties, config);
+
+ //RemotingConfig config = BeanUtils.populate(properties, RemotingConfig.class);
+ Assert.assertEquals(config.getRmqMaxRedeliveryTimes(), 120);
+ Assert.assertEquals(config.getStringTest(), "kaka");
+ Assert.assertEquals(config.getRmqConsumerGroup(), "Default_Consumer_Group");
+ Assert.assertEquals(config.getRmqMessageConsumeTimeout(), 101);
+ Assert.assertEquals(config.getLongTest(), 1234567890L);
+ Assert.assertEquals(config.getDoubleTest(), 10.234, 0.000001);
+ }
+
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 05ead63974..25e4c841b3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -181,6 +181,7 @@
filtertestdistribution
+ openmessaging
@@ -617,6 +618,11 @@
guava19.0
+
+ io.openmessaging
+ openmessaging-api
+ 0.1.0-alpha
+
From f009ba0b3e7e9166bbb56cdbdb94703db5825ea4 Mon Sep 17 00:00:00 2001
From: yukon
Date: Thu, 25 May 2017 13:48:22 +0800
Subject: [PATCH 45/74] [ROCKETMQ-206] Fix bug when non-1byte character exists
in JSON config files.
---
.../org/apache/rocketmq/common/MixAll.java | 26 +++++++------------
.../apache/rocketmq/common/MixAllTest.java | 20 ++++++++++++++
2 files changed, 30 insertions(+), 16 deletions(-)
diff --git a/common/src/main/java/org/apache/rocketmq/common/MixAll.java b/common/src/main/java/org/apache/rocketmq/common/MixAll.java
index e75efd9a3b..36d81d09bc 100644
--- a/common/src/main/java/org/apache/rocketmq/common/MixAll.java
+++ b/common/src/main/java/org/apache/rocketmq/common/MixAll.java
@@ -18,7 +18,7 @@ package org.apache.rocketmq.common;
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -187,30 +187,24 @@ public class MixAll {
}
}
- public static String file2String(final String fileName) {
+ public static String file2String(final String fileName) throws IOException {
File file = new File(fileName);
return file2String(file);
}
- public static String file2String(final File file) {
+ public static String file2String(final File file) throws IOException {
if (file.exists()) {
- char[] data = new char[(int) file.length()];
- boolean result = false;
+ byte[] data = new byte[(int) file.length()];
+ boolean result;
- FileReader fileReader = null;
+ FileInputStream inputStream = null;
try {
- fileReader = new FileReader(file);
- int len = fileReader.read(data);
+ inputStream = new FileInputStream(file);
+ int len = inputStream.read(data);
result = len == data.length;
- } catch (IOException e) {
- // e.printStackTrace();
} finally {
- if (fileReader != null) {
- try {
- fileReader.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
+ if (inputStream != null) {
+ inputStream.close();
}
}
diff --git a/common/src/test/java/org/apache/rocketmq/common/MixAllTest.java b/common/src/test/java/org/apache/rocketmq/common/MixAllTest.java
index 8220981e51..218b36d2d8 100644
--- a/common/src/test/java/org/apache/rocketmq/common/MixAllTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/MixAllTest.java
@@ -21,6 +21,10 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Test;
@@ -67,6 +71,22 @@ public class MixAllTest {
file.delete();
}
+ @Test
+ public void testFile2String_WithChinese() throws IOException {
+ String fileName = System.getProperty("java.io.tmpdir") + File.separator + "MixAllTest" + System.currentTimeMillis();
+ File file = new File(fileName);
+ if (file.exists()) {
+ file.delete();
+ }
+ file.createNewFile();
+ PrintWriter out = new PrintWriter(fileName);
+ out.write("TestForMixAll_䏿–‡");
+ out.close();
+ String string = MixAll.file2String(fileName);
+ assertThat(string).isEqualTo("TestForMixAll_䏿–‡");
+ file.delete();
+ }
+
@Test
public void testString2File() throws IOException {
String fileName = System.getProperty("java.io.tmpdir") + File.separator + "MixAllTest" + System.currentTimeMillis();
From ac83283022fd3909ca270c4f09725b6d94677c99 Mon Sep 17 00:00:00 2001
From: dongeforever
Date: Thu, 25 May 2017 14:26:37 +0800
Subject: [PATCH 46/74] [ROCKETMQ-161] Update runbroker.sh and runserver.sh to
support user defined jvm mem flag closes apache/incubator-rocketmq#87
---
distribution/bin/runbroker.sh | 1 +
distribution/bin/runserver.sh | 1 +
2 files changed, 2 insertions(+)
diff --git a/distribution/bin/runbroker.sh b/distribution/bin/runbroker.sh
index 3405d3926c..b0c490e966 100644
--- a/distribution/bin/runbroker.sh
+++ b/distribution/bin/runbroker.sh
@@ -46,6 +46,7 @@ JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=15g"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking"
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${BASE_DIR}/lib"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
+JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"
numactl --interleave=all pwd > /dev/null 2>&1
diff --git a/distribution/bin/runserver.sh b/distribution/bin/runserver.sh
index e85991c6fa..7c41b0c278 100644
--- a/distribution/bin/runserver.sh
+++ b/distribution/bin/runserver.sh
@@ -43,6 +43,7 @@ JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${BASE_DIR}/lib"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
+JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"
$JAVA ${JAVA_OPT} $@
From 6755cad7b4926bd7608576f87e0aab2bf4d6124f Mon Sep 17 00:00:00 2001
From: wu-sheng
Date: Thu, 25 May 2017 19:43:21 +0800
Subject: [PATCH 47/74] [ROCKETMQ-39] avoid duplicated codes closes
apache/incubator-rocketmq#34
---
.../rocketmq/filtersrv/FiltersrvStartup.java | 24 ++-----
.../rocketmq/namesrv/NamesrvStartup.java | 31 +++------
.../rocketmq/srvutil/ShutdownHookThread.java | 69 +++++++++++++++++++
3 files changed, 85 insertions(+), 39 deletions(-)
create mode 100644 srvutil/src/main/java/org/apache/rocketmq/srvutil/ShutdownHookThread.java
diff --git a/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/FiltersrvStartup.java b/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/FiltersrvStartup.java
index 5dd15dd532..41169c9a50 100644
--- a/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/FiltersrvStartup.java
+++ b/filtersrv/src/main/java/org/apache/rocketmq/filtersrv/FiltersrvStartup.java
@@ -22,7 +22,7 @@ import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.Callable;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
@@ -34,6 +34,7 @@ import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.remoting.netty.NettySystemConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.srvutil.ServerUtil;
+import org.apache.rocketmq.srvutil.ShutdownHookThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -135,24 +136,13 @@ public class FiltersrvStartup {
System.exit(-3);
}
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- private volatile boolean hasShutdown = false;
- private AtomicInteger shutdownTimes = new AtomicInteger(0);
-
+ Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable() {
@Override
- public void run() {
- synchronized (this) {
- log.info("shutdown hook was invoked, " + this.shutdownTimes.incrementAndGet());
- if (!this.hasShutdown) {
- this.hasShutdown = true;
- long begineTime = System.currentTimeMillis();
- controller.shutdown();
- long consumingTimeTotal = System.currentTimeMillis() - begineTime;
- log.info("shutdown hook over, consuming time total(ms): " + consumingTimeTotal);
- }
- }
+ public Void call() throws Exception {
+ controller.shutdown();
+ return null;
}
- }, "ShutdownHook"));
+ }));
return controller;
} catch (Throwable e) {
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
index 4fa97ad0a4..f49d2b31ff 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
@@ -22,7 +22,7 @@ import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.Callable;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
@@ -35,6 +35,7 @@ import org.apache.rocketmq.remoting.netty.NettyServerConfig;
import org.apache.rocketmq.remoting.netty.NettySystemConfig;
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
import org.apache.rocketmq.srvutil.ServerUtil;
+import org.apache.rocketmq.srvutil.ShutdownHookThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,9 +62,7 @@ public class NamesrvStartup {
//PackageConflictDetect.detectFastjson();
Options options = ServerUtil.buildCommandlineOptions(new Options());
- commandLine =
- ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options),
- new PosixParser());
+ commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
System.exit(-1);
return null;
@@ -97,8 +96,7 @@ public class NamesrvStartup {
MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);
if (null == namesrvConfig.getRocketmqHome()) {
- System.out.printf("Please set the " + MixAll.ROCKETMQ_HOME_ENV
- + " variable in your environment to match the location of the RocketMQ installation%n");
+ System.out.printf("Please set the " + MixAll.ROCKETMQ_HOME_ENV + " variable in your environment to match the location of the RocketMQ installation%n");
System.exit(-2);
}
@@ -123,24 +121,13 @@ public class NamesrvStartup {
System.exit(-3);
}
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- private volatile boolean hasShutdown = false;
- private AtomicInteger shutdownTimes = new AtomicInteger(0);
-
+ Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable() {
@Override
- public void run() {
- synchronized (this) {
- log.info("shutdown hook was invoked, " + this.shutdownTimes.incrementAndGet());
- if (!this.hasShutdown) {
- this.hasShutdown = true;
- long begineTime = System.currentTimeMillis();
- controller.shutdown();
- long consumingTimeTotal = System.currentTimeMillis() - begineTime;
- log.info("shutdown hook over, consuming time total(ms): " + consumingTimeTotal);
- }
- }
+ public Void call() throws Exception {
+ controller.shutdown();
+ return null;
}
- }, "ShutdownHook"));
+ }));
controller.start();
diff --git a/srvutil/src/main/java/org/apache/rocketmq/srvutil/ShutdownHookThread.java b/srvutil/src/main/java/org/apache/rocketmq/srvutil/ShutdownHookThread.java
new file mode 100644
index 0000000000..11f9b2c336
--- /dev/null
+++ b/srvutil/src/main/java/org/apache/rocketmq/srvutil/ShutdownHookThread.java
@@ -0,0 +1,69 @@
+/*
+ * 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.srvutil;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.slf4j.Logger;
+
+/**
+ * {@link ShutdownHookThread} is the standard hook for filtersrv and namesrv modules.
+ * Through {@link Callable} interface, this hook can customization operations in anywhere.
+ */
+public class ShutdownHookThread extends Thread {
+ private volatile boolean hasShutdown = false;
+ private AtomicInteger shutdownTimes = new AtomicInteger(0);
+ private final Logger log;
+ private final Callable callback;
+
+ /**
+ * Create the standard hook thread, with a call back, by using {@link Callable} interface.
+ *
+ * @param log The log instance is used in hook thread.
+ * @param callback The call back function.
+ */
+ public ShutdownHookThread(Logger log, Callable callback) {
+ super("ShutdownHook");
+ this.log = log;
+ this.callback = callback;
+ }
+
+ /**
+ * Thread run method.
+ * Invoke when the jvm shutdown.
+ * 1. count the invocation times.
+ * 2. execute the {@link ShutdownHookThread#callback}, and time it.
+ */
+ @Override
+ public void run() {
+ synchronized (this) {
+ log.info("shutdown hook was invoked, " + this.shutdownTimes.incrementAndGet() + " times.");
+ if (!this.hasShutdown) {
+ this.hasShutdown = true;
+ long beginTime = System.currentTimeMillis();
+ try {
+ this.callback.call();
+ } catch (Exception e) {
+ log.error("shutdown hook callback invoked failure.", e);
+ }
+ long consumingTimeTotal = System.currentTimeMillis() - beginTime;
+ log.info("shutdown hook done, consuming time total(ms): " + consumingTimeTotal);
+ }
+ }
+ }
+}
From 36e675df4fafedd936eb89eaba6612e8117bc919 Mon Sep 17 00:00:00 2001
From: Li Zhanhui
Date: Fri, 26 May 2017 14:42:23 +0800
Subject: [PATCH 48/74] [ROCKETMQ-178] Fix -p -m options closes
apache/incubator-rocketmq#93
---
.../apache/rocketmq/broker/BrokerStartup.java | 32 ++++++++++---------
.../rocketmq/common/constant/LoggerName.java | 1 +
distribution/conf/logback_broker.xml | 5 +++
3 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
index 98ff136574..85d2e3af51 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
@@ -103,20 +103,6 @@ public class BrokerStartup {
messageStoreConfig.setAccessMessageInMemoryMaxRatio(ratio);
}
- if (commandLine.hasOption('p')) {
- MixAll.printObjectProperties(null, brokerConfig);
- MixAll.printObjectProperties(null, nettyServerConfig);
- MixAll.printObjectProperties(null, nettyClientConfig);
- MixAll.printObjectProperties(null, messageStoreConfig);
- System.exit(0);
- } else if (commandLine.hasOption('m')) {
- MixAll.printObjectProperties(null, brokerConfig, true);
- MixAll.printObjectProperties(null, nettyServerConfig, true);
- MixAll.printObjectProperties(null, nettyClientConfig, true);
- MixAll.printObjectProperties(null, messageStoreConfig, true);
- System.exit(0);
- }
-
if (commandLine.hasOption('c')) {
String file = commandLine.getOptionValue('c');
if (file != null) {
@@ -181,8 +167,24 @@ public class BrokerStartup {
configurator.setContext(lc);
lc.reset();
configurator.doConfigure(brokerConfig.getRocketmqHome() + "/conf/logback_broker.xml");
- log = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+ if (commandLine.hasOption('p')) {
+ Logger console = LoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
+ MixAll.printObjectProperties(console, brokerConfig);
+ MixAll.printObjectProperties(console, nettyServerConfig);
+ MixAll.printObjectProperties(console, nettyClientConfig);
+ MixAll.printObjectProperties(console, messageStoreConfig);
+ System.exit(0);
+ } else if (commandLine.hasOption('m')) {
+ Logger console = LoggerFactory.getLogger(LoggerName.BROKER_CONSOLE_NAME);
+ MixAll.printObjectProperties(console, brokerConfig, true);
+ MixAll.printObjectProperties(console, nettyServerConfig, true);
+ MixAll.printObjectProperties(console, nettyClientConfig, true);
+ MixAll.printObjectProperties(console, messageStoreConfig, true);
+ System.exit(0);
+ }
+
+ log = LoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
MixAll.printObjectProperties(log, brokerConfig);
MixAll.printObjectProperties(log, nettyServerConfig);
MixAll.printObjectProperties(log, nettyClientConfig);
diff --git a/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java b/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java
index 385c121434..12070ddc34 100644
--- a/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java
+++ b/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java
@@ -20,6 +20,7 @@ public class LoggerName {
public static final String FILTERSRV_LOGGER_NAME = "RocketmqFiltersrv";
public static final String NAMESRV_LOGGER_NAME = "RocketmqNamesrv";
public static final String BROKER_LOGGER_NAME = "RocketmqBroker";
+ public static final String BROKER_CONSOLE_NAME = "RocketmqConsole";
public static final String CLIENT_LOGGER_NAME = "RocketmqClient";
public static final String TOOLS_LOGGER_NAME = "RocketmqTools";
public static final String COMMON_LOGGER_NAME = "RocketmqCommon";
diff --git a/distribution/conf/logback_broker.xml b/distribution/conf/logback_broker.xml
index dd5c63f5de..3945fac388 100644
--- a/distribution/conf/logback_broker.xml
+++ b/distribution/conf/logback_broker.xml
@@ -349,6 +349,11 @@
+
+
+
+
+
From 547004c0968fb5280e01c391e91851218a9da2bc Mon Sep 17 00:00:00 2001
From: vsair
Date: Fri, 26 May 2017 15:13:29 +0800
Subject: [PATCH 49/74] =?UTF-8?q?[ROCKETMQ-175]=20Consumer=20may=20miss=20?=
=?UTF-8?q?messages=20because=20of=20inconsistent=20sub=E2=80=A6=20=20clos?=
=?UTF-8?q?es=20apache/incubator-rocketmq#92?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../client/impl/consumer/RebalancePushImpl.java | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java
index 1730c99276..509c9a4628 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java
@@ -30,6 +30,7 @@ import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
public class RebalancePushImpl extends RebalanceImpl {
private final static long UNLOCK_DELAY_TIME_MILLS = Long.parseLong(System.getProperty("rocketmq.client.unlockDelayTimeMills", "20000"));
@@ -47,6 +48,16 @@ public class RebalancePushImpl extends RebalanceImpl {
@Override
public void messageQueueChanged(String topic, Set mqAll, Set mqDivided) {
+ /**
+ * When rebalance result changed, should update subscription's version to notify broker.
+ * Fix: inconsistency subscription may lead to consumer miss messages.
+ */
+ SubscriptionData subscriptionData = this.subscriptionInner.get(topic);
+ long newVersion = System.currentTimeMillis();
+ log.info("{} Rebalance changed, also update version: {}, {}", topic, subscriptionData.getSubVersion(), newVersion);
+ subscriptionData.setSubVersion(newVersion);
+ // notify broker
+ this.getmQClientFactory().sendHeartbeatToAllBrokerWithLock();
}
@Override
From 3480aaed6467928728075f6a7e8e9d552c137ad8 Mon Sep 17 00:00:00 2001
From: lindzh
Date: Fri, 26 May 2017 15:28:28 +0800
Subject: [PATCH 50/74] [ROCKETMQ-189] Misleading tip on consumeTimestamp and
wrong consumeTimestamp exception message closes apache/incubator-rocketmq#97
---
.../client/impl/consumer/DefaultMQPushConsumerImpl.java | 8 ++++----
.../rocketmq/client/impl/consumer/RebalancePushImpl.java | 2 +-
.../src/main/java/org/apache/rocketmq/common/UtilAll.java | 2 +-
.../org/apache/rocketmq/example/simple/PushConsumer.java | 2 ++
4 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
index 2cafe29a6f..8767964244 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java
@@ -655,12 +655,12 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner {
null);
}
- Date dt = UtilAll.parseDate(this.defaultMQPushConsumer.getConsumeTimestamp(), UtilAll.YYYY_MMDD_HHMMSS);
+ Date dt = UtilAll.parseDate(this.defaultMQPushConsumer.getConsumeTimestamp(), UtilAll.YYYYMMDDHHMMSS);
if (null == dt) {
throw new MQClientException(
- "consumeTimestamp is invalid, YYYY_MMDD_HHMMSS"
- + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL),
- null);
+ "consumeTimestamp is invalid, the valid format is yyyyMMddHHmmss,but received "
+ + this.defaultMQPushConsumer.getConsumeTimestamp()
+ + " " + FAQUrl.suggestTodo(FAQUrl.CLIENT_PARAMETER_CHECK_URL), null);
}
// allocateMessageQueueStrategy
diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java
index 509c9a4628..112bceeb81 100644
--- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java
+++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java
@@ -171,7 +171,7 @@ public class RebalancePushImpl extends RebalanceImpl {
} else {
try {
long timestamp = UtilAll.parseDate(this.defaultMQPushConsumerImpl.getDefaultMQPushConsumer().getConsumeTimestamp(),
- UtilAll.YYYY_MMDD_HHMMSS).getTime();
+ UtilAll.YYYYMMDDHHMMSS).getTime();
result = this.mQClientFactory.getMQAdminImpl().searchOffset(mq, timestamp);
} catch (MQClientException e) {
result = -1;
diff --git a/common/src/main/java/org/apache/rocketmq/common/UtilAll.java b/common/src/main/java/org/apache/rocketmq/common/UtilAll.java
index 016da0b44c..e9d926fedf 100644
--- a/common/src/main/java/org/apache/rocketmq/common/UtilAll.java
+++ b/common/src/main/java/org/apache/rocketmq/common/UtilAll.java
@@ -41,7 +41,7 @@ import org.apache.rocketmq.remoting.common.RemotingHelper;
public class UtilAll {
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd#HH:mm:ss:SSS";
- public static final String YYYY_MMDD_HHMMSS = "yyyyMMddHHmmss";
+ public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static int getPid() {
diff --git a/example/src/main/java/org/apache/rocketmq/example/simple/PushConsumer.java b/example/src/main/java/org/apache/rocketmq/example/simple/PushConsumer.java
index d03c2c5aff..c8252d03ca 100644
--- a/example/src/main/java/org/apache/rocketmq/example/simple/PushConsumer.java
+++ b/example/src/main/java/org/apache/rocketmq/example/simple/PushConsumer.java
@@ -31,6 +31,8 @@ public class PushConsumer {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1");
consumer.subscribe("Jodie_topic_1023", "*");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
+ //wrong time format 2017_0422_221800
+ consumer.setConsumeTimestamp("20170422221800");
consumer.registerMessageListener(new MessageListenerConcurrently() {
/**
From 3eea44a714e6a488167b775c4fc612df44af6340 Mon Sep 17 00:00:00 2001
From: lindzh
Date: Fri, 26 May 2017 16:12:16 +0800
Subject: [PATCH 51/74] [ROCKETMQ-194] log appender support closes
apache/incubator-rocketmq#101
---
logappender/pom.xml | 86 +++++++
.../logappender/common/ProducerInstance.java | 93 +++++++
.../log4j/RocketmqLog4jAppender.java | 194 +++++++++++++++
.../log4j2/RocketmqLog4j2Appender.java | 233 ++++++++++++++++++
.../logback/RocketmqLogbackAppender.java | 183 ++++++++++++++
.../logappender/AbstractTestCase.java | 154 ++++++++++++
.../logappender/Log4jPropertiesTest.java | 32 +++
.../rocketmq/logappender/Log4jTest.java | 43 ++++
.../rocketmq/logappender/Log4jXmlTest.java | 32 +++
.../rocketmq/logappender/LogbackTest.java | 54 ++++
.../rocketmq/logappender/log4j2Test.java | 44 ++++
.../test/resources/log4j-example.properties | 38 +++
.../src/test/resources/log4j-example.xml | 62 +++++
.../src/test/resources/log4j2-example.xml | 41 +++
.../src/test/resources/logback-example.xml | 89 +++++++
pom.xml | 11 +
style/rmq_checkstyle.xml | 6 +
17 files changed, 1395 insertions(+)
create mode 100644 logappender/pom.xml
create mode 100644 logappender/src/main/java/org/apache/rocketmq/logappender/common/ProducerInstance.java
create mode 100644 logappender/src/main/java/org/apache/rocketmq/logappender/log4j/RocketmqLog4jAppender.java
create mode 100644 logappender/src/main/java/org/apache/rocketmq/logappender/log4j2/RocketmqLog4j2Appender.java
create mode 100644 logappender/src/main/java/org/apache/rocketmq/logappender/logback/RocketmqLogbackAppender.java
create mode 100644 logappender/src/test/java/org/apache/rocketmq/logappender/AbstractTestCase.java
create mode 100644 logappender/src/test/java/org/apache/rocketmq/logappender/Log4jPropertiesTest.java
create mode 100644 logappender/src/test/java/org/apache/rocketmq/logappender/Log4jTest.java
create mode 100644 logappender/src/test/java/org/apache/rocketmq/logappender/Log4jXmlTest.java
create mode 100644 logappender/src/test/java/org/apache/rocketmq/logappender/LogbackTest.java
create mode 100644 logappender/src/test/java/org/apache/rocketmq/logappender/log4j2Test.java
create mode 100644 logappender/src/test/resources/log4j-example.properties
create mode 100644 logappender/src/test/resources/log4j-example.xml
create mode 100644 logappender/src/test/resources/log4j2-example.xml
create mode 100644 logappender/src/test/resources/logback-example.xml
diff --git a/logappender/pom.xml b/logappender/pom.xml
new file mode 100644
index 0000000000..5974c75332
--- /dev/null
+++ b/logappender/pom.xml
@@ -0,0 +1,86 @@
+
+
+
+
+ org.apache.rocketmq
+ rocketmq-all
+ 4.1.0-incubating-SNAPSHOT
+
+ 4.0.0
+ rocketmq-logappender
+ jar
+ rocketmq-logappender ${project.version}
+
+
+
+ org.slf4j
+ slf4j-api
+ true
+
+
+ log4j
+ log4j
+ true
+
+
+ org.apache.logging.log4j
+ log4j-core
+ true
+
+
+ ch.qos.logback
+ logback-core
+ true
+
+
+ ch.qos.logback
+ logback-classic
+ true
+
+
+ ${project.groupId}
+ rocketmq-client
+
+
+ ${project.groupId}
+ rocketmq-namesrv
+ test
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+ ${project.groupId}
+ rocketmq-broker
+ test
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+
+
+
+
diff --git a/logappender/src/main/java/org/apache/rocketmq/logappender/common/ProducerInstance.java b/logappender/src/main/java/org/apache/rocketmq/logappender/common/ProducerInstance.java
new file mode 100644
index 0000000000..669e30c01e
--- /dev/null
+++ b/logappender/src/main/java/org/apache/rocketmq/logappender/common/ProducerInstance.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.logappender.common;
+
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.client.producer.MQProducer;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Common Producer component
+ */
+public class ProducerInstance {
+
+ public static final String APPENDER_TYPE = "APPENDER_TYPE";
+
+ public static final String LOG4J_APPENDER = "LOG4J_APPENDER";
+
+ public static final String LOG4J2_APPENDER = "LOG4J2_APPENDER";
+
+ public static final String LOGBACK_APPENDER = "LOGBACK_APPENDER";
+
+ public static final String DEFAULT_GROUP = "rocketmq_appender";
+
+ private static ConcurrentHashMap producerMap = new ConcurrentHashMap();
+
+ private static String genKey(String nameServerAddress, String group) {
+ return nameServerAddress + "_" + group;
+ }
+
+
+ public static MQProducer getInstance(String nameServerAddress, String group) throws MQClientException {
+ if (group == null) {
+ group = DEFAULT_GROUP;
+ }
+
+ String genKey = genKey(nameServerAddress, group);
+ MQProducer p = producerMap.get(genKey);
+ if (p != null) {
+ return p;
+ }
+
+ DefaultMQProducer defaultMQProducer = new DefaultMQProducer(group);
+ defaultMQProducer.setNamesrvAddr(nameServerAddress);
+ MQProducer beforeProducer = null;
+ //cas put producer
+ beforeProducer = producerMap.putIfAbsent(genKey, defaultMQProducer);
+ if (beforeProducer != null) {
+ return beforeProducer;
+ }
+ defaultMQProducer.start();
+ return defaultMQProducer;
+ }
+
+
+ public static void removeAndClose(String nameServerAddress, String group) {
+ if (group == null) {
+ group = DEFAULT_GROUP;
+ }
+ String genKey = genKey(nameServerAddress, group);
+ MQProducer producer = producerMap.remove(genKey);
+
+ if (producer != null) {
+ producer.shutdown();
+ }
+ }
+
+ public static void closeAll() {
+ Set> entries = producerMap.entrySet();
+ for (Map.Entry entry : entries) {
+ producerMap.remove(entry.getKey());
+ entry.getValue().shutdown();
+ }
+ }
+
+}
diff --git a/logappender/src/main/java/org/apache/rocketmq/logappender/log4j/RocketmqLog4jAppender.java b/logappender/src/main/java/org/apache/rocketmq/logappender/log4j/RocketmqLog4jAppender.java
new file mode 100644
index 0000000000..b2983b6c1c
--- /dev/null
+++ b/logappender/src/main/java/org/apache/rocketmq/logappender/log4j/RocketmqLog4jAppender.java
@@ -0,0 +1,194 @@
+/*
+ * 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.logappender.log4j;
+
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.logappender.common.ProducerInstance;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.rocketmq.client.producer.MQProducer;
+
+/**
+ * Log4j Appender Component
+ */
+public class RocketmqLog4jAppender extends AppenderSkeleton {
+
+ /**
+ * Appended message tag define
+ */
+ private String tag;
+
+ /**
+ * Whitch topic to send log messages
+ */
+ private String topic;
+
+ private boolean locationInfo;
+
+ /**
+ * Log producer send instance
+ */
+ private MQProducer producer;
+
+ /**
+ * RocketMQ nameserver address
+ */
+ private String nameServerAddress;
+
+ /**
+ * Log producer group
+ */
+ private String producerGroup;
+
+ public RocketmqLog4jAppender() {
+ }
+
+
+ public void activateOptions() {
+ LogLog.debug("Getting initial context.");
+ if (!checkEntryConditions()) {
+ return;
+ }
+ try {
+ producer = ProducerInstance.getInstance(nameServerAddress, producerGroup);
+ } catch (Exception e) {
+ LogLog.error("activateOptions nameserver:" + nameServerAddress + " group:" + producerGroup + " " + e.getMessage());
+ }
+ }
+
+
+ /**
+ * Info,error,warn,callback method implementation
+ *
+ * @param event
+ */
+ public void append(LoggingEvent event) {
+ if (null == producer) {
+ return;
+ }
+ if (locationInfo) {
+ event.getLocationInformation();
+ }
+ byte[] data = this.layout.format(event).getBytes();
+ try {
+ Message msg = new Message(topic, tag, data);
+ msg.getProperties().put(ProducerInstance.APPENDER_TYPE, ProducerInstance.LOG4J_APPENDER);
+
+ //Send message and do not wait for the ack from the message broker.
+ producer.sendOneway(msg);
+ } catch (Exception e) {
+ String msg = new String(data);
+ errorHandler.error("Could not send message in RocketmqLog4jAppender [" + name + "].Message is :" + msg, e,
+ ErrorCode.GENERIC_FAILURE);
+ }
+ }
+
+ protected boolean checkEntryConditions() {
+ String fail = null;
+
+ if (this.topic == null) {
+ fail = "No topic";
+ } else if (this.tag == null) {
+ fail = "No tag";
+ }
+
+ if (fail != null) {
+ errorHandler.error(fail + " for RocketmqLog4jAppender named [" + name + "].");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * When system exit,this method will be called to close resources
+ */
+ public synchronized void close() {
+ // The synchronized modifier avoids concurrent append and close operations
+
+ if (this.closed)
+ return;
+
+ LogLog.debug("Closing RocketmqLog4jAppender [" + name + "].");
+ this.closed = true;
+
+ try {
+ ProducerInstance.removeAndClose(this.nameServerAddress, this.producerGroup);
+ } catch (Exception e) {
+ LogLog.error("Closing RocketmqLog4jAppender [" + name + "] nameServerAddress:" + nameServerAddress + " group:" + producerGroup + " " + e.getMessage());
+ }
+ // Help garbage collection
+ producer = null;
+ }
+
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+
+ public void setTopic(String topic) {
+ this.topic = topic;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public void setTag(String tag) {
+ this.tag = tag;
+ }
+
+ /**
+ * Returns value of the LocationInfo property which
+ * determines whether location (stack) info is sent to the remote
+ * subscriber.
+ */
+ public boolean isLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ * If true, the information sent to the remote subscriber will
+ * include caller's location information. By default no location
+ * information is sent to the subscriber.
+ */
+ public void setLocationInfo(boolean locationInfo) {
+ this.locationInfo = locationInfo;
+ }
+
+ /**
+ * Returns the message producer,Only valid after
+ * activateOptions() method has been invoked.
+ */
+ protected MQProducer getProducer() {
+ return producer;
+ }
+
+ public void setNameServerAddress(String nameServerAddress) {
+ this.nameServerAddress = nameServerAddress;
+ }
+
+ public void setProducerGroup(String producerGroup) {
+ this.producerGroup = producerGroup;
+ }
+}
diff --git a/logappender/src/main/java/org/apache/rocketmq/logappender/log4j2/RocketmqLog4j2Appender.java b/logappender/src/main/java/org/apache/rocketmq/logappender/log4j2/RocketmqLog4j2Appender.java
new file mode 100644
index 0000000000..fb8341f167
--- /dev/null
+++ b/logappender/src/main/java/org/apache/rocketmq/logappender/log4j2/RocketmqLog4j2Appender.java
@@ -0,0 +1,233 @@
+/*
+ * 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.logappender.log4j2;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.ErrorHandler;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.logappender.common.ProducerInstance;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.rocketmq.client.producer.MQProducer;
+
+import java.io.Serializable;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Log4j2 Appender Component
+ */
+@Plugin(name = "RocketMQ",
+ category = Node.CATEGORY,
+ elementType = Appender.ELEMENT_TYPE,
+ printObject = true)
+public class RocketmqLog4j2Appender extends AbstractAppender {
+
+ /**
+ * RocketMQ nameserver address
+ */
+ private String nameServerAddress;
+
+ /**
+ * Log producer group
+ */
+ private String producerGroup;
+
+ /**
+ * Log producer send instance
+ */
+ private MQProducer producer;
+
+ /**
+ * Appended message tag define
+ */
+ private String tag;
+
+ /**
+ * Whitch topic to send log messages
+ */
+ private String topic;
+
+
+ protected RocketmqLog4j2Appender(String name, Filter filter, Layout extends Serializable> layout,
+ boolean ignoreExceptions, String nameServerAddress, String producerGroup,
+ String topic, String tag) {
+ super(name, filter, layout, ignoreExceptions);
+ this.producer = producer;
+ this.topic = topic;
+ this.tag = tag;
+ this.nameServerAddress = nameServerAddress;
+ this.producerGroup = producerGroup;
+ try {
+ this.producer = ProducerInstance.getInstance(this.nameServerAddress, this.producerGroup);
+ } catch (Exception e) {
+ ErrorHandler handler = this.getHandler();
+ if (handler != null) {
+ handler.error("Starting RocketmqLog4j2Appender [" + this.getName()
+ + "] nameServerAddress:" + nameServerAddress + " group:" + producerGroup + " " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Info,error,warn,callback method implementation
+ *
+ * @param event
+ */
+ public void append(LogEvent event) {
+ if (null == producer) {
+ return;
+ }
+ byte[] data = this.getLayout().toByteArray(event);
+ try {
+ Message msg = new Message(topic, tag, data);
+ msg.getProperties().put(ProducerInstance.APPENDER_TYPE, ProducerInstance.LOG4J2_APPENDER);
+
+ //Send message and do not wait for the ack from the message broker.
+ producer.sendOneway(msg);
+ } catch (Exception e) {
+ ErrorHandler handler = this.getHandler();
+ if (handler != null) {
+ String msg = new String(data);
+ handler.error("Could not send message in RocketmqLog4j2Appender [" + this.getName() + "].Message is : " + msg, e);
+ }
+
+ }
+ }
+
+ /**
+ * When system exit,this method will be called to close resources
+ *
+ * @param timeout
+ * @param timeUnit
+ * @return
+ */
+ public boolean stop(long timeout, TimeUnit timeUnit) {
+ this.setStopping();
+ try {
+ ProducerInstance.removeAndClose(this.nameServerAddress, this.producerGroup);
+ } catch (Exception e) {
+ ErrorHandler handler = this.getHandler();
+ if (handler != null) {
+ handler.error("Closeing RocketmqLog4j2Appender [" + this.getName()
+ + "] nameServerAddress:" + nameServerAddress + " group:" + producerGroup + " " + e.getMessage());
+ }
+ }
+
+ boolean stopped = super.stop(timeout, timeUnit, false);
+ this.setStopped();
+ return stopped;
+ }
+
+ /**
+ * Log4j2 builder creator
+ */
+ @PluginBuilderFactory
+ public static RocketmqLog4j2Appender.Builder newBuilder() {
+ return new RocketmqLog4j2Appender.Builder();
+ }
+
+ /**
+ * Log4j2 xml builder define
+ */
+ public static class Builder implements org.apache.logging.log4j.core.util.Builder {
+
+ @PluginBuilderAttribute
+ @Required(message = "A name for the RocketmqLog4j2Appender must be specified")
+ private String name;
+
+ @PluginElement("Layout")
+ private Layout extends Serializable> layout;
+
+ @PluginElement("Filter")
+ private Filter filter;
+
+ @PluginBuilderAttribute
+ private boolean ignoreExceptions;
+
+ @PluginBuilderAttribute
+ private String tag;
+
+ @PluginBuilderAttribute
+ private String nameServerAddress;
+
+ @PluginBuilderAttribute
+ private String producerGroup;
+
+ @PluginBuilderAttribute
+ @Required(message = "A topic name must be specified")
+ private String topic;
+
+ private Builder() {
+ this.layout = SerializedLayout.createLayout();
+ this.ignoreExceptions = true;
+ }
+
+ public RocketmqLog4j2Appender.Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setLayout(Layout extends Serializable> layout) {
+ this.layout = layout;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setFilter(Filter filter) {
+ this.filter = filter;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setIgnoreExceptions(boolean ignoreExceptions) {
+ this.ignoreExceptions = ignoreExceptions;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setTag(final String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setTopic(final String topic) {
+ this.topic = topic;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setNameServerAddress(String nameServerAddress) {
+ this.nameServerAddress = nameServerAddress;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender.Builder setProducerGroup(String producerGroup) {
+ this.producerGroup = producerGroup;
+ return this;
+ }
+
+ public RocketmqLog4j2Appender build() {
+ return new RocketmqLog4j2Appender(name, filter, layout, ignoreExceptions,
+ nameServerAddress, producerGroup, topic, tag);
+ }
+ }
+}
diff --git a/logappender/src/main/java/org/apache/rocketmq/logappender/logback/RocketmqLogbackAppender.java b/logappender/src/main/java/org/apache/rocketmq/logappender/logback/RocketmqLogbackAppender.java
new file mode 100644
index 0000000000..cb45522853
--- /dev/null
+++ b/logappender/src/main/java/org/apache/rocketmq/logappender/logback/RocketmqLogbackAppender.java
@@ -0,0 +1,183 @@
+/*
+ * 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.logappender.logback;
+
+import ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.spi.PreSerializationTransformer;
+import ch.qos.logback.core.status.ErrorStatus;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.logappender.common.ProducerInstance;
+import org.apache.rocketmq.client.producer.MQProducer;
+
+/**
+ * Logback Appender Component
+ */
+public class RocketmqLogbackAppender extends AppenderBase {
+
+ /**
+ * Message tag define
+ */
+ private String tag;
+
+ /**
+ * Whitch topic to send log messages
+ */
+ private String topic;
+
+ /**
+ * RocketMQ nameserver address
+ */
+ private String nameServerAddress;
+
+ /**
+ * Log producer group
+ */
+ private String producerGroup;
+
+ /**
+ * Log producer send instance
+ */
+ private MQProducer producer;
+
+ private Layout layout;
+
+ private PreSerializationTransformer pst = new LoggingEventPreSerializationTransformer();
+
+ /**
+ * Info,error,warn,callback method implementation
+ *
+ * @param event
+ */
+ @Override
+ protected void append(ILoggingEvent event) {
+ if (!isStarted()) {
+ return;
+ }
+ String logStr = this.layout.doLayout(event);
+ try {
+ Message msg = new Message(topic, tag, logStr.getBytes());
+ msg.getProperties().put(ProducerInstance.APPENDER_TYPE, ProducerInstance.LOGBACK_APPENDER);
+
+ //Send message and do not wait for the ack from the message broker.
+ producer.sendOneway(msg);
+ } catch (Exception e) {
+ addError("Could not send message in RocketmqLogbackAppender [" + name + "]. Message is : " + logStr, e);
+ }
+ }
+
+ /**
+ * Options are activated and become effective only after calling this method.
+ */
+ public void start() {
+ int errors = 0;
+
+ if (this.layout == null) {
+ addStatus(new ErrorStatus("No layout set for the RocketmqLogbackAppender named \"" + name + "\".", this));
+ errors++;
+ }
+
+ if (errors > 0 || !checkEntryConditions()) {
+ return;
+ }
+ try {
+ producer = ProducerInstance.getInstance(nameServerAddress, producerGroup);
+ } catch (Exception e) {
+ addError("Starting RocketmqLogbackAppender [" + this.getName()
+ + "] nameServerAddress:" + nameServerAddress + " group:" + producerGroup + " " + e.getMessage());
+ }
+ if (producer != null) {
+ super.start();
+ }
+ }
+
+ /**
+ * When system exit,this method will be called to close resources
+ */
+ public synchronized void stop() {
+ // The synchronized modifier avoids concurrent append and close operations
+ if (!this.started) {
+ return;
+ }
+
+ this.started = false;
+
+ try {
+ ProducerInstance.removeAndClose(this.nameServerAddress, this.producerGroup);
+ } catch (Exception e) {
+ addError("Closeing RocketmqLogbackAppender [" + this.getName()
+ + "] nameServerAddress:" + nameServerAddress + " group:" + producerGroup + " " + e.getMessage());
+ }
+
+ // Help garbage collection
+ producer = null;
+ }
+
+ protected boolean checkEntryConditions() {
+ String fail = null;
+
+ if (this.topic == null) {
+ fail = "No topic";
+ }
+
+ if (fail != null) {
+ addError(fail + " for RocketmqLogbackAppender named [" + name + "].");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+ public Layout getLayout() {
+ return this.layout;
+ }
+
+ /**
+ * Set the pattern layout to format the log.
+ */
+ public void setLayout(Layout layout) {
+ this.layout = layout;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+
+ public void setTag(String tag) {
+ this.tag = tag;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public void setTopic(String topic) {
+ this.topic = topic;
+ }
+
+ public void setNameServerAddress(String nameServerAddress) {
+ this.nameServerAddress = nameServerAddress;
+ }
+
+ public void setProducerGroup(String producerGroup) {
+ this.producerGroup = producerGroup;
+ }
+}
diff --git a/logappender/src/test/java/org/apache/rocketmq/logappender/AbstractTestCase.java b/logappender/src/test/java/org/apache/rocketmq/logappender/AbstractTestCase.java
new file mode 100644
index 0000000000..d3e2f8aedc
--- /dev/null
+++ b/logappender/src/test/java/org/apache/rocketmq/logappender/AbstractTestCase.java
@@ -0,0 +1,154 @@
+/*
+ * 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.logappender;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
+import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
+import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MQVersion;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.namesrv.NamesrvConfig;
+import org.apache.rocketmq.logappender.common.ProducerInstance;
+import org.apache.rocketmq.namesrv.NamesrvController;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Basic test rocketmq broker and name server init
+ */
+public class AbstractTestCase {
+
+ private static String nameServer = "localhost:9876";
+
+ private static NamesrvController namesrvController;
+
+ private static BrokerController brokerController;
+
+ private static String topic = "TopicTest";
+
+ @BeforeClass
+ public static void startRocketmqService() throws Exception {
+
+ startNamesrv();
+
+ startBroker();
+ }
+
+ /**
+ * Start rocketmq name server
+ * @throws Exception
+ */
+ private static void startNamesrv() throws Exception {
+
+ NamesrvConfig namesrvConfig = new NamesrvConfig();
+ NettyServerConfig nettyServerConfig = new NettyServerConfig();
+ nettyServerConfig.setListenPort(9876);
+
+ namesrvController = new NamesrvController(namesrvConfig, nettyServerConfig);
+ boolean initResult = namesrvController.initialize();
+ if (!initResult) {
+ namesrvController.shutdown();
+ throw new Exception();
+ }
+ namesrvController.start();
+ }
+
+ /**
+ * Start rocketmq broker service
+ * @throws Exception
+ */
+ private static void startBroker() throws Exception {
+
+ System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));
+
+ BrokerConfig brokerConfig = new BrokerConfig();
+ brokerConfig.setNamesrvAddr(nameServer);
+ brokerConfig.setBrokerId(MixAll.MASTER_ID);
+ NettyServerConfig nettyServerConfig = new NettyServerConfig();
+ nettyServerConfig.setListenPort(10911);
+ NettyClientConfig nettyClientConfig = new NettyClientConfig();
+ MessageStoreConfig messageStoreConfig = new MessageStoreConfig();
+
+ brokerController = new BrokerController(brokerConfig, nettyServerConfig, nettyClientConfig, messageStoreConfig);
+ boolean initResult = brokerController.initialize();
+ if (!initResult) {
+ brokerController.shutdown();
+ throw new Exception();
+ }
+ brokerController.start();
+ }
+
+ @AfterClass
+ public static void stop() {
+ ProducerInstance.closeAll();
+ if (brokerController != null) {
+ brokerController.shutdown();
+ }
+
+ if (namesrvController != null) {
+ namesrvController.shutdown();
+ }
+ }
+
+ protected int consumeMessages(int count,final String key,int timeout) throws MQClientException, InterruptedException {
+
+ final AtomicInteger cc = new AtomicInteger(0);
+ final CountDownLatch countDownLatch = new CountDownLatch(count);
+
+ DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("hello");
+ consumer.setNamesrvAddr(nameServer);
+ consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
+ consumer.subscribe(topic, "*");
+
+ consumer.registerMessageListener(new MessageListenerConcurrently() {
+
+ @Override
+ public ConsumeConcurrentlyStatus consumeMessage(List msgs,
+ ConsumeConcurrentlyContext context) {
+ for (MessageExt msg : msgs) {
+ String body = new String(msg.getBody());
+ if(key==null||body.contains(key)){
+ countDownLatch.countDown();
+ cc.incrementAndGet();
+ continue;
+ }
+ }
+ return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
+ }
+ });
+ consumer.start();
+ countDownLatch.await(timeout, TimeUnit.SECONDS);
+ consumer.shutdown();
+ return cc.get();
+ }
+}
diff --git a/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jPropertiesTest.java b/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jPropertiesTest.java
new file mode 100644
index 0000000000..86752301a1
--- /dev/null
+++ b/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jPropertiesTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.logappender;
+
+import org.apache.log4j.PropertyConfigurator;
+
+public class Log4jPropertiesTest extends Log4jTest {
+
+ @Override
+ public void init() {
+ PropertyConfigurator.configure("src/test/resources/log4j-example.properties");
+ }
+
+ @Override
+ public String getType() {
+ return "properties";
+ }
+}
diff --git a/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jTest.java b/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jTest.java
new file mode 100644
index 0000000000..75f9bf2b60
--- /dev/null
+++ b/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.logappender;
+
+import org.apache.log4j.Logger;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public abstract class Log4jTest extends AbstractTestCase{
+
+ @Before
+ public abstract void init();
+
+ public abstract String getType();
+
+ @Test
+ public void testLog4j() throws InterruptedException, MQClientException {
+ Logger logger = Logger.getLogger("testLogger");
+ for (int i = 0; i < 50; i++) {
+ logger.info("log4j " + this.getType() + " simple test message " + i);
+ }
+ int received = consumeMessages(30, "log4j",30);
+ Assert.assertTrue(received>20);
+ }
+
+}
diff --git a/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jXmlTest.java b/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jXmlTest.java
new file mode 100644
index 0000000000..6743f7c166
--- /dev/null
+++ b/logappender/src/test/java/org/apache/rocketmq/logappender/Log4jXmlTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.logappender;
+
+import org.apache.log4j.xml.DOMConfigurator;
+
+public class Log4jXmlTest extends Log4jTest {
+
+ @Override
+ public void init() {
+ DOMConfigurator.configure("src/test/resources/log4j-example.xml");
+ }
+
+ @Override
+ public String getType() {
+ return "xml";
+ }
+}
diff --git a/logappender/src/test/java/org/apache/rocketmq/logappender/LogbackTest.java b/logappender/src/test/java/org/apache/rocketmq/logappender/LogbackTest.java
new file mode 100644
index 0000000000..15a21a3909
--- /dev/null
+++ b/logappender/src/test/java/org/apache/rocketmq/logappender/LogbackTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.logappender;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.StatusPrinter;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class LogbackTest extends AbstractTestCase{
+
+ @Before
+ public void init() throws JoranException {
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ JoranConfigurator configurator = new JoranConfigurator();
+ configurator.setContext(lc);
+ lc.reset();
+ configurator.doConfigure(new File("src/test/resources/logback-example.xml"));
+ StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
+ }
+
+
+ @Test
+ public void testLogback() throws InterruptedException, MQClientException {
+ Logger logger = LoggerFactory.getLogger("testLogger");
+ for (int i = 0; i < 50; i++) {
+ logger.info("logback test message " + i);
+ }
+ int received = consumeMessages(30, "logback",30);
+ Assert.assertTrue(received>20);
+ }
+}
diff --git a/logappender/src/test/java/org/apache/rocketmq/logappender/log4j2Test.java b/logappender/src/test/java/org/apache/rocketmq/logappender/log4j2Test.java
new file mode 100644
index 0000000000..75ba523f03
--- /dev/null
+++ b/logappender/src/test/java/org/apache/rocketmq/logappender/log4j2Test.java
@@ -0,0 +1,44 @@
+/*
+ * 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.logappender;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class log4j2Test extends AbstractTestCase{
+
+ @Before
+ public void init() {
+ Configurator.initialize("log4j2", "src/test/resources/log4j2-example.xml");
+ }
+
+
+ @Test
+ public void testLog4j2() throws InterruptedException, MQClientException {
+ Logger logger = LogManager.getLogger("test");
+ for (int i = 0; i < 50; i++) {
+ logger.info("log4j2 log message " + i);
+ }
+ int received = consumeMessages(30, "log4j2",30);
+ Assert.assertTrue(received>20);
+ }
+}
diff --git a/logappender/src/test/resources/log4j-example.properties b/logappender/src/test/resources/log4j-example.properties
new file mode 100644
index 0000000000..b4e8114a8b
--- /dev/null
+++ b/logappender/src/test/resources/log4j-example.properties
@@ -0,0 +1,38 @@
+# 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.
+
+log4j.rootLogger=INFO,stdout
+
+log4j.logger.testLogger=INFO,mq
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %-4r [%t] (%F:%L) %-5p - %m%n
+
+log4j.appender.store=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.store.File=${user.home}/logs/rocketmqlogs/appender.log
+log4j.appender.store.Append=true
+log4j.appender.store.DatePattern ='_'yyyy-MM-dd'.log'
+log4j.appender.store.layout=org.apache.log4j.PatternLayout
+log4j.appender.store.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-4r [%t] (%F:%L) %-5p - %m%n
+
+log4j.appender.mq=org.apache.rocketmq.logappender.log4j.RocketmqLog4jAppender
+log4j.appender.mq.Tag=log
+log4j.appender.mq.Topic=TopicTest
+log4j.appender.mq.ProducerGroup=log4jp
+log4j.appender.mq.NameServerAddress=127.0.0.1:9876
+log4j.appender.mq.layout=org.apache.log4j.PatternLayout
+log4j.appender.mq.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-4r [%t] (%F:%L) %-5p - %m%n
\ No newline at end of file
diff --git a/logappender/src/test/resources/log4j-example.xml b/logappender/src/test/resources/log4j-example.xml
new file mode 100644
index 0000000000..e58bcb09ce
--- /dev/null
+++ b/logappender/src/test/resources/log4j-example.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/logappender/src/test/resources/log4j2-example.xml b/logappender/src/test/resources/log4j2-example.xml
new file mode 100644
index 0000000000..358d40ea54
--- /dev/null
+++ b/logappender/src/test/resources/log4j2-example.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/logappender/src/test/resources/logback-example.xml b/logappender/src/test/resources/logback-example.xml
new file mode 100644
index 0000000000..21b5434950
--- /dev/null
+++ b/logappender/src/test/resources/logback-example.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+ ${user.home}/logs/simple/system.log
+ true
+
+ ${user.home}/logs/simple/system.%i.log
+
+ 1
+ 30
+
+
+ 100MB
+
+
+ %date %p %t - %m%n
+ UTF-8
+
+
+
+
+ System.out
+
+ %date %p %t - %m%n
+ UTF-8
+
+
+
+
+ ${user.home}/logs/simple/daily.log
+
+ ${user.home}/logs/simple/daily.log.%d{yyyy-MM-dd_HH}
+ 30
+
+
+ %date %p %t - %m%n
+
+
+
+
+ log1
+ TopicTest
+ logback
+ 127.0.0.1:9876
+
+ %date %p %t - %m%n
+
+
+
+
+ 1024
+ 80
+ 2000
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 25e4c841b3..c60c93c1c6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -175,6 +175,7 @@
storenamesrvremoting
+ logappenderexamplefiltersrvsrvutil
@@ -623,6 +624,16 @@
openmessaging-api0.1.0-alpha
+
+ log4j
+ log4j
+ 1.2.17
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.7
+
diff --git a/style/rmq_checkstyle.xml b/style/rmq_checkstyle.xml
index 2872eb7202..6ec2ad08fd 100644
--- a/style/rmq_checkstyle.xml
+++ b/style/rmq_checkstyle.xml
@@ -30,6 +30,12 @@
+
+
+
+
+
+
From 16c8d43a603157ee56fdac872e867043c315e1f8 Mon Sep 17 00:00:00 2001
From: djKooks
Date: Sat, 27 May 2017 11:06:37 +0800
Subject: [PATCH 52/74] Add test case for LocalFileOffsetStore closes
apache/incubator-rocketmq#59
---
.../consumer/store/LocalFileOffsetStoreTest.java | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStoreTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStoreTest.java
index 22e212b336..a705b30fc3 100644
--- a/client/src/test/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStoreTest.java
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStoreTest.java
@@ -19,6 +19,8 @@ package org.apache.rocketmq.client.consumer.store;
import java.io.File;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Map;
+
import org.apache.rocketmq.client.ClientConfig;
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
import org.apache.rocketmq.common.message.MessageQueue;
@@ -72,4 +74,15 @@ public class LocalFileOffsetStoreTest {
offsetStore.persistAll(new HashSet(Collections.singletonList(messageQueue)));
assertThat(offsetStore.readOffset(messageQueue, ReadOffsetType.READ_FROM_STORE)).isEqualTo(1024);
}
+
+ @Test
+ public void testCloneOffset() throws Exception {
+ OffsetStore offsetStore = new LocalFileOffsetStore(mQClientFactory, group);
+ MessageQueue messageQueue = new MessageQueue(topic, brokerName, 3);
+ offsetStore.updateOffset(messageQueue, 1024, false);
+ Map cloneOffsetTable = offsetStore.cloneOffsetTable(topic);
+
+ assertThat(cloneOffsetTable.size()).isEqualTo(1);
+ assertThat(cloneOffsetTable.get(messageQueue)).isEqualTo(1024);
+ }
}
\ No newline at end of file
From 031347db7314b511ea7356ac892001ac1349489e Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Sat, 27 May 2017 11:21:09 +0800
Subject: [PATCH 53/74] [ROCKETMQ-98]Fix risk of unable to release putMessage
Lock forever closes apache/incubator-rocketmq#61
---
.../apache/rocketmq/common/BrokerConfig.java | 3 ++
.../org/apache/rocketmq/store/CommitLog.java | 40 +++--------------
.../apache/rocketmq/store/PutMessageLock.java | 25 +++++++++++
.../store/PutMessageReentrantLock.java | 37 ++++++++++++++++
.../rocketmq/store/PutMessageSpinLock.java | 43 +++++++++++++++++++
.../store/config/MessageStoreConfig.java | 4 ++
6 files changed, 119 insertions(+), 33 deletions(-)
create mode 100644 store/src/main/java/org/apache/rocketmq/store/PutMessageLock.java
create mode 100644 store/src/main/java/org/apache/rocketmq/store/PutMessageReentrantLock.java
create mode 100644 store/src/main/java/org/apache/rocketmq/store/PutMessageSpinLock.java
diff --git a/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java b/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java
index f0a73bd289..5bce013da8 100644
--- a/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java
+++ b/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java
@@ -47,6 +47,9 @@ public class BrokerConfig {
private boolean autoCreateSubscriptionGroup = true;
private String messageStorePlugIn = "";
+ /**
+ * thread numbers for send message thread pool, since spin lock will be used by default since 4.0.x, the default value is 1.
+ */
private int sendMessageThreadPoolNums = 1; //16 + Runtime.getRuntime().availableProcessors() * 4;
private int pullMessageThreadPoolNums = 16 + Runtime.getRuntime().availableProcessors() * 2;
private int adminBrokerThreadPoolNums = 16;
diff --git a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
index 7841febf70..7b2926318a 100644
--- a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
+++ b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java
@@ -23,8 +23,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.ReentrantLock;
import org.apache.rocketmq.common.ServiceThread;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.constant.LoggerName;
@@ -63,12 +61,7 @@ public class CommitLog {
private volatile long confirmOffset = -1L;
private volatile long beginTimeInLock = 0;
-
- //true: Can lock, false : in lock.
- private AtomicBoolean putMessageSpinLock = new AtomicBoolean(true);
-
- private ReentrantLock putMessageNormalLock = new ReentrantLock(); // NonfairSync
-
+ private final PutMessageLock putMessageLock;
public CommitLog(final DefaultMessageStore defaultMessageStore) {
this.mappedFileQueue = new MappedFileQueue(defaultMessageStore.getMessageStoreConfig().getStorePathCommitLog(),
defaultMessageStore.getMessageStoreConfig().getMapedFileSizeCommitLog(), defaultMessageStore.getAllocateMappedFileService());
@@ -88,6 +81,8 @@ public class CommitLog {
return new MessageExtBatchEncoder(defaultMessageStore.getMessageStoreConfig().getMaxMessageSize());
}
};
+ this.putMessageLock = defaultMessageStore.getMessageStoreConfig().isUseReentrantLockWhenPutMessage() ? new PutMessageReentrantLock() : new PutMessageSpinLock();
+
}
public boolean load() {
@@ -577,7 +572,7 @@ public class CommitLog {
MappedFile unlockMappedFile = null;
MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();
- lockForPutMessage(); //spin...
+ putMessageLock.lock(); //spin or ReentrantLock ,depending on store config
try {
long beginLockTimestamp = this.defaultMessageStore.getSystemClock().now();
this.beginTimeInLock = beginLockTimestamp;
@@ -626,7 +621,7 @@ public class CommitLog {
eclipseTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginLockTimestamp;
beginTimeInLock = 0;
} finally {
- releasePutMessageLock();
+ putMessageLock.unlock();
}
if (eclipseTimeInLock > 500) {
@@ -861,7 +856,7 @@ public class CommitLog {
}
public boolean appendData(long startOffset, byte[] data) {
- lockForPutMessage(); //spin...
+ putMessageLock.lock();
try {
MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile(startOffset);
if (null == mappedFile) {
@@ -871,7 +866,7 @@ public class CommitLog {
return mappedFile.appendMessage(data);
} finally {
- releasePutMessageLock();
+ putMessageLock.unlock();
}
}
@@ -906,28 +901,7 @@ public class CommitLog {
return diff;
}
- /**
- * Spin util acquired the lock.
- */
- private void lockForPutMessage() {
- if (this.defaultMessageStore.getMessageStoreConfig().isUseReentrantLockWhenPutMessage()) {
- putMessageNormalLock.lock();
- } else {
- boolean flag;
- do {
- flag = this.putMessageSpinLock.compareAndSet(true, false);
- }
- while (!flag);
- }
- }
- private void releasePutMessageLock() {
- if (this.defaultMessageStore.getMessageStoreConfig().isUseReentrantLockWhenPutMessage()) {
- putMessageNormalLock.unlock();
- } else {
- this.putMessageSpinLock.compareAndSet(false, true);
- }
- }
public static class GroupCommitRequest {
private final long nextOffset;
diff --git a/store/src/main/java/org/apache/rocketmq/store/PutMessageLock.java b/store/src/main/java/org/apache/rocketmq/store/PutMessageLock.java
new file mode 100644
index 0000000000..a03e41adf7
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/PutMessageLock.java
@@ -0,0 +1,25 @@
+/*
+ * 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.store;
+
+/**
+ * Used when trying to put message
+ */
+public interface PutMessageLock {
+ void lock();
+ void unlock();
+}
diff --git a/store/src/main/java/org/apache/rocketmq/store/PutMessageReentrantLock.java b/store/src/main/java/org/apache/rocketmq/store/PutMessageReentrantLock.java
new file mode 100644
index 0000000000..9198f1c6f5
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/PutMessageReentrantLock.java
@@ -0,0 +1,37 @@
+/*
+ * 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.store;
+
+
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Exclusive lock implementation to put message
+ */
+public class PutMessageReentrantLock implements PutMessageLock {
+ private ReentrantLock putMessageNormalLock = new ReentrantLock(); // NonfairSync
+
+ @Override
+ public void lock() {
+ putMessageNormalLock.lock();
+ }
+
+ @Override
+ public void unlock() {
+ putMessageNormalLock.unlock();
+ }
+}
diff --git a/store/src/main/java/org/apache/rocketmq/store/PutMessageSpinLock.java b/store/src/main/java/org/apache/rocketmq/store/PutMessageSpinLock.java
new file mode 100644
index 0000000000..baa809db9e
--- /dev/null
+++ b/store/src/main/java/org/apache/rocketmq/store/PutMessageSpinLock.java
@@ -0,0 +1,43 @@
+/*
+ * 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.store;
+
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Spin lock Implementation to put message, suggest using this witb low race conditions
+ *
+ */
+public class PutMessageSpinLock implements PutMessageLock {
+ //true: Can lock, false : in lock.
+ private AtomicBoolean putMessageSpinLock = new AtomicBoolean(true);
+
+ @Override
+ public void lock() {
+ boolean flag;
+ do {
+ flag = this.putMessageSpinLock.compareAndSet(true, false);
+ }
+ while (!flag);
+ }
+
+ @Override
+ public void unlock() {
+ this.putMessageSpinLock.compareAndSet(false, true);
+ }
+}
diff --git a/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java b/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java
index 29f800c205..19ed2113df 100644
--- a/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java
+++ b/store/src/main/java/org/apache/rocketmq/store/config/MessageStoreConfig.java
@@ -52,6 +52,10 @@ public class MessageStoreConfig {
@ImportantField
private int commitIntervalCommitLog = 200;
+ /**
+ * introduced since 4.0.x. Determine whether to use mutex reentrantLock when putting message.
+ * By default it is set to false indicating using spin lock when putting message.
+ */
private boolean useReentrantLockWhenPutMessage = false;
// Whether schedule flush,default is real-time
From 7ab59a335d4745fef92d6ba8b5df14dfd179ea80 Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Sat, 27 May 2017 11:22:46 +0800
Subject: [PATCH 54/74] [ROCKETMQ-188]RemotingExecption is not consistent
between invoke async and invoke oneway closes apache/incubator-rocketmq#98
---
.../remoting/netty/NettyRemotingAbstract.java | 20 +++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
index cddab3d738..15586cbf9b 100644
--- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
+++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstract.java
@@ -417,14 +417,18 @@ public abstract class NettyRemotingAbstract {
throw new RemotingSendRequestException(RemotingHelper.parseChannelRemoteAddr(channel), e);
}
} else {
- String info =
- String.format("invokeAsyncImpl tryAcquire semaphore timeout, %dms, waiting thread nums: %d semaphoreAsyncValue: %d", //
- timeoutMillis, //
- this.semaphoreAsync.getQueueLength(), //
- this.semaphoreAsync.availablePermits()//
- );
- PLOG.warn(info);
- throw new RemotingTooMuchRequestException(info);
+ if (timeoutMillis <= 0) {
+ throw new RemotingTooMuchRequestException("invokeAsyncImpl invoke too fast");
+ } else {
+ String info =
+ String.format("invokeAsyncImpl tryAcquire semaphore timeout, %dms, waiting thread nums: %d semaphoreAsyncValue: %d", //
+ timeoutMillis, //
+ this.semaphoreAsync.getQueueLength(), //
+ this.semaphoreAsync.availablePermits()//
+ );
+ PLOG.warn(info);
+ throw new RemotingTimeoutException(info);
+ }
}
}
From f115b528668736544f5a8632670655f875145f72 Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Sat, 27 May 2017 11:24:02 +0800
Subject: [PATCH 55/74] [ROCKETMQ-200]-Cluster name is always missing when
fetch ClusterInfo from name server closes apache/incubator-rocketmq#105
---
.../rocketmq/common/protocol/route/BrokerData.java | 14 +++++++++++---
.../namesrv/routeinfo/RouteInfoManager.java | 10 ++--------
2 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java b/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java
index 612e5b411e..9d868ae03c 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java
@@ -15,9 +15,7 @@
* limitations under the License.
*/
-/**
- * $Id: BrokerData.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
- */
+
package org.apache.rocketmq.common.protocol.route;
import java.util.HashMap;
@@ -29,6 +27,16 @@ public class BrokerData implements Comparable {
private String brokerName;
private HashMap brokerAddrs;
+ public BrokerData() {
+
+ }
+
+ public BrokerData(String cluster, String brokerName, HashMap brokerAddrs) {
+ this.cluster = cluster;
+ this.brokerName = brokerName;
+ this.brokerAddrs = brokerAddrs;
+ }
+
public String selectBrokerAddr() {
String value = this.brokerAddrs.get(MixAll.MASTER_ID);
if (null == value) {
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java
index 16b7847ae9..5a953a9e20 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java
@@ -125,11 +125,7 @@ public class RouteInfoManager {
BrokerData brokerData = this.brokerAddrTable.get(brokerName);
if (null == brokerData) {
registerFirst = true;
- brokerData = new BrokerData();
- brokerData.setBrokerName(brokerName);
- HashMap brokerAddrs = new HashMap();
- brokerData.setBrokerAddrs(brokerAddrs);
-
+ brokerData = new BrokerData(clusterName, brokerName, new HashMap());
this.brokerAddrTable.put(brokerName, brokerData);
}
String oldAddr = brokerData.getBrokerAddrs().put(brokerId, brokerAddr);
@@ -381,9 +377,7 @@ public class RouteInfoManager {
for (String brokerName : brokerNameSet) {
BrokerData brokerData = this.brokerAddrTable.get(brokerName);
if (null != brokerData) {
- BrokerData brokerDataClone = new BrokerData();
- brokerDataClone.setBrokerName(brokerData.getBrokerName());
- brokerDataClone.setBrokerAddrs((HashMap) brokerData
+ BrokerData brokerDataClone = new BrokerData(brokerData.getCluster(), brokerData.getBrokerName(), (HashMap) brokerData
.getBrokerAddrs().clone());
brokerDataList.add(brokerDataClone);
foundBrokerData = true;
From 3292d039ae882f42168d5f98969df83130ca05fb Mon Sep 17 00:00:00 2001
From: yukon
Date: Sat, 27 May 2017 11:34:44 +0800
Subject: [PATCH 56/74] [ROCKETMQ-206] Catch the IOException when call the
file2String method.
---
.../consumer/store/LocalFileOffsetStore.java | 14 ++++++++++++--
.../rocketmq/example/benchmark/Consumer.java | 3 ++-
.../apache/rocketmq/example/filter/Consumer.java | 3 ++-
.../rocketmq/namesrv/kvconfig/KVConfigManager.java | 7 ++++++-
4 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStore.java b/client/src/main/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStore.java
index 2cde5f8144..6c815167bb 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStore.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/store/LocalFileOffsetStore.java
@@ -180,7 +180,12 @@ public class LocalFileOffsetStore implements OffsetStore {
}
private OffsetSerializeWrapper readLocalOffset() throws MQClientException {
- String content = MixAll.file2String(this.storePath);
+ String content = null;
+ try {
+ content = MixAll.file2String(this.storePath);
+ } catch (IOException e) {
+ log.warn("Load local offset store file exception", e);
+ }
if (null == content || content.length() == 0) {
return this.readLocalOffsetBak();
} else {
@@ -198,7 +203,12 @@ public class LocalFileOffsetStore implements OffsetStore {
}
private OffsetSerializeWrapper readLocalOffsetBak() throws MQClientException {
- String content = MixAll.file2String(this.storePath + ".bak");
+ String content = null;
+ try {
+ content = MixAll.file2String(this.storePath + ".bak");
+ } catch (IOException e) {
+ log.warn("Load local offset store bak file exception", e);
+ }
if (content != null && content.length() > 0) {
OffsetSerializeWrapper offsetSerializeWrapper = null;
try {
diff --git a/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java b/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java
index 3e1b79b0cd..d431d3ecc6 100644
--- a/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java
+++ b/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.example.benchmark;
+import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
@@ -39,7 +40,7 @@ import org.apache.rocketmq.srvutil.ServerUtil;
public class Consumer {
- public static void main(String[] args) throws MQClientException {
+ public static void main(String[] args) throws MQClientException, IOException {
Options options = ServerUtil.buildCommandlineOptions(new Options());
CommandLine commandLine = ServerUtil.parseCmdLine("benchmarkConsumer", args, buildCommandlineOptions(options), new PosixParser());
if (null == commandLine) {
diff --git a/example/src/main/java/org/apache/rocketmq/example/filter/Consumer.java b/example/src/main/java/org/apache/rocketmq/example/filter/Consumer.java
index d63435b592..0be8e1d388 100644
--- a/example/src/main/java/org/apache/rocketmq/example/filter/Consumer.java
+++ b/example/src/main/java/org/apache/rocketmq/example/filter/Consumer.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.example.filter;
import java.io.File;
+import java.io.IOException;
import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
@@ -28,7 +29,7 @@ import org.apache.rocketmq.common.message.MessageExt;
public class Consumer {
- public static void main(String[] args) throws InterruptedException, MQClientException {
+ public static void main(String[] args) throws InterruptedException, MQClientException, IOException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupNamecc4");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/kvconfig/KVConfigManager.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/kvconfig/KVConfigManager.java
index 69afcad7ea..be13bd6bed 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/kvconfig/KVConfigManager.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/kvconfig/KVConfigManager.java
@@ -43,7 +43,12 @@ public class KVConfigManager {
}
public void load() {
- String content = MixAll.file2String(this.namesrvController.getNamesrvConfig().getKvConfigPath());
+ String content = null;
+ try {
+ content = MixAll.file2String(this.namesrvController.getNamesrvConfig().getKvConfigPath());
+ } catch (IOException e) {
+ log.warn("Load KV config table exception", e);
+ }
if (content != null) {
KVConfigSerializeWrapper kvConfigSerializeWrapper =
KVConfigSerializeWrapper.fromJson(content, KVConfigSerializeWrapper.class);
From 787d128613d00a617be2f8ef943e9cfafcc07f85 Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Sat, 27 May 2017 11:42:03 +0800
Subject: [PATCH 57/74] [ROCKETMQ-67] Consistent Hash allocate strategy closes
apache/incubator-rocketmq#67
---
.../AllocateMessageQueueConsistentHash.java | 124 +++++++++
...AllocateMessageQueueConsitentHashTest.java | 243 ++++++++++++++++++
.../consistenthash/ConsistentHashRouter.java | 140 ++++++++++
.../common/consistenthash/HashFunction.java | 24 ++
.../rocketmq/common/consistenthash/Node.java | 28 ++
.../common/consistenthash/VirtualNode.java | 41 +++
6 files changed, 600 insertions(+)
create mode 100644 client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java
create mode 100644 client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/consistenthash/ConsistentHashRouter.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/consistenthash/HashFunction.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/consistenthash/Node.java
create mode 100644 common/src/main/java/org/apache/rocketmq/common/consistenthash/VirtualNode.java
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java b/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java
new file mode 100644
index 0000000000..77198b7fdc
--- /dev/null
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java
@@ -0,0 +1,124 @@
+/*
+ * 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.consumer.rebalance;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.rocketmq.client.consumer.AllocateMessageQueueStrategy;
+import org.apache.rocketmq.client.log.ClientLogger;
+import org.apache.rocketmq.common.consistenthash.ConsistentHashRouter;
+import org.apache.rocketmq.common.consistenthash.HashFunction;
+import org.apache.rocketmq.common.consistenthash.Node;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.slf4j.Logger;
+
+/**
+ * Consistent Hashing queue algorithm
+ */
+public class AllocateMessageQueueConsistentHash implements AllocateMessageQueueStrategy {
+ private final Logger log = ClientLogger.getLog();
+
+ private final int virtualNodeCnt;
+ private final HashFunction customHashFunction;
+
+ public AllocateMessageQueueConsistentHash() {
+ this(10);
+ }
+
+ public AllocateMessageQueueConsistentHash(int virtualNodeCnt) {
+ this(virtualNodeCnt,null);
+ }
+
+ public AllocateMessageQueueConsistentHash(int virtualNodeCnt, HashFunction customHashFunction) {
+ if (virtualNodeCnt < 0) {
+ throw new IllegalArgumentException("illegal virtualNodeCnt :" + virtualNodeCnt);
+ }
+ this.virtualNodeCnt = virtualNodeCnt;
+ this.customHashFunction = customHashFunction;
+ }
+
+ @Override
+ public List allocate(String consumerGroup, String currentCID, List mqAll,
+ List cidAll) {
+
+ if (currentCID == null || currentCID.length() < 1) {
+ throw new IllegalArgumentException("currentCID is empty");
+ }
+ if (mqAll == null || mqAll.isEmpty()) {
+ throw new IllegalArgumentException("mqAll is null or mqAll empty");
+ }
+ if (cidAll == null || cidAll.isEmpty()) {
+ throw new IllegalArgumentException("cidAll is null or cidAll empty");
+ }
+
+ List result = new ArrayList();
+ if (!cidAll.contains(currentCID)) {
+ log.info("[BUG] ConsumerGroup: {} The consumerId: {} not in cidAll: {}",
+ consumerGroup,
+ currentCID,
+ cidAll);
+ return result;
+ }
+
+
+ Collection cidNodes = new ArrayList<>();
+ for (String cid : cidAll) {
+ cidNodes.add(new ClientNode(cid));
+ }
+
+ final ConsistentHashRouter router; //for building hash ring
+ if (customHashFunction != null) {
+ router = new ConsistentHashRouter<>(cidNodes, virtualNodeCnt, customHashFunction);
+ } else {
+ router = new ConsistentHashRouter<>(cidNodes, virtualNodeCnt);
+ }
+
+ List results = new ArrayList<>();
+ for (MessageQueue mq : mqAll) {
+ ClientNode clientNode = router.routeNode(mq.toString());
+ if (clientNode != null && currentCID.equals(clientNode.getKey())) {
+ results.add(mq);
+ }
+ }
+
+ return results;
+
+ }
+
+ @Override
+ public String getName() {
+ return "CONSISTENT_HASH";
+ }
+
+
+ private static class ClientNode implements Node {
+ private final String clientID;
+
+ public ClientNode(String clientID) {
+ this.clientID = clientID;
+ }
+
+ @Override
+ public String getKey() {
+ return clientID;
+ }
+ }
+
+
+
+}
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java
new file mode 100644
index 0000000000..fc7ab9fcf4
--- /dev/null
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.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.client.consumer.rebalance;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+import org.apache.rocketmq.client.consumer.AllocateMessageQueueStrategy;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class AllocateMessageQueueConsitentHashTest {
+
+ private String topic;
+ private static final String CID_PREFIX = "CID-";
+
+ @Before
+ public void init() {
+ topic = "topic_test";
+ }
+
+
+
+ public void printMessageQueue(List messageQueueList, String name) {
+ if (messageQueueList == null || messageQueueList.size() < 1)
+ return;
+ System.out.println(name + ".......................................start");
+ for (MessageQueue messageQueue : messageQueueList) {
+ System.out.println(messageQueue);
+ }
+ System.out.println(name + ".......................................end");
+ }
+
+ @Test
+ public void testCurrentCIDNotExists() {
+ String currentCID = String.valueOf(Integer.MAX_VALUE);
+ List consumerIdList = createConsumerIdList(2);
+ List messageQueueList = createMessageQueueList(6);
+ List result = new AllocateMessageQueueConsistentHash().allocate("", currentCID, messageQueueList, consumerIdList);
+ printMessageQueue(result, "testCurrentCIDNotExists");
+ Assert.assertEquals(result.size(), 0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCurrentCIDIllegalArgument() {
+ List consumerIdList = createConsumerIdList(2);
+ List messageQueueList = createMessageQueueList(6);
+ new AllocateMessageQueueConsistentHash().allocate("", "", messageQueueList, consumerIdList);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMessageQueueIllegalArgument() {
+ String currentCID = "0";
+ List consumerIdList = createConsumerIdList(2);
+ new AllocateMessageQueueConsistentHash().allocate("", currentCID, null, consumerIdList);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConsumerIdIllegalArgument() {
+ String currentCID = "0";
+ List messageQueueList = createMessageQueueList(6);
+ new AllocateMessageQueueConsistentHash().allocate("", currentCID, messageQueueList, null);
+ }
+
+ @Test
+ public void testAllocate1() {
+ testAllocate(20,10);
+ }
+
+ @Test
+ public void testAllocate2() {
+ testAllocate(10,20);
+ }
+
+
+ @Test
+ public void testRun100RandomCase(){
+ for(int i=0;i<100;i++){
+ int consumerSize = new Random().nextInt(200)+1;//1-200
+ int queueSize = new Random().nextInt(100)+1;//1-100
+ testAllocate(queueSize,consumerSize);
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {}
+ }
+ }
+
+
+ public void testAllocate(int queueSize, int consumerSize) {
+ AllocateMessageQueueStrategy allocateMessageQueueConsistentHash = new AllocateMessageQueueConsistentHash(3);
+
+ List mqAll = createMessageQueueList(queueSize);
+ //System.out.println("mqAll:" + mqAll.toString());
+
+ List cidAll = createConsumerIdList(consumerSize);
+ List allocatedResAll = new ArrayList<>();
+
+ Map allocateToAllOrigin = new TreeMap<>();
+ //test allocate all
+ {
+
+ List cidBegin = new ArrayList<>(cidAll);
+
+ //System.out.println("cidAll:" + cidBegin.toString());
+ for (String cid : cidBegin) {
+ List rs = allocateMessageQueueConsistentHash.allocate("testConsumerGroup", cid, mqAll, cidBegin);
+ for (MessageQueue mq : rs) {
+ allocateToAllOrigin.put(mq, cid);
+ }
+ allocatedResAll.addAll(rs);
+ //System.out.println("rs[" + cid + "]:" + rs.toString());
+ }
+
+ Assert.assertTrue(
+ verifyAllocateAll(cidBegin,mqAll, allocatedResAll));
+ }
+
+ Map allocateToAllAfterRemoveOne = new TreeMap<>();
+ List cidAfterRemoveOne = new ArrayList<>(cidAll);
+ //test allocate remove one cid
+ {
+ String removeCID = cidAfterRemoveOne.remove(0);
+ //System.out.println("removing one cid "+removeCID);
+ List mqShouldOnlyChanged = new ArrayList<>();
+ Iterator> it = allocateToAllOrigin.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry entry = it.next();
+ if (entry.getValue().equals(removeCID)) {
+ mqShouldOnlyChanged.add(entry.getKey());
+ }
+ }
+
+ //System.out.println("cidAll:" + cidAfterRemoveOne.toString());
+ List allocatedResAllAfterRemove = new ArrayList<>();
+ for (String cid : cidAfterRemoveOne) {
+ List rs = allocateMessageQueueConsistentHash.allocate("testConsumerGroup", cid, mqAll, cidAfterRemoveOne);
+ allocatedResAllAfterRemove.addAll(rs);
+ for (MessageQueue mq : rs) {
+ allocateToAllAfterRemoveOne.put(mq, cid);
+ }
+ //System.out.println("rs[" + cid + "]:" + "[" + rs.size() + "]" + rs.toString());
+ }
+
+ Assert.assertTrue("queueSize"+queueSize+"consumerSize:"+consumerSize+"\nmqAll:"+mqAll+"\nallocatedResAllAfterRemove"+allocatedResAllAfterRemove,
+ verifyAllocateAll(cidAfterRemoveOne, mqAll, allocatedResAllAfterRemove));
+ verifyAfterRemove(allocateToAllOrigin, allocateToAllAfterRemoveOne, removeCID);
+ }
+
+ List cidAfterAdd = new ArrayList<>(cidAfterRemoveOne);
+ //test allocate add one more cid
+ {
+ String newCid = CID_PREFIX+"NEW";
+ //System.out.println("add one more cid "+newCid);
+ cidAfterAdd.add(newCid);
+ List mqShouldOnlyChanged = new ArrayList<>();
+ //System.out.println("cidAll:" + cidAfterAdd.toString());
+ List allocatedResAllAfterAdd = new ArrayList<>();
+ Map allocateToAll3 = new TreeMap<>();
+ for (String cid : cidAfterAdd) {
+ List rs = allocateMessageQueueConsistentHash.allocate("testConsumerGroup", cid, mqAll, cidAfterAdd);
+ allocatedResAllAfterAdd.addAll(rs);
+ for (MessageQueue mq : rs) {
+ allocateToAll3.put(mq, cid);
+ if (cid.equals(newCid)){
+ mqShouldOnlyChanged.add(mq);
+ }
+ }
+ //System.out.println("rs[" + cid + "]:" + "[" + rs.size() + "]" + rs.toString());
+ }
+
+ Assert.assertTrue(
+ verifyAllocateAll(cidAfterAdd,mqAll, allocatedResAllAfterAdd));
+ verifyAfterAdd(allocateToAllAfterRemoveOne, allocateToAll3, newCid);
+ }
+ }
+
+ private boolean verifyAllocateAll(List cidAll,List mqAll, List allocatedResAll) {
+ if (cidAll.isEmpty()){
+ return allocatedResAll.isEmpty();
+ }
+ return mqAll.containsAll(allocatedResAll) && allocatedResAll.containsAll(mqAll);
+ }
+
+ private void verifyAfterRemove(Map allocateToBefore, Map allocateAfter, String removeCID) {
+ for (MessageQueue mq : allocateToBefore.keySet()) {
+ String allocateToOrigin = allocateToBefore.get(mq);
+ if (allocateToOrigin.equals(removeCID)) {
+
+ } else {//the rest queue should be the same
+ Assert.assertTrue(allocateAfter.get(mq).equals(allocateToOrigin));//should be the same
+ }
+ }
+ }
+
+ private void verifyAfterAdd(Map allocateBefore, Map allocateAfter, String newCID) {
+ for (MessageQueue mq : allocateAfter.keySet()) {
+ String allocateToOrigin = allocateBefore.get(mq);
+ String allocateToAfter = allocateAfter.get(mq);
+ if (allocateToAfter.equals(newCID)) {
+
+ } else {//the rest queue should be the same
+ Assert.assertTrue("it was allocated to "+allocateToOrigin+". Now, it is to "+allocateAfter.get(mq)+" mq:"+mq,allocateAfter.get(mq).equals(allocateToOrigin));//should be the same
+ }
+ }
+ }
+
+ private List createConsumerIdList(int size) {
+ List consumerIdList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ consumerIdList.add(CID_PREFIX + String.valueOf(i));
+ }
+ return consumerIdList;
+ }
+
+ private List createMessageQueueList(int size) {
+ List messageQueueList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ MessageQueue mq = new MessageQueue(topic, "brokerName", i);
+ messageQueueList.add(mq);
+ }
+ return messageQueueList;
+ }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/consistenthash/ConsistentHashRouter.java b/common/src/main/java/org/apache/rocketmq/common/consistenthash/ConsistentHashRouter.java
new file mode 100644
index 0000000000..8606c4330e
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/consistenthash/ConsistentHashRouter.java
@@ -0,0 +1,140 @@
+/*
+ * 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.consistenthash;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * To hash Node objects to a hash ring with a certain amount of virtual node.
+ * Method routeNode will return a Node instance which the object key should be allocated to according to consistent hash algorithm
+ *
+ * @param
+ */
+public class ConsistentHashRouter {
+ private final SortedMap> ring = new TreeMap<>();
+ private final HashFunction hashFunction;
+
+ public ConsistentHashRouter(Collection pNodes, int vNodeCount) {
+ this(pNodes,vNodeCount, new MD5Hash());
+ }
+
+ /**
+ *
+ * @param pNodes collections of physical nodes
+ * @param vNodeCount amounts of virtual nodes
+ * @param hashFunction hash Function to hash Node instances
+ */
+ public ConsistentHashRouter(Collection pNodes, int vNodeCount, HashFunction hashFunction) {
+ if (hashFunction == null) {
+ throw new NullPointerException("Hash Function is null");
+ }
+ this.hashFunction = hashFunction;
+ if (pNodes != null) {
+ for (T pNode : pNodes) {
+ addNode(pNode, vNodeCount);
+ }
+ }
+ }
+
+ /**
+ * add physic node to the hash ring with some virtual nodes
+ * @param pNode physical node needs added to hash ring
+ * @param vNodeCount the number of virtual node of the physical node. Value should be greater than or equals to 0
+ */
+ public void addNode(T pNode, int vNodeCount) {
+ if (vNodeCount < 0) throw new IllegalArgumentException("illegal virtual node counts :" + vNodeCount);
+ int existingReplicas = getExistingReplicas(pNode);
+ for (int i = 0; i < vNodeCount; i++) {
+ VirtualNode vNode = new VirtualNode<>(pNode, i + existingReplicas);
+ ring.put(hashFunction.hash(vNode.getKey()), vNode);
+ }
+ }
+
+ /**
+ * remove the physical node from the hash ring
+ * @param pNode
+ */
+ public void removeNode(T pNode) {
+ Iterator it = ring.keySet().iterator();
+ while (it.hasNext()) {
+ Long key = it.next();
+ VirtualNode virtualNode = ring.get(key);
+ if (virtualNode.isVirtualNodeOf(pNode)) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * with a specified key, route the nearest Node instance in the current hash ring
+ * @param objectKey the object key to find a nearest Node
+ * @return
+ */
+ public T routeNode(String objectKey) {
+ if (ring.isEmpty()) {
+ return null;
+ }
+ Long hashVal = hashFunction.hash(objectKey);
+ SortedMap> tailMap = ring.tailMap(hashVal);
+ Long nodeHashVal = !tailMap.isEmpty() ? tailMap.firstKey() : ring.firstKey();
+ return ring.get(nodeHashVal).getPhysicalNode();
+ }
+
+
+ public int getExistingReplicas(T pNode) {
+ int replicas = 0;
+ for (VirtualNode vNode : ring.values()) {
+ if (vNode.isVirtualNodeOf(pNode)) {
+ replicas++;
+ }
+ }
+ return replicas;
+ }
+
+
+ //default hash function
+ private static class MD5Hash implements HashFunction {
+ MessageDigest instance;
+
+ public MD5Hash() {
+ try {
+ instance = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ }
+ }
+
+ @Override
+ public long hash(String key) {
+ instance.reset();
+ instance.update(key.getBytes());
+ byte[] digest = instance.digest();
+
+ long h = 0;
+ for (int i = 0; i < 4; i++) {
+ h <<= 8;
+ h |= ((int) digest[i]) & 0xFF;
+ }
+ return h;
+ }
+ }
+
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/consistenthash/HashFunction.java b/common/src/main/java/org/apache/rocketmq/common/consistenthash/HashFunction.java
new file mode 100644
index 0000000000..58fd777cad
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/consistenthash/HashFunction.java
@@ -0,0 +1,24 @@
+/*
+ * 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.consistenthash;
+
+/**
+ * Hash String to long value
+ */
+public interface HashFunction {
+ long hash(String key);
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/consistenthash/Node.java b/common/src/main/java/org/apache/rocketmq/common/consistenthash/Node.java
new file mode 100644
index 0000000000..0ece21098a
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/consistenthash/Node.java
@@ -0,0 +1,28 @@
+/*
+ * 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.consistenthash;
+
+/**
+ * Represent a node which should be mapped to a hash ring
+ */
+public interface Node {
+ /**
+ *
+ * @return the key which will be used for hash mapping
+ */
+ String getKey();
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/consistenthash/VirtualNode.java b/common/src/main/java/org/apache/rocketmq/common/consistenthash/VirtualNode.java
new file mode 100644
index 0000000000..c8b72d9018
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/consistenthash/VirtualNode.java
@@ -0,0 +1,41 @@
+/*
+ * 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.consistenthash;
+
+
+public class VirtualNode implements Node {
+ final T physicalNode;
+ final int replicaIndex;
+
+ public VirtualNode(T physicalNode, int replicaIndex) {
+ this.replicaIndex = replicaIndex;
+ this.physicalNode = physicalNode;
+ }
+
+ @Override
+ public String getKey() {
+ return physicalNode.getKey() + "-" + replicaIndex;
+ }
+
+ public boolean isVirtualNodeOf(T pNode) {
+ return physicalNode.getKey().equals(pNode.getKey());
+ }
+
+ public T getPhysicalNode() {
+ return physicalNode;
+ }
+}
From 1f4b893ce70c615daa0c8fce948cef776c108d92 Mon Sep 17 00:00:00 2001
From: Jaskey
Date: Sat, 27 May 2017 12:38:00 +0800
Subject: [PATCH 58/74] [ROCKETMQ-160]SendHeartBeat may not be logged in the
expected period closes apache/incubator-rocketmq#86
---
.../client/impl/factory/MQClientInstance.java | 66 ++++++++++---------
1 file changed, 34 insertions(+), 32 deletions(-)
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 a8c65b29a6..1b075ee114 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
@@ -112,7 +112,7 @@ public class MQClientInstance {
private final RebalanceService rebalanceService;
private final DefaultMQProducer defaultMQProducer;
private final ConsumerStatsManager consumerStatsManager;
- private final AtomicLong storeTimesTotal = new AtomicLong(0);
+ private final AtomicLong sendHeartbeatTimesTotal = new AtomicLong(0);
private ServiceState serviceState = ServiceState.CREATE_JUST;
private DatagramSocket datagramSocket;
private Random random = new Random();
@@ -517,38 +517,40 @@ public class MQClientInstance {
return;
}
- long times = this.storeTimesTotal.getAndIncrement();
- Iterator>> it = this.brokerAddrTable.entrySet().iterator();
- while (it.hasNext()) {
- Entry> entry = it.next();
- String brokerName = entry.getKey();
- HashMap oneTable = entry.getValue();
- if (oneTable != null) {
- for (Map.Entry entry1 : oneTable.entrySet()) {
- Long id = entry1.getKey();
- String addr = entry1.getValue();
- if (addr != null) {
- if (consumerEmpty) {
- if (id != MixAll.MASTER_ID)
- continue;
- }
+ if (!this.brokerAddrTable.isEmpty()) {
+ long times = this.sendHeartbeatTimesTotal.getAndIncrement();
+ Iterator>> it = this.brokerAddrTable.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry> entry = it.next();
+ String brokerName = entry.getKey();
+ HashMap oneTable = entry.getValue();
+ if (oneTable != null) {
+ for (Map.Entry entry1 : oneTable.entrySet()) {
+ Long id = entry1.getKey();
+ String addr = entry1.getValue();
+ if (addr != null) {
+ if (consumerEmpty) {
+ if (id != MixAll.MASTER_ID)
+ continue;
+ }
- try {
- int version = this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000);
- if (!this.brokerVersionTable.containsKey(brokerName)) {
- this.brokerVersionTable.put(brokerName, new HashMap(4));
- }
- this.brokerVersionTable.get(brokerName).put(addr, version);
- if (times % 20 == 0) {
- log.info("send heart beat to broker[{} {} {}] success", brokerName, id, addr);
- log.info(heartbeatData.toString());
- }
- } catch (Exception e) {
- if (this.isBrokerInNameServer(addr)) {
- log.error("send heart beat to broker exception", e);
- } else {
- log.info("send heart beat to broker[{} {} {}] exception, because the broker not up, forget it", brokerName,
- id, addr);
+ try {
+ int version = this.mQClientAPIImpl.sendHearbeat(addr, heartbeatData, 3000);
+ if (!this.brokerVersionTable.containsKey(brokerName)) {
+ this.brokerVersionTable.put(brokerName, new HashMap(4));
+ }
+ this.brokerVersionTable.get(brokerName).put(addr, version);
+ if (times % 20 == 0) {
+ log.info("send heart beat to broker[{} {} {}] success", brokerName, id, addr);
+ log.info(heartbeatData.toString());
+ }
+ } catch (Exception e) {
+ if (this.isBrokerInNameServer(addr)) {
+ log.info("send heart beat to broker[{} {} {}] failed", brokerName, id, addr);
+ } else {
+ log.info("send heart beat to broker[{} {} {}] exception, because the broker not up, forget it", brokerName,
+ id, addr);
+ }
}
}
}
From 5c5180ec1f2d884d806b4256557ea8d5c472a601 Mon Sep 17 00:00:00 2001
From: dongeforever
Date: Sat, 27 May 2017 14:09:08 +0800
Subject: [PATCH 59/74] Remove diamond operator for client module with JDK 1.6
---
.../AllocateMessageQueueConsistentHash.java | 8 +++---
...AllocateMessageQueueConsitentHashTest.java | 27 ++++++++++---------
.../consistenthash/ConsistentHashRouter.java | 4 +--
.../org/apache/rocketmq/store/CommitLog.java | 4 +--
4 files changed, 22 insertions(+), 21 deletions(-)
diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java b/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java
index 77198b7fdc..09d940a1cf 100644
--- a/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java
+++ b/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsistentHash.java
@@ -76,19 +76,19 @@ public class AllocateMessageQueueConsistentHash implements AllocateMessageQueue
}
- Collection cidNodes = new ArrayList<>();
+ Collection cidNodes = new ArrayList();
for (String cid : cidAll) {
cidNodes.add(new ClientNode(cid));
}
final ConsistentHashRouter router; //for building hash ring
if (customHashFunction != null) {
- router = new ConsistentHashRouter<>(cidNodes, virtualNodeCnt, customHashFunction);
+ router = new ConsistentHashRouter(cidNodes, virtualNodeCnt, customHashFunction);
} else {
- router = new ConsistentHashRouter<>(cidNodes, virtualNodeCnt);
+ router = new ConsistentHashRouter(cidNodes, virtualNodeCnt);
}
- List results = new ArrayList<>();
+ List results = new ArrayList();
for (MessageQueue mq : mqAll) {
ClientNode clientNode = router.routeNode(mq.toString());
if (clientNode != null && currentCID.equals(clientNode.getKey())) {
diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java
index fc7ab9fcf4..e9e5db7efd 100644
--- a/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java
+++ b/client/src/test/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueConsitentHashTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import org.apache.rocketmq.client.consumer.AllocateMessageQueueStrategy;
+import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.junit.Assert;
import org.junit.Before;
@@ -113,13 +114,13 @@ public class AllocateMessageQueueConsitentHashTest {
//System.out.println("mqAll:" + mqAll.toString());
List cidAll = createConsumerIdList(consumerSize);
- List allocatedResAll = new ArrayList<>();
+ List allocatedResAll = new ArrayList();
- Map allocateToAllOrigin = new TreeMap<>();
+ Map allocateToAllOrigin = new TreeMap();
//test allocate all
{
- List cidBegin = new ArrayList<>(cidAll);
+ List cidBegin = new ArrayList