You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by yu...@apache.org on 2019/06/28 11:19:36 UTC
[rocketmq-remoting] 01/39: Import remoting module from
apache/rocketmq/rocketmq5
This is an automated email from the ASF dual-hosted git repository.
yukon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/rocketmq-remoting.git
commit 548934acca5eaaea54402b4919d0bc86f056416a
Author: yukon <yu...@apache.org>
AuthorDate: Mon May 13 15:51:32 2019 +0800
Import remoting module from apache/rocketmq/rocketmq5
---
.gitignore | 15 +
README.md | 3 +
pom.xml | 99 ++++
remoting-core/remoting-api/pom.xml | 30 +
.../apache/rocketmq/remoting/api/AsyncHandler.java | 34 ++
.../rocketmq/remoting/api/ConnectionService.java | 24 +
.../rocketmq/remoting/api/ObjectLifecycle.java | 29 +
.../rocketmq/remoting/api/RemotingClient.java | 28 +
.../rocketmq/remoting/api/RemotingEndPoint.java | 23 +
.../rocketmq/remoting/api/RemotingMarshaller.java | 27 +
.../rocketmq/remoting/api/RemotingServer.java | 32 +
.../rocketmq/remoting/api/RemotingService.java | 40 ++
.../rocketmq/remoting/api/RequestProcessor.java | 25 +
.../remoting/api/buffer/ByteBufferWrapper.java | 56 ++
.../remoting/api/channel/ChannelEventListener.java | 28 +
.../api/channel/ChannelHandlerContextWrapper.java | 21 +
.../rocketmq/remoting/api/channel/ChunkRegion.java | 54 ++
.../remoting/api/channel/RemotingChannel.java | 78 +++
.../remoting/api/command/RemotingCommand.java | 90 +++
.../api/command/RemotingCommandFactory.java | 24 +
.../rocketmq/remoting/api/command/TrafficType.java | 40 ++
.../remoting/api/compressable/Compressor.java | 28 +
.../api/compressable/CompressorFactory.java | 28 +
.../api/exception/NestedRuntimeException.java | 97 ++++
.../api/exception/RemoteAccessException.java | 57 ++
.../api/exception/RemoteCodecException.java | 33 ++
.../exception/RemoteConnectFailureException.java | 49 ++
.../api/exception/RemoteTimeoutException.java | 70 +++
.../remoting/api/interceptor/ExceptionContext.java | 76 +++
.../remoting/api/interceptor/Interceptor.java | 26 +
.../remoting/api/interceptor/InterceptorGroup.java | 49 ++
.../remoting/api/interceptor/RequestContext.java | 65 +++
.../remoting/api/interceptor/ResponseContext.java | 73 +++
.../rocketmq/remoting/api/protocol/Protocol.java | 39 ++
.../remoting/api/protocol/ProtocolFactory.java | 30 +
.../remoting/api/serializable/Serializer.java | 36 ++
.../api/serializable/SerializerFactory.java | 28 +
.../org/apache/rocketmq/remoting/common/Pair.java | 44 ++
.../rocketmq/remoting/common/TypePresentation.java | 73 +++
remoting-core/remoting-impl/pom.xml | 52 ++
.../remoting/common/ChannelEventListenerGroup.java | 61 ++
.../common/RemotingCommandFactoryMeta.java | 49 ++
.../rocketmq/remoting/common/ResponseResult.java | 190 ++++++
.../remoting/common/SemaphoreReleaseOnlyOnce.java | 40 ++
.../remoting/common/metrics/ChannelMetrics.java | 27 +
.../rocketmq/remoting/config/RemotingConfig.java | 375 ++++++++++++
.../rocketmq/remoting/config/TcpSocketConfig.java | 98 ++++
.../rocketmq/remoting/external/ThreadUtils.java | 185 ++++++
.../impl/buffer/NettyByteBufferWrapper.java | 117 ++++
.../channel/ChannelHandlerContextWrapperImpl.java | 33 ++
.../remoting/impl/channel/FileRegionImpl.java | 85 +++
.../remoting/impl/channel/NettyChannelImpl.java | 94 +++
.../remoting/impl/command/CodecHelper.java | 180 ++++++
.../impl/command/RemotingCommandFactoryImpl.java | 53 ++
.../remoting/impl/command/RemotingCommandImpl.java | 210 +++++++
.../remoting/impl/command/RequestIdGenerator.java | 34 ++
.../remoting/impl/netty/NettyChannelEvent.java | 55 ++
.../remoting/impl/netty/NettyChannelEventType.java | 25 +
.../remoting/impl/netty/NettyRemotingAbstract.java | 642 +++++++++++++++++++++
.../remoting/impl/netty/NettyRemotingClient.java | 499 ++++++++++++++++
.../remoting/impl/netty/NettyRemotingServer.java | 286 +++++++++
.../impl/netty/RemotingBootstrapFactory.java | 60 ++
.../impl/netty/handler/ChannelStatistics.java | 61 ++
.../remoting/impl/netty/handler/Decoder.java | 107 ++++
.../remoting/impl/netty/handler/Encoder.java | 89 +++
.../impl/netty/handler/ExceptionHandler.java | 37 ++
.../remoting/impl/netty/handler/Http2Handler.java | 139 +++++
.../impl/netty/handler/ProtocolSelector.java | 65 +++
.../remoting/impl/protocol/Httpv2Protocol.java | 52 ++
.../impl/protocol/ProtocolFactoryImpl.java | 83 +++
.../impl/protocol/RemotingCoreProtocol.java | 46 ++
.../remoting/impl/protocol/WebSocketProtocol.java | 38 ++
.../compression/CompressorFactoryImpl.java | 68 +++
.../impl/protocol/compression/GZipCompressor.java | 100 ++++
.../impl/protocol/serializer/JsonSerializer.java | 88 +++
.../impl/protocol/serializer/Kryo3Serializer.java | 90 +++
.../protocol/serializer/MsgPackSerializer.java | 78 +++
.../protocol/serializer/SerializerFactoryImpl.java | 69 +++
.../impl/protocol/serializer/ThreadSafeKryo.java | 99 ++++
.../rocketmq/remoting/internal/BeanUtils.java | 210 +++++++
.../rocketmq/remoting/internal/ByteUtils.java | 379 ++++++++++++
.../rocketmq/remoting/internal/ExceptionUtils.java | 80 +++
.../rocketmq/remoting/internal/JvmUtils.java | 94 +++
.../rocketmq/remoting/internal/NetworkUtils.java | 81 +++
.../rocketmq/remoting/internal/PropertyUtils.java | 60 ++
.../rocketmq/remoting/internal/UIDGenerator.java | 97 ++++
.../org/apache/rocketmq/remoting/package-info.java | 49 ++
style/copyright/Apache.xml | 24 +
style/copyright/profiles_settings.xml | 64 ++
style/rmq_checkstyle.xml | 140 +++++
style/rmq_codeStyle.xml | 143 +++++
91 files changed, 7881 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..264f48d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*dependency-reduced-pom.xml
+.classpath
+.project
+.settings/
+target/
+devenv
+*.log*
+*.iml
+.idea/
+*.versionsBackup
+!NOTICE-BIN
+!LICENSE-BIN
+.DS_Store
+localbin
+nohup.out
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2b8325e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+## Apache RocketMQ XXX
+
+Apache RocketMQ XXX is the next generation of RocketMQ, which is extremely simple and high available.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a3f8246
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.rocketmq</groupId>
+ <artifactId>rocketmq-xxx</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+ <!-- Maven properties -->
+ <maven.test.skip>false</maven.test.skip>
+ <maven.javadoc.skip>true</maven.javadoc.skip>
+ <!-- Compiler settings properties -->
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <modules>
+ <module>remoting-core/remoting-api</module>
+ <module>remoting-core/remoting-impl</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>2.6.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.6.3</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>remoting-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ <version>4.1.26.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ <version>1.2.51</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.7</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.0.13</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ <version>15.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.esotericsoftware</groupId>
+ <artifactId>kryo</artifactId>
+ <version>3.0.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.msgpack</groupId>
+ <artifactId>msgpack</artifactId>
+ <version>0.6.12</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+</project>
\ No newline at end of file
diff --git a/remoting-core/remoting-api/pom.xml b/remoting-core/remoting-api/pom.xml
new file mode 100644
index 0000000..a5d1d0b
--- /dev/null
+++ b/remoting-core/remoting-api/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>rocketmq-xxx</artifactId>
+ <groupId>org.apache.rocketmq</groupId>
+ <version>0.1.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>remoting-api</artifactId>
+
+ <properties>
+ <maven.compiler.source>1.7</maven.compiler.source>
+ <maven.compiler.target>1.7</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/AsyncHandler.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/AsyncHandler.java
new file mode 100644
index 0000000..106431b
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/AsyncHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api;
+
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+/**
+ * The AsyncHandler interface is implemented when wishing to receive callback notification of the completion of
+ * service invoked asynchronously.
+ *
+ * @since 1.0.0
+ */
+public interface AsyncHandler {
+ void onFailure(RemotingCommand command);
+
+ void onSuccess(RemotingCommand command);
+
+ void onTimeout(long costTimeMillis, long timeoutMillis);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/ConnectionService.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/ConnectionService.java
new file mode 100644
index 0000000..c42498f
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/ConnectionService.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.remoting.api;
+
+import org.apache.rocketmq.remoting.api.channel.ChannelEventListener;
+
+public interface ConnectionService {
+ void registerChannelEventListener(ChannelEventListener listener);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/ObjectLifecycle.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/ObjectLifecycle.java
new file mode 100644
index 0000000..c4a75c8
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/ObjectLifecycle.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+public interface ObjectLifecycle {
+ @PostConstruct
+ void start();
+
+ @PreDestroy
+ void stop();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingClient.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingClient.java
new file mode 100644
index 0000000..1603af4
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingClient.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.remoting.api;
+
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public interface RemotingClient extends RemotingService {
+ RemotingCommand invoke(String address, RemotingCommand request, long timeoutMillis);
+
+ void invokeAsync(String address, RemotingCommand request, AsyncHandler asyncHandler, long timeoutMillis);
+
+ void invokeOneWay(String address, RemotingCommand request, long timeoutMillis);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingEndPoint.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingEndPoint.java
new file mode 100644
index 0000000..2bc3edf
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingEndPoint.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api;
+
+public enum RemotingEndPoint {
+ REQUEST,
+ RESPONSE
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingMarshaller.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingMarshaller.java
new file mode 100644
index 0000000..0386a03
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingMarshaller.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api;
+
+import org.apache.rocketmq.remoting.api.protocol.ProtocolFactory;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+
+public interface RemotingMarshaller {
+ ProtocolFactory protocolFactory();
+
+ SerializerFactory serializerFactory();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingServer.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingServer.java
new file mode 100644
index 0000000..f36c83c
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingServer.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.remoting.api;
+
+import org.apache.rocketmq.remoting.api.channel.RemotingChannel;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public interface RemotingServer extends RemotingService {
+ int localListenPort();
+
+ RemotingCommand invoke(RemotingChannel remotingChannel, RemotingCommand request, long timeoutMillis);
+
+ void invokeAsync(RemotingChannel remotingChannel, RemotingCommand request, AsyncHandler asyncHandler,
+ long timeoutMillis);
+
+ void invokeOneWay(RemotingChannel remotingChannel, RemotingCommand request, long timeoutMillis);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingService.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingService.java
new file mode 100644
index 0000000..2f6343c
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RemotingService.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.remoting.api;
+
+import java.util.concurrent.ExecutorService;
+import org.apache.rocketmq.remoting.api.command.RemotingCommandFactory;
+import org.apache.rocketmq.remoting.api.interceptor.Interceptor;
+import org.apache.rocketmq.remoting.common.Pair;
+
+public interface RemotingService extends RemotingMarshaller, ConnectionService, ObjectLifecycle {
+ void registerInterceptor(Interceptor interceptor);
+
+ void registerRequestProcessor(final String requestCode, final RequestProcessor processor,
+ final ExecutorService executor);
+
+ void registerRequestProcessor(final String requestCode, final RequestProcessor processor);
+
+ void unregisterRequestProcessor(final String requestCode);
+
+ Pair<RequestProcessor, ExecutorService> processor(final String requestCode);
+
+ String remotingInstanceId();
+
+ RemotingCommandFactory commandFactory();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RequestProcessor.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RequestProcessor.java
new file mode 100644
index 0000000..cd201c2
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/RequestProcessor.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.remoting.api;
+
+import org.apache.rocketmq.remoting.api.channel.RemotingChannel;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public interface RequestProcessor {
+ RemotingCommand processRequest(RemotingChannel channel, RemotingCommand request);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/buffer/ByteBufferWrapper.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/buffer/ByteBufferWrapper.java
new file mode 100644
index 0000000..7360c88
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/buffer/ByteBufferWrapper.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.buffer;
+
+import java.nio.ByteBuffer;
+
+public interface ByteBufferWrapper {
+ void writeByte(byte data);
+
+ void writeByte(int index, byte data);
+
+ void writeBytes(byte[] data);
+
+ void writeBytes(ByteBuffer data);
+
+ void writeInt(int data);
+
+ void writeShort(short value);
+
+ void writeLong(long id);
+
+ byte readByte();
+
+ void readBytes(byte[] dst);
+
+ void readBytes(ByteBuffer dst);
+
+ short readShort();
+
+ int readInt();
+
+ long readLong();
+
+ int readableBytes();
+
+ int readerIndex();
+
+ void setReaderIndex(int readerIndex);
+
+ void ensureCapacity(int capacity);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChannelEventListener.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChannelEventListener.java
new file mode 100644
index 0000000..0c0afcf
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChannelEventListener.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.remoting.api.channel;
+
+public interface ChannelEventListener {
+ void onChannelConnect(final RemotingChannel channel);
+
+ void onChannelClose(final RemotingChannel channel);
+
+ void onChannelException(final RemotingChannel channel);
+
+ void onChannelIdle(final RemotingChannel channel);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChannelHandlerContextWrapper.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChannelHandlerContextWrapper.java
new file mode 100644
index 0000000..05c3b18
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChannelHandlerContextWrapper.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.remoting.api.channel;
+
+public interface ChannelHandlerContextWrapper<T> {
+ T getContext();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChunkRegion.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChunkRegion.java
new file mode 100644
index 0000000..8266ff0
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/ChunkRegion.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.remoting.api.channel;
+
+import java.io.IOException;
+import java.nio.channels.WritableByteChannel;
+
+public interface ChunkRegion {
+ void release();
+
+ /**
+ * @return Returns the offset in the file where the transfer began.
+ */
+ long position();
+
+ /**
+ * @return Return the bytes which was transferred already
+ */
+ long transferred();
+
+ /**
+ * @return Returns the number of bytes to transfer.
+ */
+ long count();
+
+ /**
+ * Transfers the content of this file region to the specified channel.
+ *
+ * @param target the destination of the transfer
+ * @param position the relative offset of the file where the transfer begins
+ * from. For example, <tt>0</tt> will make the transfer start
+ * from {@link #position()}th byte and
+ * <tt>{@link #count()} - 1</tt> will make the last byte of the
+ * region transferred.
+ * @return the length of the transferred file region
+ * @throws IOException IOException
+ */
+ long transferTo(WritableByteChannel target, long position) throws IOException;
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/RemotingChannel.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/RemotingChannel.java
new file mode 100644
index 0000000..28cfc53
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/channel/RemotingChannel.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.channel;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public interface RemotingChannel {
+ /**
+ * Returns the local address where this {@code RemotingChannel} is bound to. The returned
+ * {@link SocketAddress} is supposed to be down-cast into more concrete
+ * type such as {@link InetSocketAddress} to retrieve the detailed
+ * information.
+ *
+ * @return the local address of this channel.
+ * {@code null} if this channel is not bound.
+ */
+ SocketAddress localAddress();
+
+ /**
+ * Returns the remote address where this {@code RemotingChannel} is connected to. The
+ * returned {@link SocketAddress} is supposed to be down-cast into more
+ * concrete type such as {@link InetSocketAddress} to retrieve the detailed
+ * information.
+ *
+ * @return the remote address of this channel.
+ * {@code null} if this channel is not connected.
+ */
+ SocketAddress remoteAddress();
+
+ /**
+ * Returns {@code true} if and only if the I/O thread will perform the
+ * requested write operation immediately. Any write requests made when
+ * this method returns {@code false} are queued until the I/O thread is
+ * ready to process the queued write requests.
+ */
+ boolean isWritable();
+
+ /**
+ * Returns {@code true} if the {@code RemotingChannel} is active and so connected.
+ */
+ boolean isActive();
+
+ /**
+ * Requests to close the {@code RemotingChannel} immediately.
+ */
+ void close();
+
+ /**
+ * Writes a response {@code RemotingCommand} to remote.
+ *
+ * @param command the response command
+ */
+ void reply(RemotingCommand command);
+
+ /**
+ * Writes a response {@code ChunkRegion} to remote.
+ *
+ * @param fileRegion the response chunk file region
+ */
+ void reply(ChunkRegion fileRegion);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/RemotingCommand.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/RemotingCommand.java
new file mode 100644
index 0000000..f21a45d
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/RemotingCommand.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.command;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+import org.apache.rocketmq.remoting.common.TypePresentation;
+
+public interface RemotingCommand {
+ byte protocolType();
+
+ void protocolType(byte value);
+
+ int requestID();
+
+ void requestID(int value);
+
+ byte serializerType();
+
+ void serializerType(byte value);
+
+ TrafficType trafficType();
+
+ void trafficType(TrafficType value);
+
+ String opCode();
+
+ void opCode(String value);
+
+ String remark();
+
+ void remark(String value);
+
+ Map<String, String> properties();
+
+ void properties(Map<String, String> value);
+
+ String property(String key);
+
+ void property(String key, String value);
+
+ Object parameter();
+
+ void parameter(Object value);
+
+ byte[] parameterBytes();
+
+ void parameterBytes(byte[] value);
+
+ byte[] extraPayload();
+
+ void extraPayload(byte[] value);
+
+ <T> T parameter(final SerializerFactory serializerFactory, Class<T> c);
+
+ <T> T parameter(final SerializerFactory serializerFactory, final TypePresentation<T> typePresentation);
+
+ <T> T parameter(final SerializerFactory serializerFactory, final Type type);
+
+ enum CommandFlag {
+ SUCCESS("0"),
+ ERROR("-1");
+
+ private String flag;
+
+ CommandFlag(final String flag) {
+ this.flag = flag;
+ }
+
+ public String flag() {
+ return flag;
+ }
+ }
+}
\ No newline at end of file
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/RemotingCommandFactory.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/RemotingCommandFactory.java
new file mode 100644
index 0000000..82a5c6a
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/RemotingCommandFactory.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.remoting.api.command;
+
+public interface RemotingCommandFactory {
+ RemotingCommand createRequest();
+
+ RemotingCommand createResponse(RemotingCommand command);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/TrafficType.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/TrafficType.java
new file mode 100644
index 0000000..efebfe7
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/command/TrafficType.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.remoting.api.command;
+
+public enum TrafficType {
+ REQUEST_SYNC,
+ REQUEST_ASYNC,
+ REQUEST_ONEWAY,
+ RESPONSE;
+
+ public static TrafficType parse(int index) {
+ switch (index) {
+ case 0:
+ return REQUEST_SYNC;
+ case 1:
+ return REQUEST_ASYNC;
+ case 2:
+ return REQUEST_ONEWAY;
+ case 3:
+ return RESPONSE;
+ default:
+ throw new IllegalArgumentException("Not supported " + index);
+ }
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/compressable/Compressor.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/compressable/Compressor.java
new file mode 100644
index 0000000..4688c45
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/compressable/Compressor.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.remoting.api.compressable;
+
+public interface Compressor {
+ String name();
+
+ byte type();
+
+ byte[] compress(final byte[] content) throws Exception;
+
+ byte[] deCompress(final byte[] content) throws Exception;
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/compressable/CompressorFactory.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/compressable/CompressorFactory.java
new file mode 100644
index 0000000..2494c78
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/compressable/CompressorFactory.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.remoting.api.compressable;
+
+public interface CompressorFactory {
+ void register(Compressor compressor);
+
+ byte type(String compressionName);
+
+ Compressor get(byte type);
+
+ void clearAll();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/NestedRuntimeException.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/NestedRuntimeException.java
new file mode 100644
index 0000000..7ef01db
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/NestedRuntimeException.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.remoting.api.exception;
+
+/**
+ * Handy class for wrapping runtime {@code Exceptions} with a root cause.
+ *
+ * <p>This class is {@code abstract} to force the programmer to extend
+ * the class. {@code getMessage} will include nested exception
+ * information; {@code getRootCause} will include the innermost cause of
+ * this exception, if any; {@code printStackTrace} and other like methods will
+ * delegate to the wrapped exception, if any.
+ *
+ * @since 1.0.0
+ */
+public abstract class NestedRuntimeException extends RuntimeException {
+ private static final long serialVersionUID = -8371779880133933367L;
+
+ /**
+ * Construct a {@code NestedRuntimeException} with the specified detail message.
+ *
+ * @param msg the detail message
+ */
+ public NestedRuntimeException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct a {@code NestedRuntimeException} with the specified detail message
+ * and nested exception.
+ *
+ * @param msg the detail message
+ * @param cause the nested exception
+ */
+ public NestedRuntimeException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * Build a message for the given base message and root cause.
+ *
+ * @param message the base message
+ * @param cause the root cause
+ * @return the full exception message
+ */
+ private static String getMessageWithCause(String message, Throwable cause) {
+ if (cause != null) {
+ StringBuilder sb = new StringBuilder();
+ if (message != null) {
+ sb.append(message).append("; ");
+ }
+ sb.append("nested exception is ").append(cause);
+ return sb.toString();
+ } else {
+ return message;
+ }
+ }
+
+ /**
+ * Return the detail message, including the message from the nested exception
+ * if there is one.
+ */
+ @Override
+ public String getMessage() {
+ return getMessageWithCause(super.getMessage(), getCause());
+ }
+
+ /**
+ * Retrieve the innermost cause of this exception, if any.
+ *
+ * @return the innermost exception, or {@code null} if none
+ */
+ public Throwable getRootCause() {
+ Throwable rootCause = null;
+ Throwable cause = getCause();
+ while (cause != null && cause != rootCause) {
+ rootCause = cause;
+ cause = cause.getCause();
+ }
+ return rootCause;
+ }
+
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteAccessException.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteAccessException.java
new file mode 100644
index 0000000..6ce6dd4
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteAccessException.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.exception;
+
+/**
+ * Generic remote access exception. A service proxy for any remoting
+ * protocol should throw this exception or subclasses of it, in order
+ * to transparently expose a plain Java business interface.
+ *
+ * <p>A client may catch RemoteAccessException if it wants to, but as
+ * remote access errors are typically unrecoverable, it will probably let
+ * such exceptions propagate to a higher level that handles them generically.
+ * In this case, the client opCode doesn't show any signs of being involved in
+ * remote access, as there aren't any remoting-specific dependencies.
+ *
+ * @since 1.0.0
+ */
+public class RemoteAccessException extends NestedRuntimeException {
+ private static final long serialVersionUID = 6280428909532427263L;
+
+ /**
+ * Constructor for RemoteAccessException with the specified detail message.
+ *
+ * @param msg the detail message
+ */
+ public RemoteAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for RemoteAccessException with the specified detail message
+ * and nested exception.
+ *
+ * @param msg the detail message
+ * @param cause the root cause (usually from using an underlying
+ * remoting API such as RMI)
+ */
+ public RemoteAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteCodecException.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteCodecException.java
new file mode 100644
index 0000000..a8b9e4e
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteCodecException.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.remoting.api.exception;
+
+/**
+ * @since 1.0.0
+ */
+public class RemoteCodecException extends RemoteAccessException {
+ private static final long serialVersionUID = -7597014042746200543L;
+
+ public RemoteCodecException(String msg) {
+ super(msg);
+ }
+
+ public RemoteCodecException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteConnectFailureException.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteConnectFailureException.java
new file mode 100644
index 0000000..af0a6e9
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteConnectFailureException.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.exception;
+
+/**
+ * RemoteConnectFailureException will be thrown when connection
+ * could not be established with a remote service.
+ *
+ * @since 1.0.0
+ */
+public class RemoteConnectFailureException extends RemoteAccessException {
+ private static final long serialVersionUID = -5565366231695911316L;
+
+ /**
+ * Constructor for RemoteConnectFailureException with the specified detail message
+ * and nested exception.
+ *
+ * @param msg the detail message
+ * @param cause the root cause from the remoting API in use
+ */
+ public RemoteConnectFailureException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * Constructor for RemoteConnectFailureException with the specified detail message.
+ *
+ * @param msg the detail message
+ */
+ public RemoteConnectFailureException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteTimeoutException.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteTimeoutException.java
new file mode 100644
index 0000000..adfcc8d
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/exception/RemoteTimeoutException.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.remoting.api.exception;
+
+/**
+ * RemoteTimeoutException will be thrown when the execution
+ * of the target method did not complete before a configurable
+ * timeout, for example when a reply message was not received.
+ *
+ * @since 1.0.0
+ */
+public class RemoteTimeoutException extends RemoteAccessException {
+ private static final long serialVersionUID = 8710772392914461626L;
+
+ /**
+ * Constructor for RemoteTimeoutException with the specified detail message,configurable timeout.
+ *
+ * @param msg the detail message
+ * @param timeoutMillis configurable timeout
+ */
+ public RemoteTimeoutException(String msg, long timeoutMillis) {
+ this(msg, timeoutMillis, null);
+ }
+
+ /**
+ * Constructor for RemoteTimeoutException with the specified detail message,configurable timeout
+ * and nested exception..
+ *
+ * @param msg the detail message
+ * @param timeoutMillis configurable timeout
+ * @param cause Exception cause
+ */
+ public RemoteTimeoutException(String msg, long timeoutMillis, Throwable cause) {
+ super(String.format("%s, waiting for %s ms", msg, timeoutMillis), cause);
+ }
+
+ /**
+ * Constructor for RemoteTimeoutException with the specified detail message.
+ *
+ * @param msg the detail message
+ */
+ public RemoteTimeoutException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for RemoteTimeoutException with the specified detail message
+ * and nested exception.
+ *
+ * @param msg the detail message
+ * @param cause the root cause from the remoting API in use
+ */
+ public RemoteTimeoutException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/ExceptionContext.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/ExceptionContext.java
new file mode 100644
index 0000000..2452309
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/ExceptionContext.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.remoting.api.interceptor;
+
+import org.apache.rocketmq.remoting.api.RemotingEndPoint;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public class ExceptionContext extends RequestContext {
+ private Throwable exception;
+ private String remark;
+
+ public ExceptionContext(RemotingEndPoint remotingEndPoint, String remoteAddr, RemotingCommand request,
+ Throwable exception, String remark) {
+ super(remotingEndPoint, remoteAddr, request);
+ this.remotingEndPoint = remotingEndPoint;
+ this.remoteAddr = remoteAddr;
+ this.request = request;
+ this.exception = exception;
+ this.remark = remark;
+ }
+
+ public RemotingEndPoint getRemotingEndPoint() {
+ return remotingEndPoint;
+ }
+
+ public void setRemotingEndPoint(RemotingEndPoint remotingEndPoint) {
+ this.remotingEndPoint = remotingEndPoint;
+ }
+
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ public RemotingCommand getRequest() {
+ return request;
+ }
+
+ public void setRequest(RemotingCommand request) {
+ this.request = request;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public void setException(Throwable exception) {
+ this.exception = exception;
+ }
+
+ public String getRemark() {
+ return remark;
+ }
+
+ public void setRemark(String remark) {
+ this.remark = remark;
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/Interceptor.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/Interceptor.java
new file mode 100644
index 0000000..62257ef
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/Interceptor.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.remoting.api.interceptor;
+
+public interface Interceptor {
+ void beforeRequest(final RequestContext context);
+
+ void afterResponseReceived(final ResponseContext context);
+
+ void onException(final ExceptionContext context);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/InterceptorGroup.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/InterceptorGroup.java
new file mode 100644
index 0000000..9ffc696
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/InterceptorGroup.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.interceptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InterceptorGroup {
+ private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
+
+ public void registerInterceptor(final Interceptor interceptor) {
+ if (interceptor != null) {
+ this.interceptors.add(interceptor);
+ }
+ }
+
+ public void beforeRequest(final RequestContext context) {
+ for (Interceptor interceptor : interceptors) {
+ interceptor.beforeRequest(context);
+ }
+ }
+
+ public void afterResponseReceived(final ResponseContext context) {
+ for (Interceptor interceptor : interceptors) {
+ interceptor.afterResponseReceived(context);
+ }
+ }
+
+ public void onException(final ExceptionContext context) {
+ for (Interceptor interceptor : interceptors) {
+ interceptor.onException(context);
+ }
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/RequestContext.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/RequestContext.java
new file mode 100644
index 0000000..d961556
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/RequestContext.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.interceptor;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.rocketmq.remoting.api.RemotingEndPoint;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public class RequestContext {
+ protected RemotingEndPoint remotingEndPoint;
+ protected String remoteAddr;
+ protected RemotingCommand request;
+
+ public RequestContext(RemotingEndPoint remotingEndPoint, String remoteAddr, RemotingCommand request) {
+ super();
+ this.remotingEndPoint = remotingEndPoint;
+ this.remoteAddr = remoteAddr;
+ this.request = request;
+ }
+
+ public RemotingEndPoint getRemotingEndPoint() {
+ return remotingEndPoint;
+ }
+
+ public void setRemotingEndPoint(RemotingEndPoint remotingEndPoint) {
+ this.remotingEndPoint = remotingEndPoint;
+ }
+
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ public RemotingCommand getRequest() {
+ return request;
+ }
+
+ public void setRequest(RemotingCommand request) {
+ this.request = request;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/ResponseContext.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/ResponseContext.java
new file mode 100644
index 0000000..97ec2e6
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/interceptor/ResponseContext.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.api.interceptor;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.rocketmq.remoting.api.RemotingEndPoint;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public class ResponseContext extends RequestContext {
+ private RemotingCommand response;
+
+ public ResponseContext(RemotingEndPoint remotingEndPoint, String remoteAddr, RemotingCommand request,
+ RemotingCommand response) {
+ super(remotingEndPoint, remoteAddr, request);
+ this.remotingEndPoint = remotingEndPoint;
+ this.remoteAddr = remoteAddr;
+ this.request = request;
+ this.response = response;
+ }
+
+ public RemotingEndPoint getRemotingEndPoint() {
+ return remotingEndPoint;
+ }
+
+ public void setRemotingEndPoint(RemotingEndPoint remotingEndPoint) {
+ this.remotingEndPoint = remotingEndPoint;
+ }
+
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ public RemotingCommand getRequest() {
+ return request;
+ }
+
+ public void setRequest(RemotingCommand request) {
+ this.request = request;
+ }
+
+ public RemotingCommand getResponse() {
+ return response;
+ }
+
+ public void setResponse(RemotingCommand response) {
+ this.response = response;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/protocol/Protocol.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/protocol/Protocol.java
new file mode 100644
index 0000000..5caf167
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/protocol/Protocol.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.remoting.api.protocol;
+
+import org.apache.rocketmq.remoting.api.channel.ChannelHandlerContextWrapper;
+
+public interface Protocol {
+ /**
+ * Minimum Viable Protocol
+ */
+ String MVP = "mvp";
+ String HTTP2 = "http2";
+ String WEBSOCKET = "websocket";
+
+ byte MVP_MAGIC = 0x14;
+ byte WEBSOCKET_MAGIC = 0x15;
+ byte HTTP_2_MAGIC = 0x16;
+
+ String name();
+
+ byte type();
+
+ void assembleHandler(ChannelHandlerContextWrapper ctx);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/protocol/ProtocolFactory.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/protocol/ProtocolFactory.java
new file mode 100644
index 0000000..cf016f9
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/protocol/ProtocolFactory.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 org.apache.rocketmq.remoting.api.protocol;
+
+public interface ProtocolFactory {
+ void register(Protocol protocol);
+
+ void resetAll(Protocol protocol);
+
+ byte type(String protocolName);
+
+ Protocol get(byte type);
+
+ void clearAll();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/serializable/Serializer.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/serializable/Serializer.java
new file mode 100644
index 0000000..8ef8dcd
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/serializable/Serializer.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.remoting.api.serializable;
+
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+import org.apache.rocketmq.remoting.common.TypePresentation;
+
+public interface Serializer {
+ String name();
+
+ byte type();
+
+ <T> T decode(final byte[] content, final Class<T> c);
+
+ <T> T decode(final byte[] content, final TypePresentation<T> typePresentation);
+
+ <T> T decode(final byte[] content, final Type type);
+
+ ByteBuffer encode(final Object object);
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/serializable/SerializerFactory.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/serializable/SerializerFactory.java
new file mode 100644
index 0000000..b47bf99
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/api/serializable/SerializerFactory.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.remoting.api.serializable;
+
+public interface SerializerFactory {
+ void register(Serializer serialization);
+
+ byte type(String serializationName);
+
+ Serializer get(byte type);
+
+ void clearAll();
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/common/Pair.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/common/Pair.java
new file mode 100644
index 0000000..505e104
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/common/Pair.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.remoting.common;
+
+public class Pair<L, R> {
+ private L left;
+ private R right;
+
+ public Pair(L left, R right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ public L getLeft() {
+ return left;
+ }
+
+ public void setLeft(L left) {
+ this.left = left;
+ }
+
+ public R getRight() {
+ return right;
+ }
+
+ public void setRight(R right) {
+ this.right = right;
+ }
+}
diff --git a/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/common/TypePresentation.java b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/common/TypePresentation.java
new file mode 100644
index 0000000..ef3d5f8
--- /dev/null
+++ b/remoting-core/remoting-api/src/main/java/org/apache/rocketmq/remoting/common/TypePresentation.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.common;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Represents a generic type {@code T}. Java doesn't yet provide a way to
+ * represent generic types, so this class does. Forces clients to create a
+ * subclass of this class which enables retrieval the type information even at
+ * runtime.
+ *
+ * <p>For example, to create a type literal for {@code List<String>}, you can
+ * create an empty anonymous inner class:
+ *
+ * <pre>
+ * TypePresentation<List<String>> list = new TypePresentation<List<String>>() {};
+ * </pre>
+ *
+ * To create a type literal for {@code Map<String, Integer>}:
+ *
+ * <pre>
+ * TypePresentation<Map<String, Integer>> map = new TypePresentation<Map<String, Integer>>() {};
+ * </pre>
+ *
+ * This syntax cannot be used to create type literals that have wildcard
+ * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
+ *
+ * @since 1.0.0
+ */
+public class TypePresentation<T> {
+ static ConcurrentMap<Class<?>, ConcurrentMap<Type, ConcurrentMap<Type, Type>>> classTypeCache
+ = new ConcurrentHashMap<Class<?>, ConcurrentMap<Type, ConcurrentMap<Type, Type>>>(16, 0.75f, 1);
+ protected final Type type;
+
+ /**
+ * Constructs a new type literal. Derives represented class from type
+ * parameter.
+ *
+ * <p>Clients create an empty anonymous subclass. Doing so embeds the type
+ * parameter in the anonymous class's type hierarchy so we can reconstitute it
+ * at runtime despite erasure.
+ */
+ protected TypePresentation() {
+ Type superClass = getClass().getGenericSuperclass();
+ type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
+ }
+
+ /**
+ * @return underlying {@code Type} instance.
+ */
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/remoting-core/remoting-impl/pom.xml b/remoting-core/remoting-impl/pom.xml
new file mode 100644
index 0000000..c8be150
--- /dev/null
+++ b/remoting-core/remoting-impl/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>rocketmq-xxx</artifactId>
+ <groupId>org.apache.rocketmq</groupId>
+ <version>0.1.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>remoting-impl</artifactId>
+
+ <properties>
+ <maven.compiler.source>1.7</maven.compiler.source>
+ <maven.compiler.target>1.7</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>remoting-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.esotericsoftware</groupId>
+ <artifactId>kryo</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.msgpack</groupId>
+ <artifactId>msgpack</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-tcnative-boringssl-static</artifactId>
+ <version>1.1.33.Fork26</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/ChannelEventListenerGroup.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/ChannelEventListenerGroup.java
new file mode 100644
index 0000000..8af61f7
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/ChannelEventListenerGroup.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.remoting.common;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.rocketmq.remoting.api.channel.ChannelEventListener;
+import org.apache.rocketmq.remoting.api.channel.RemotingChannel;
+
+public class ChannelEventListenerGroup {
+ private final List<ChannelEventListener> listenerList = new ArrayList<ChannelEventListener>();
+
+ public int size() {
+ return this.listenerList.size();
+ }
+
+ public void registerChannelEventListener(final ChannelEventListener listener) {
+ if (listener != null) {
+ this.listenerList.add(listener);
+ }
+ }
+
+ public void onChannelConnect(final RemotingChannel channel) {
+ for (ChannelEventListener listener : listenerList) {
+ listener.onChannelConnect(channel);
+ }
+ }
+
+ public void onChannelClose(final RemotingChannel channel) {
+ for (ChannelEventListener listener : listenerList) {
+ listener.onChannelClose(channel);
+ }
+ }
+
+ public void onChannelException(final RemotingChannel channel) {
+ for (ChannelEventListener listener : listenerList) {
+ listener.onChannelException(channel);
+ }
+ }
+
+ public void onChannelIdle(final RemotingChannel channel) {
+ for (ChannelEventListener listener : listenerList) {
+ listener.onChannelIdle(channel);
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/RemotingCommandFactoryMeta.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/RemotingCommandFactoryMeta.java
new file mode 100644
index 0000000..d5c0aaa
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/RemotingCommandFactoryMeta.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.common;
+
+import org.apache.rocketmq.remoting.api.protocol.ProtocolFactory;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+import org.apache.rocketmq.remoting.impl.protocol.Httpv2Protocol;
+import org.apache.rocketmq.remoting.impl.protocol.ProtocolFactoryImpl;
+import org.apache.rocketmq.remoting.impl.protocol.serializer.MsgPackSerializer;
+import org.apache.rocketmq.remoting.impl.protocol.serializer.SerializerFactoryImpl;
+
+public class RemotingCommandFactoryMeta {
+ private final ProtocolFactory protocolFactory = new ProtocolFactoryImpl();
+ private final SerializerFactory serializerFactory = new SerializerFactoryImpl();
+ private byte protocolType = Httpv2Protocol.MVP_MAGIC;
+ private byte serializeType = MsgPackSerializer.SERIALIZER_TYPE;
+
+ public RemotingCommandFactoryMeta() {
+ }
+
+ public RemotingCommandFactoryMeta(String protocolName, String serializeName) {
+ this.protocolType = protocolFactory.type(protocolName);
+ this.serializeType = serializerFactory.type(serializeName);
+ }
+
+ public byte getSerializeType() {
+ return serializeType;
+ }
+
+ public byte getProtocolType() {
+ return protocolType;
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/ResponseResult.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/ResponseResult.java
new file mode 100644
index 0000000..2557cdf
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/ResponseResult.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.common;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.rocketmq.remoting.api.AsyncHandler;
+import org.apache.rocketmq.remoting.api.RemotingEndPoint;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.interceptor.ExceptionContext;
+import org.apache.rocketmq.remoting.api.interceptor.InterceptorGroup;
+import org.apache.rocketmq.remoting.api.interceptor.ResponseContext;
+
+public class ResponseResult {
+ private final long beginTimestamp = System.currentTimeMillis();
+ private final CountDownLatch countDownLatch = new CountDownLatch(1);
+ private final AtomicBoolean interceptorExecuted = new AtomicBoolean(false);
+
+ private int requestId;
+ private long timeoutMillis;
+ private AsyncHandler asyncHandler;
+
+ private volatile RemotingCommand responseCommand;
+ private volatile boolean sendRequestOK = true;
+ private volatile Throwable cause;
+ private SemaphoreReleaseOnlyOnce once;
+
+ private RemotingCommand requestCommand;
+ private InterceptorGroup interceptorGroup;
+ private String remoteAddr;
+
+ public ResponseResult(int requestId, long timeoutMillis, AsyncHandler asyncHandler, SemaphoreReleaseOnlyOnce once) {
+ this.requestId = requestId;
+ this.timeoutMillis = timeoutMillis;
+ this.asyncHandler = asyncHandler;
+ this.once = once;
+ }
+
+ public ResponseResult(int requestId, long timeoutMillis) {
+ this.requestId = requestId;
+ this.timeoutMillis = timeoutMillis;
+ }
+
+ public void executeRequestSendFailed() {
+ if (this.interceptorExecuted.compareAndSet(false, true)) {
+ try {
+ interceptorGroup.onException(new ExceptionContext(RemotingEndPoint.REQUEST, this.remoteAddr, this.requestCommand,
+ cause, "REQUEST_SEND_FAILED"));
+ } catch (Throwable e) {
+ }
+ //Sync call
+ if (null != asyncHandler) {
+ asyncHandler.onFailure(requestCommand);
+ }
+ }
+ }
+
+ public void executeCallbackArrived(final RemotingCommand response) {
+ if (this.interceptorExecuted.compareAndSet(false, true)) {
+ try {
+ interceptorGroup.afterResponseReceived(new ResponseContext(RemotingEndPoint.REQUEST, this.remoteAddr,
+ this.requestCommand, response));
+ } catch (Throwable e) {
+ }
+ if (null != asyncHandler) {
+ asyncHandler.onSuccess(response);
+ }
+ }
+ }
+
+ public void onTimeout(long costTimeMillis, long timoutMillis) {
+ if (this.interceptorExecuted.compareAndSet(false, true)) {
+ try {
+ interceptorGroup.onException(new ExceptionContext(RemotingEndPoint.REQUEST, this.remoteAddr, this.requestCommand,
+ null, "CALLBACK_TIMEOUT"));
+ } catch (Throwable e) {
+ }
+ if (null != asyncHandler) {
+ asyncHandler.onTimeout(costTimeMillis, timoutMillis);
+ }
+ }
+ }
+
+ public void release() {
+ if (this.once != null) {
+ this.once.release();
+ }
+ }
+
+ public RemotingCommand waitResponse(final long timeoutMillis) {
+ try {
+ this.countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return this.responseCommand;
+ }
+
+ public void putResponse(final RemotingCommand responseCommand) {
+ this.responseCommand = responseCommand;
+ this.countDownLatch.countDown();
+ }
+
+ public long getBeginTimestamp() {
+ return beginTimestamp;
+ }
+
+ public boolean isSendRequestOK() {
+ return sendRequestOK;
+ }
+
+ public void setSendRequestOK(boolean sendRequestOK) {
+ this.sendRequestOK = sendRequestOK;
+ }
+
+ public long getTimeoutMillis() {
+ return timeoutMillis;
+ }
+
+ public AsyncHandler getAsyncHandler() {
+ return asyncHandler;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+
+ public void setCause(Throwable cause) {
+ this.cause = cause;
+ }
+
+ public RemotingCommand getResponseCommand() {
+ return responseCommand;
+ }
+
+ public void setResponseCommand(RemotingCommand responseCommand) {
+ this.responseCommand = responseCommand;
+ }
+
+ public int getRequestId() {
+ return requestId;
+ }
+
+ public RemotingCommand getRequestCommand() {
+ return requestCommand;
+ }
+
+ public void setRequestCommand(RemotingCommand requestCommand) {
+ this.requestCommand = requestCommand;
+ }
+
+ public InterceptorGroup getInterceptorGroup() {
+ return interceptorGroup;
+ }
+
+ public void setInterceptorGroup(InterceptorGroup interceptorGroup) {
+ this.interceptorGroup = interceptorGroup;
+ }
+
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/SemaphoreReleaseOnlyOnce.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/SemaphoreReleaseOnlyOnce.java
new file mode 100644
index 0000000..1c5849b
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/SemaphoreReleaseOnlyOnce.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.remoting.common;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class SemaphoreReleaseOnlyOnce {
+ private final AtomicBoolean released = new AtomicBoolean(false);
+ private final Semaphore semaphore;
+
+ public SemaphoreReleaseOnlyOnce(Semaphore semaphore) {
+ this.semaphore = semaphore;
+ }
+
+ public void release() {
+ if (this.released.compareAndSet(false, true)) {
+ this.semaphore.release();
+ }
+ }
+
+ public Semaphore getSemaphore() {
+ return semaphore;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/metrics/ChannelMetrics.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/metrics/ChannelMetrics.java
new file mode 100755
index 0000000..db959b7
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/common/metrics/ChannelMetrics.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.common.metrics;
+
+import io.netty.channel.group.ChannelGroup;
+
+public interface ChannelMetrics {
+
+ Integer getChannelCount();
+
+ ChannelGroup getChannels();
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/config/RemotingConfig.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/config/RemotingConfig.java
new file mode 100644
index 0000000..b330041
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/config/RemotingConfig.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.config;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.rocketmq.remoting.api.protocol.Protocol;
+import org.apache.rocketmq.remoting.impl.protocol.compression.GZipCompressor;
+import org.apache.rocketmq.remoting.impl.protocol.serializer.MsgPackSerializer;
+
+public class RemotingConfig extends TcpSocketConfig {
+ private int connectionMaxRetries = 3;
+ private int connectionChannelReaderIdleSeconds = 0;
+ private int connectionChannelWriterIdleSeconds = 0;
+ /**
+ * IdleStateEvent will be triggered when neither read nor write was
+ * performed for the specified period of this time. Specify {@code 0} to
+ * disable
+ */
+ private int connectionChannelIdleSeconds = 120;
+ private int writeBufLowWaterMark = 32 * 10240;
+ private int writeBufHighWaterMark = 64 * 10240;
+ private int threadTaskLowWaterMark = 30000;
+ private int threadTaskHighWaterMark = 50000;
+ private int connectionRetryBackoffMillis = 3000;
+ private String protocolName = Protocol.MVP;
+ private String serializerName = MsgPackSerializer.SERIALIZER_NAME;
+ private String compressorName = GZipCompressor.COMPRESSOR_NAME;
+ private int serviceThreadBlockQueueSize = 50000;
+ private boolean clientNativeEpollEnable = false;
+ private int clientWorkerThreads = 16 + Runtime.getRuntime().availableProcessors() * 2;
+ private int clientConnectionFutureAwaitTimeoutMillis = 30000;
+ private int clientAsyncCallbackExecutorThreads = 16 + Runtime.getRuntime().availableProcessors() * 2;
+ private int clientOnewayInvokeSemaphore = 20480;
+
+ //=============Server configuration==================
+ private int clientAsyncInvokeSemaphore = 20480;
+ private boolean clientPooledBytebufAllocatorEnable = false;
+ private boolean clientCloseSocketIfTimeout = true;
+ private boolean clientShortConnectionEnable = false;
+ private long clientPublishServiceTimeout = 10000;
+ private long clientConsumerServiceTimeout = 10000;
+ private long clientInvokeServiceTimeout = 10000;
+ private int clientMaxRetryCount = 10;
+ private int clientSleepBeforeRetry = 100;
+ private int serverListenPort = 8888;
+ /**
+ * If server only listened 1 port,recommend to set the value to 1
+ */
+ private int serverAcceptorThreads = 1;
+ private int serverIoThreads = 16 + Runtime.getRuntime().availableProcessors() * 2;
+ private int serverWorkerThreads = 16 + Runtime.getRuntime().availableProcessors() * 2;
+ private int serverOnewayInvokeSemaphore = 256;
+ private int serverAsyncInvokeSemaphore = 6400;
+ private boolean serverNativeEpollEnable = false;
+ private int serverAsyncCallbackExecutorThreads = Runtime.getRuntime().availableProcessors() * 2;
+ private boolean serverPooledBytebufAllocatorEnable = true;
+ private boolean serverAuthOpenEnable = true;
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+
+ public int getConnectionMaxRetries() {
+ return connectionMaxRetries;
+ }
+
+ public void setConnectionMaxRetries(final int connectionMaxRetries) {
+ this.connectionMaxRetries = connectionMaxRetries;
+ }
+
+ public int getConnectionChannelReaderIdleSeconds() {
+ return connectionChannelReaderIdleSeconds;
+ }
+
+ public void setConnectionChannelReaderIdleSeconds(final int connectionChannelReaderIdleSeconds) {
+ this.connectionChannelReaderIdleSeconds = connectionChannelReaderIdleSeconds;
+ }
+
+ public int getConnectionChannelWriterIdleSeconds() {
+ return connectionChannelWriterIdleSeconds;
+ }
+
+ public void setConnectionChannelWriterIdleSeconds(final int connectionChannelWriterIdleSeconds) {
+ this.connectionChannelWriterIdleSeconds = connectionChannelWriterIdleSeconds;
+ }
+
+ public int getConnectionChannelIdleSeconds() {
+ return connectionChannelIdleSeconds;
+ }
+
+ public void setConnectionChannelIdleSeconds(final int connectionChannelIdleSeconds) {
+ this.connectionChannelIdleSeconds = connectionChannelIdleSeconds;
+ }
+
+ public int getWriteBufLowWaterMark() {
+ return writeBufLowWaterMark;
+ }
+
+ public void setWriteBufLowWaterMark(final int writeBufLowWaterMark) {
+ this.writeBufLowWaterMark = writeBufLowWaterMark;
+ }
+
+ public int getWriteBufHighWaterMark() {
+ return writeBufHighWaterMark;
+ }
+
+ public void setWriteBufHighWaterMark(final int writeBufHighWaterMark) {
+ this.writeBufHighWaterMark = writeBufHighWaterMark;
+ }
+
+ public int getThreadTaskLowWaterMark() {
+ return threadTaskLowWaterMark;
+ }
+
+ public void setThreadTaskLowWaterMark(final int threadTaskLowWaterMark) {
+ this.threadTaskLowWaterMark = threadTaskLowWaterMark;
+ }
+
+ public int getThreadTaskHighWaterMark() {
+ return threadTaskHighWaterMark;
+ }
+
+ public void setThreadTaskHighWaterMark(final int threadTaskHighWaterMark) {
+ this.threadTaskHighWaterMark = threadTaskHighWaterMark;
+ }
+
+ public int getConnectionRetryBackoffMillis() {
+ return connectionRetryBackoffMillis;
+ }
+
+ public void setConnectionRetryBackoffMillis(final int connectionRetryBackoffMillis) {
+ this.connectionRetryBackoffMillis = connectionRetryBackoffMillis;
+ }
+
+ public String getProtocolName() {
+ return protocolName;
+ }
+
+ public void setProtocolName(final String protocolName) {
+ this.protocolName = protocolName;
+ }
+
+ public String getSerializerName() {
+ return serializerName;
+ }
+
+ public void setSerializerName(final String serializerName) {
+ this.serializerName = serializerName;
+ }
+
+ public String getCompressorName() {
+ return compressorName;
+ }
+
+ public void setCompressorName(final String compressorName) {
+ this.compressorName = compressorName;
+ }
+
+ public int getServiceThreadBlockQueueSize() {
+ return serviceThreadBlockQueueSize;
+ }
+
+ public void setServiceThreadBlockQueueSize(final int serviceThreadBlockQueueSize) {
+ this.serviceThreadBlockQueueSize = serviceThreadBlockQueueSize;
+ }
+
+ public boolean isClientNativeEpollEnable() {
+ return clientNativeEpollEnable;
+ }
+
+ public void setClientNativeEpollEnable(final boolean clientNativeEpollEnable) {
+ this.clientNativeEpollEnable = clientNativeEpollEnable;
+ }
+
+ public int getClientWorkerThreads() {
+ return clientWorkerThreads;
+ }
+
+ public void setClientWorkerThreads(final int clientWorkerThreads) {
+ this.clientWorkerThreads = clientWorkerThreads;
+ }
+
+ public int getClientConnectionFutureAwaitTimeoutMillis() {
+ return clientConnectionFutureAwaitTimeoutMillis;
+ }
+
+ public void setClientConnectionFutureAwaitTimeoutMillis(final int clientConnectionFutureAwaitTimeoutMillis) {
+ this.clientConnectionFutureAwaitTimeoutMillis = clientConnectionFutureAwaitTimeoutMillis;
+ }
+
+ public int getClientAsyncCallbackExecutorThreads() {
+ return clientAsyncCallbackExecutorThreads;
+ }
+
+ public void setClientAsyncCallbackExecutorThreads(final int clientAsyncCallbackExecutorThreads) {
+ this.clientAsyncCallbackExecutorThreads = clientAsyncCallbackExecutorThreads;
+ }
+
+ public int getClientOnewayInvokeSemaphore() {
+ return clientOnewayInvokeSemaphore;
+ }
+
+ public void setClientOnewayInvokeSemaphore(final int clientOnewayInvokeSemaphore) {
+ this.clientOnewayInvokeSemaphore = clientOnewayInvokeSemaphore;
+ }
+
+ public int getClientAsyncInvokeSemaphore() {
+ return clientAsyncInvokeSemaphore;
+ }
+
+ public void setClientAsyncInvokeSemaphore(final int clientAsyncInvokeSemaphore) {
+ this.clientAsyncInvokeSemaphore = clientAsyncInvokeSemaphore;
+ }
+
+ public boolean isClientPooledBytebufAllocatorEnable() {
+ return clientPooledBytebufAllocatorEnable;
+ }
+
+ public void setClientPooledBytebufAllocatorEnable(final boolean clientPooledBytebufAllocatorEnable) {
+ this.clientPooledBytebufAllocatorEnable = clientPooledBytebufAllocatorEnable;
+ }
+
+ public boolean isClientCloseSocketIfTimeout() {
+ return clientCloseSocketIfTimeout;
+ }
+
+ public void setClientCloseSocketIfTimeout(final boolean clientCloseSocketIfTimeout) {
+ this.clientCloseSocketIfTimeout = clientCloseSocketIfTimeout;
+ }
+
+ public boolean isClientShortConnectionEnable() {
+ return clientShortConnectionEnable;
+ }
+
+ public void setClientShortConnectionEnable(final boolean clientShortConnectionEnable) {
+ this.clientShortConnectionEnable = clientShortConnectionEnable;
+ }
+
+ public long getClientPublishServiceTimeout() {
+ return clientPublishServiceTimeout;
+ }
+
+ public void setClientPublishServiceTimeout(final long clientPublishServiceTimeout) {
+ this.clientPublishServiceTimeout = clientPublishServiceTimeout;
+ }
+
+ public long getClientConsumerServiceTimeout() {
+ return clientConsumerServiceTimeout;
+ }
+
+ public void setClientConsumerServiceTimeout(final long clientConsumerServiceTimeout) {
+ this.clientConsumerServiceTimeout = clientConsumerServiceTimeout;
+ }
+
+ public long getClientInvokeServiceTimeout() {
+ return clientInvokeServiceTimeout;
+ }
+
+ public void setClientInvokeServiceTimeout(final long clientInvokeServiceTimeout) {
+ this.clientInvokeServiceTimeout = clientInvokeServiceTimeout;
+ }
+
+ public int getClientMaxRetryCount() {
+ return clientMaxRetryCount;
+ }
+
+ public void setClientMaxRetryCount(final int clientMaxRetryCount) {
+ this.clientMaxRetryCount = clientMaxRetryCount;
+ }
+
+ public int getClientSleepBeforeRetry() {
+ return clientSleepBeforeRetry;
+ }
+
+ public void setClientSleepBeforeRetry(final int clientSleepBeforeRetry) {
+ this.clientSleepBeforeRetry = clientSleepBeforeRetry;
+ }
+
+ public int getServerListenPort() {
+ return serverListenPort;
+ }
+
+ public void setServerListenPort(final int serverListenPort) {
+ this.serverListenPort = serverListenPort;
+ }
+
+ public int getServerAcceptorThreads() {
+ return serverAcceptorThreads;
+ }
+
+ public void setServerAcceptorThreads(final int serverAcceptorThreads) {
+ this.serverAcceptorThreads = serverAcceptorThreads;
+ }
+
+ public int getServerIoThreads() {
+ return serverIoThreads;
+ }
+
+ public void setServerIoThreads(final int serverIoThreads) {
+ this.serverIoThreads = serverIoThreads;
+ }
+
+ public int getServerWorkerThreads() {
+ return serverWorkerThreads;
+ }
+
+ public void setServerWorkerThreads(final int serverWorkerThreads) {
+ this.serverWorkerThreads = serverWorkerThreads;
+ }
+
+ public int getServerOnewayInvokeSemaphore() {
+ return serverOnewayInvokeSemaphore;
+ }
+
+ public void setServerOnewayInvokeSemaphore(final int serverOnewayInvokeSemaphore) {
+ this.serverOnewayInvokeSemaphore = serverOnewayInvokeSemaphore;
+ }
+
+ public int getServerAsyncInvokeSemaphore() {
+ return serverAsyncInvokeSemaphore;
+ }
+
+ public void setServerAsyncInvokeSemaphore(final int serverAsyncInvokeSemaphore) {
+ this.serverAsyncInvokeSemaphore = serverAsyncInvokeSemaphore;
+ }
+
+ public boolean isServerNativeEpollEnable() {
+ return serverNativeEpollEnable;
+ }
+
+ public void setServerNativeEpollEnable(final boolean serverNativeEpollEnable) {
+ this.serverNativeEpollEnable = serverNativeEpollEnable;
+ }
+
+ public int getServerAsyncCallbackExecutorThreads() {
+ return serverAsyncCallbackExecutorThreads;
+ }
+
+ public void setServerAsyncCallbackExecutorThreads(final int serverAsyncCallbackExecutorThreads) {
+ this.serverAsyncCallbackExecutorThreads = serverAsyncCallbackExecutorThreads;
+ }
+
+ public boolean isServerPooledBytebufAllocatorEnable() {
+ return serverPooledBytebufAllocatorEnable;
+ }
+
+ public void setServerPooledBytebufAllocatorEnable(final boolean serverPooledBytebufAllocatorEnable) {
+ this.serverPooledBytebufAllocatorEnable = serverPooledBytebufAllocatorEnable;
+ }
+
+ public boolean isServerAuthOpenEnable() {
+ return serverAuthOpenEnable;
+ }
+
+ public void setServerAuthOpenEnable(final boolean serverAuthOpenEnable) {
+ this.serverAuthOpenEnable = serverAuthOpenEnable;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/config/TcpSocketConfig.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/config/TcpSocketConfig.java
new file mode 100755
index 0000000..4dfcde7
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/config/TcpSocketConfig.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.remoting.config;
+
+/**
+ * TCP socket configuration
+ *
+ * @see java.net.SocketOptions
+ */
+public class TcpSocketConfig {
+ private boolean tcpSoReuseAddress;
+ private boolean tcpSoKeepAlive;
+ private boolean tcpSoNoDelay;
+ private int tcpSoSndBufSize; // see /proc/sys/net/ipv4/tcp_rmem
+ private int tcpSoRcvBufSize; // see /proc/sys/net/ipv4/tcp_wmem
+ private int tcpSoBacklogSize;
+ private int tcpSoLinger;
+ private int tcpSoTimeout;
+
+ public boolean isTcpSoReuseAddress() {
+ return tcpSoReuseAddress;
+ }
+
+ public void setTcpSoReuseAddress(final boolean tcpSoReuseAddress) {
+ this.tcpSoReuseAddress = tcpSoReuseAddress;
+ }
+
+ public boolean isTcpSoKeepAlive() {
+ return tcpSoKeepAlive;
+ }
+
+ public void setTcpSoKeepAlive(final boolean tcpSoKeepAlive) {
+ this.tcpSoKeepAlive = tcpSoKeepAlive;
+ }
+
+ public boolean isTcpSoNoDelay() {
+ return tcpSoNoDelay;
+ }
+
+ public void setTcpSoNoDelay(final boolean tcpSoNoDelay) {
+ this.tcpSoNoDelay = tcpSoNoDelay;
+ }
+
+ public int getTcpSoSndBufSize() {
+ return tcpSoSndBufSize;
+ }
+
+ public void setTcpSoSndBufSize(final int tcpSoSndBufSize) {
+ this.tcpSoSndBufSize = tcpSoSndBufSize;
+ }
+
+ public int getTcpSoRcvBufSize() {
+ return tcpSoRcvBufSize;
+ }
+
+ public void setTcpSoRcvBufSize(final int tcpSoRcvBufSize) {
+ this.tcpSoRcvBufSize = tcpSoRcvBufSize;
+ }
+
+ public int getTcpSoBacklogSize() {
+ return tcpSoBacklogSize;
+ }
+
+ public void setTcpSoBacklogSize(final int tcpSoBacklogSize) {
+ this.tcpSoBacklogSize = tcpSoBacklogSize;
+ }
+
+ public int getTcpSoLinger() {
+ return tcpSoLinger;
+ }
+
+ public void setTcpSoLinger(final int tcpSoLinger) {
+ this.tcpSoLinger = tcpSoLinger;
+ }
+
+ public int getTcpSoTimeout() {
+ return tcpSoTimeout;
+ }
+
+ public void setTcpSoTimeout(final int tcpSoTimeout) {
+ this.tcpSoTimeout = tcpSoTimeout;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/external/ThreadUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/external/ThreadUtils.java
new file mode 100644
index 0000000..5a50089
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/external/ThreadUtils.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 org.apache.rocketmq.remoting.external;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ThreadUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(ThreadUtils.class);
+
+ /**
+ * A constructor to stop this class being constructed.
+ */
+ private ThreadUtils() {
+ // Unused
+
+ }
+
+ public static ExecutorService newThreadPoolExecutor(int corePoolSize,
+ int maximumPoolSize,
+ long keepAliveTime,
+ TimeUnit unit,
+ BlockingQueue<Runnable> workQueue,
+ String processName, boolean isDaemon) {
+ return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, newGenericThreadFactory(processName, isDaemon));
+ }
+
+ public static ExecutorService newFixedThreadPool(int nThreads, int workQueueCapacity, String processName, boolean isDaemon) {
+ return new ThreadPoolExecutor(
+ nThreads,
+ nThreads,
+ 0,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>(workQueueCapacity),
+ newGenericThreadFactory(processName, isDaemon));
+ }
+
+ public static ExecutorService newSingleThreadExecutor(String processName, boolean isDaemon) {
+ return Executors.newSingleThreadExecutor(newGenericThreadFactory(processName, isDaemon));
+ }
+
+ public static ScheduledExecutorService newSingleThreadScheduledExecutor(String processName, boolean isDaemon) {
+ return Executors.newSingleThreadScheduledExecutor(newGenericThreadFactory(processName, isDaemon));
+ }
+
+ public static ScheduledExecutorService newFixedThreadScheduledPool(int nThreads, String processName,
+ boolean isDaemon) {
+ return Executors.newScheduledThreadPool(nThreads, newGenericThreadFactory(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();
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/buffer/NettyByteBufferWrapper.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/buffer/NettyByteBufferWrapper.java
new file mode 100644
index 0000000..5a71452
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/buffer/NettyByteBufferWrapper.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.buffer;
+
+import io.netty.buffer.ByteBuf;
+import java.nio.ByteBuffer;
+import org.apache.rocketmq.remoting.api.buffer.ByteBufferWrapper;
+
+public class NettyByteBufferWrapper implements ByteBufferWrapper {
+ private final ByteBuf buffer;
+
+ public NettyByteBufferWrapper(ByteBuf buffer) {
+ this.buffer = buffer;
+ }
+
+ @Override
+ public void writeByte(byte data) {
+ buffer.writeByte(data);
+ }
+
+ @Override
+ public void writeByte(int index, byte data) {
+ buffer.writeByte(data);
+ }
+
+ @Override
+ public void writeBytes(byte[] data) {
+ buffer.writeBytes(data);
+ }
+
+ @Override
+ public void writeBytes(final ByteBuffer data) {
+ buffer.writeBytes(data);
+ }
+
+ @Override
+ public void writeShort(final short value) {
+ buffer.writeShort(value);
+ }
+
+ @Override
+ public void writeInt(int data) {
+ buffer.writeInt(data);
+ }
+
+ @Override
+ public void writeLong(long value) {
+ buffer.writeLong(value);
+ }
+
+ @Override
+ public byte readByte() {
+ return buffer.readByte();
+ }
+
+ @Override
+ public void readBytes(final ByteBuffer dst) {
+ buffer.readBytes(dst);
+ }
+
+ @Override
+ public void readBytes(byte[] dst) {
+ buffer.readBytes(dst);
+ }
+
+ @Override
+ public short readShort() {
+ return buffer.readShort();
+ }
+
+ @Override
+ public int readInt() {
+ return buffer.readInt();
+ }
+
+ @Override
+ public long readLong() {
+ return buffer.readLong();
+ }
+
+ @Override
+ public int readableBytes() {
+ return buffer.readableBytes();
+ }
+
+ @Override
+ public int readerIndex() {
+ return buffer.readerIndex();
+ }
+
+ @Override
+ public void setReaderIndex(int index) {
+ buffer.setIndex(index, buffer.writerIndex());
+ }
+
+ @Override
+ public void ensureCapacity(int capacity) {
+ buffer.capacity(capacity);
+ }
+}
+
+
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/ChannelHandlerContextWrapperImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/ChannelHandlerContextWrapperImpl.java
new file mode 100644
index 0000000..bbd33ea
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/ChannelHandlerContextWrapperImpl.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.remoting.impl.channel;
+
+import org.apache.rocketmq.remoting.api.channel.ChannelHandlerContextWrapper;
+
+public class ChannelHandlerContextWrapperImpl<ChannelHandlerContext> implements ChannelHandlerContextWrapper {
+
+ private io.netty.channel.ChannelHandlerContext context;
+
+ public ChannelHandlerContextWrapperImpl(io.netty.channel.ChannelHandlerContext context) {
+ this.context = context;
+ }
+
+ public io.netty.channel.ChannelHandlerContext getContext() {
+ return context;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/FileRegionImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/FileRegionImpl.java
new file mode 100644
index 0000000..b90afc1
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/FileRegionImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.channel;
+
+import io.netty.channel.FileRegion;
+import io.netty.util.AbstractReferenceCounted;
+import java.io.IOException;
+import java.nio.channels.WritableByteChannel;
+import org.apache.rocketmq.remoting.api.channel.ChunkRegion;
+
+public class FileRegionImpl extends AbstractReferenceCounted implements FileRegion {
+ private final ChunkRegion chunkRegion;
+
+ public FileRegionImpl(ChunkRegion chunkRegion) {
+ this.chunkRegion = chunkRegion;
+ }
+
+ @Override
+ public long position() {
+ return chunkRegion.position();
+ }
+
+ @Override
+ public long transfered() {
+ return chunkRegion.transferred();
+ }
+
+ @Override
+ public long transferred() {
+ return chunkRegion.transferred();
+ }
+
+ @Override
+ public long count() {
+ return chunkRegion.count();
+ }
+
+ @Override
+ public long transferTo(WritableByteChannel target, long position) throws IOException {
+ return chunkRegion.transferTo(target, position);
+ }
+
+ @Override
+ protected void deallocate() {
+ chunkRegion.release();
+ }
+
+ @Override
+ public FileRegion retain() {
+ super.retain();
+ return this;
+ }
+
+ @Override
+ public FileRegion retain(int increment) {
+ super.retain(increment);
+ return this;
+ }
+
+ @Override
+ public FileRegion touch() {
+ return this;
+ }
+
+ @Override
+ public FileRegion touch(Object hint) {
+ return this;
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/NettyChannelImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/NettyChannelImpl.java
new file mode 100644
index 0000000..ba4a969
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/channel/NettyChannelImpl.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.remoting.impl.channel;
+
+import io.netty.channel.Channel;
+import java.net.SocketAddress;
+import org.apache.rocketmq.remoting.api.channel.ChunkRegion;
+import org.apache.rocketmq.remoting.api.channel.RemotingChannel;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+
+public class NettyChannelImpl implements RemotingChannel {
+ private final io.netty.channel.Channel channel;
+
+ public NettyChannelImpl(Channel channel) {
+ this.channel = channel;
+ }
+
+ @Override
+ public SocketAddress localAddress() {
+ return channel.localAddress();
+ }
+
+ @Override
+ public SocketAddress remoteAddress() {
+ return channel.remoteAddress();
+ }
+
+ @Override
+ public boolean isWritable() {
+ return channel.isWritable();
+ }
+
+ @Override
+ public boolean isActive() {
+ return channel.isActive();
+ }
+
+ @Override
+ public void close() {
+ channel.close();
+ }
+
+ @Override
+ public void reply(final RemotingCommand command) {
+ channel.writeAndFlush(command);
+ }
+
+ @Override
+ public void reply(final ChunkRegion fileRegion) {
+ channel.writeAndFlush(fileRegion);
+ }
+
+ public io.netty.channel.Channel getChannel() {
+ return channel;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ final NettyChannelImpl that = (NettyChannelImpl) o;
+
+ return channel != null ? channel.equals(that.channel) : that.channel == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ return channel != null ? channel.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "NettyChannelImpl [channel=" + channel + "]";
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/CodecHelper.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/CodecHelper.java
new file mode 100644
index 0000000..44d4fd9
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/CodecHelper.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.command;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Map.Entry;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.command.TrafficType;
+import org.apache.rocketmq.remoting.api.exception.RemoteCodecException;
+
+public class CodecHelper {
+ //ProtocolType + TotalLength + RequestId + SerializeType + TrafficType + CodeLength + RemarkLength + PropertiesSize + ParameterLength
+ public final static int MIN_PROTOCOL_LEN = 1 + 4 + 4 + 1 + 1 + 2 + 2 + 2 + 4;
+ public final static char PROPERTY_SEPARATOR = '\n';
+ public final static Charset REMOTING_CHARSET = Charset.forName("UTF-8");
+
+ public final static int CODE_MAX_LEN = 512;
+ public final static int PARAMETER_MAX_LEN = 33554432;
+ public final static int BODY_MAX_LEN = 33554432;
+ public final static int PACKET_MAX_LEN = 33554432;
+
+ public static ByteBuffer encodeHeader(final RemotingCommand command, final int parameterLength,
+ final int extraPayload) {
+ byte[] code = command.opCode().getBytes(REMOTING_CHARSET);
+ int codeLength = code.length;
+
+ byte[] remark = command.remark().getBytes(REMOTING_CHARSET);
+ int remarkLength = remark.length;
+
+ byte[][] props = null;
+ int propsLength = 0;
+ StringBuilder sb = new StringBuilder();
+ if (!command.properties().isEmpty()) {
+ props = new byte[command.properties().size()][];
+ int i = 0;
+ for (Entry<String, String> next : command.properties().entrySet()) {
+ sb.setLength(0);
+ sb.append(next.getKey());
+ sb.append(PROPERTY_SEPARATOR);
+ sb.append(next.getValue());
+
+ props[i] = sb.toString().getBytes(REMOTING_CHARSET);
+
+ propsLength += 2;
+ propsLength += props[i].length;
+ i++;
+ }
+ }
+
+ int totalLength = MIN_PROTOCOL_LEN - 1 - 4
+ + codeLength
+ + remarkLength
+ + propsLength
+ + parameterLength
+ + extraPayload;
+
+ int headerLength = 1 + 4 + totalLength - parameterLength - extraPayload;
+
+ ByteBuffer buf = ByteBuffer.allocate(headerLength);
+ buf.put(command.protocolType());
+ buf.putInt(totalLength);
+ buf.putInt(command.requestID());
+ buf.put(command.serializerType());
+ buf.put((byte) command.trafficType().ordinal());
+
+ buf.putShort((short) codeLength);
+ if (codeLength > 0) {
+ buf.put(code);
+ }
+ buf.putShort((short) remarkLength);
+ if (remarkLength > 0) {
+ buf.put(remark);
+ }
+ if (props != null) {
+ buf.putShort((short) props.length);
+ for (byte[] prop : props) {
+ buf.putShort((short) prop.length);
+ buf.put(prop);
+ }
+ } else {
+ buf.putShort((short) 0);
+ }
+
+ buf.putInt(parameterLength);
+
+ buf.flip();
+
+ return buf;
+ }
+
+ public static RemotingCommand decode(final ByteBuffer byteBuffer) {
+ RemotingCommandImpl cmd = new RemotingCommandImpl();
+ int totalLength = byteBuffer.limit();
+ cmd.requestID(byteBuffer.getInt());
+ cmd.serializerType(byteBuffer.get());
+ cmd.trafficType(TrafficType.parse(byteBuffer.get()));
+
+ {
+ short size = byteBuffer.getShort();
+ if (size > 0 && size <= CODE_MAX_LEN) {
+ byte[] bytes = new byte[size];
+ byteBuffer.get(bytes);
+ String str = new String(bytes, REMOTING_CHARSET);
+ cmd.opCode(str);
+ } else {
+ throw new RemoteCodecException(String.format("Code length: %d over max limit: %d", size, CODE_MAX_LEN));
+ }
+ }
+
+ {
+ short size = byteBuffer.getShort();
+ if (size > 0) {
+ byte[] bytes = new byte[size];
+ byteBuffer.get(bytes);
+ String str = new String(bytes, REMOTING_CHARSET);
+ cmd.remark(str);
+ }
+ }
+
+ {
+ short size = byteBuffer.getShort();
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ short length = byteBuffer.getShort();
+ if (length > 0) {
+ byte[] bytes = new byte[length];
+ byteBuffer.get(bytes);
+ String str = new String(bytes, REMOTING_CHARSET);
+ int index = str.indexOf(PROPERTY_SEPARATOR);
+ if (index > 0) {
+ String key = str.substring(0, index);
+ String value = str.substring(index + 1);
+ cmd.property(key, value);
+ }
+ }
+ }
+ }
+ }
+
+ {
+ int size = byteBuffer.getInt();
+ if (size > 0 && size <= PARAMETER_MAX_LEN) {
+ byte[] bytes = new byte[size];
+ byteBuffer.get(bytes);
+ cmd.parameterBytes(bytes);
+ } else if (size != 0) {
+ throw new RemoteCodecException(String.format("Parameter size: %d over max limit: %d", size, PARAMETER_MAX_LEN));
+ }
+ }
+
+ {
+ int size = totalLength - byteBuffer.position();
+ if (size > 0 && size <= BODY_MAX_LEN) {
+ byte[] bytes = new byte[size];
+ byteBuffer.get(bytes);
+ cmd.extraPayload(bytes);
+ } else if (size != 0) {
+ throw new RemoteCodecException(String.format("Body size: %d over max limit: %d", size, BODY_MAX_LEN));
+ }
+ }
+
+ return cmd;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RemotingCommandFactoryImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RemotingCommandFactoryImpl.java
new file mode 100644
index 0000000..f5d2126
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RemotingCommandFactoryImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.command;
+
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.command.RemotingCommandFactory;
+import org.apache.rocketmq.remoting.api.command.TrafficType;
+import org.apache.rocketmq.remoting.common.RemotingCommandFactoryMeta;
+
+public class RemotingCommandFactoryImpl implements RemotingCommandFactory {
+ private RemotingCommandFactoryMeta remotingCommandFactoryMeta;
+
+ public RemotingCommandFactoryImpl() {
+ this(new RemotingCommandFactoryMeta());
+ }
+
+ public RemotingCommandFactoryImpl(final RemotingCommandFactoryMeta remotingCommandFactoryMeta) {
+ this.remotingCommandFactoryMeta = remotingCommandFactoryMeta;
+ }
+
+ @Override
+ public RemotingCommand createRequest() {
+ RemotingCommand request = new RemotingCommandImpl();
+ request.protocolType(this.remotingCommandFactoryMeta.getProtocolType());
+ request.serializerType(this.remotingCommandFactoryMeta.getSerializeType());
+ return request;
+ }
+
+ @Override
+ public RemotingCommand createResponse(final RemotingCommand command) {
+ RemotingCommand response = new RemotingCommandImpl();
+ response.requestID(command.requestID());
+ response.protocolType(command.protocolType());
+ response.serializerType(command.serializerType());
+ response.trafficType(TrafficType.RESPONSE);
+ return response;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RemotingCommandImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RemotingCommandImpl.java
new file mode 100644
index 0000000..bcf2338
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RemotingCommandImpl.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.command;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.command.TrafficType;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+import org.apache.rocketmq.remoting.common.TypePresentation;
+
+public class RemotingCommandImpl implements RemotingCommand {
+ public final static RequestIdGenerator REQUEST_ID_GENERATOR = RequestIdGenerator.inst;
+
+ private byte protocolType;
+ private byte serializeType;
+
+ private volatile int requestId = REQUEST_ID_GENERATOR.incrementAndGet();
+ private TrafficType trafficType = TrafficType.REQUEST_SYNC;
+ private String code = CommandFlag.SUCCESS.flag();
+ private String remark = "";
+ private Map<String, String> properties = new HashMap<String, String>();
+ private Object parameter;
+ private byte[] extraPayload;
+
+ private byte[] parameterByte;
+
+ protected RemotingCommandImpl() {
+ }
+
+ @Override
+ public byte protocolType() {
+ return this.protocolType;
+ }
+
+ @Override
+ public void protocolType(byte value) {
+ this.protocolType = value;
+ }
+
+ @Override
+ public int requestID() {
+ return requestId;
+ }
+
+ @Override
+ public void requestID(int value) {
+ this.requestId = value;
+ }
+
+ @Override
+ public byte serializerType() {
+ return this.serializeType;
+ }
+
+ @Override
+ public void serializerType(byte value) {
+ this.serializeType = value;
+ }
+
+ @Override
+ public TrafficType trafficType() {
+ return this.trafficType;
+ }
+
+ @Override
+ public void trafficType(TrafficType value) {
+ this.trafficType = value;
+ }
+
+ @Override
+ public String opCode() {
+ return this.code;
+ }
+
+ @Override
+ public void opCode(String value) {
+ this.code = value;
+ }
+
+ @Override
+ public String remark() {
+ return this.remark;
+ }
+
+ @Override
+ public void remark(String value) {
+ this.remark = value;
+ }
+
+ @Override
+ public Map<String, String> properties() {
+ return this.properties;
+ }
+
+ @Override
+ public void properties(Map<String, String> value) {
+ this.properties = value;
+ }
+
+ @Override
+ public String property(String key) {
+ return this.properties.get(key);
+ }
+
+ @Override
+ public void property(String key, String value) {
+ this.properties.put(key, value);
+ }
+
+ @Override
+ public Object parameter() {
+ return this.parameter;
+ }
+
+ @Override
+ public void parameter(Object value) {
+ this.parameter = value;
+ }
+
+ @Override
+ public byte[] parameterBytes() {
+ return this.getParameterByte();
+ }
+
+ public byte[] getParameterByte() {
+ return parameterByte;
+ }
+
+ public void setParameterByte(byte[] parameterByte) {
+ this.parameterByte = parameterByte;
+ }
+
+ @Override
+ public void parameterBytes(byte[] value) {
+ this.setParameterByte(value);
+ }
+
+ @Override
+ public byte[] extraPayload() {
+ return this.extraPayload;
+ }
+
+ @Override
+ public void extraPayload(byte[] value) {
+ this.extraPayload = value;
+ }
+
+ @Override
+ public <T> T parameter(SerializerFactory serializerFactory, Class<T> c) {
+ if (this.parameter() != null)
+ return (T) this.parameter();
+ final T decode = serializerFactory.get(this.serializerType()).decode(this.parameterBytes(), c);
+ this.parameter(decode);
+ return decode;
+ }
+
+ @Override
+ public <T> T parameter(SerializerFactory serializerFactory, TypePresentation<T> typePresentation) {
+ if (this.parameter() != null)
+ return (T) this.parameter();
+ final T decode = serializerFactory.get(this.serializerType()).decode(this.parameterBytes(), typePresentation);
+ this.parameter(decode);
+ return decode;
+ }
+
+ @Override
+ public <T> T parameter(SerializerFactory serializerFactory, Type type) {
+ if (this.parameter() != null)
+ return (T) this.parameter();
+ final T decode = serializerFactory.get(this.serializerType()).decode(this.parameterBytes(), type);
+ this.parameter(decode);
+ return decode;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return EqualsBuilder.reflectionEquals(this, o);
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RequestIdGenerator.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RequestIdGenerator.java
new file mode 100644
index 0000000..9b85c95
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/command/RequestIdGenerator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.command;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RequestIdGenerator {
+ public static RequestIdGenerator inst = new RequestIdGenerator();
+
+ private AtomicInteger generator = new AtomicInteger(0);
+
+ private RequestIdGenerator() {
+
+ }
+
+ public int incrementAndGet() {
+ return generator.incrementAndGet();
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyChannelEvent.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyChannelEvent.java
new file mode 100644
index 0000000..ec9cece
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyChannelEvent.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 org.apache.rocketmq.remoting.impl.netty;
+
+import io.netty.channel.Channel;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class NettyChannelEvent {
+ private final Channel channel;
+ private final NettyChannelEventType type;
+ private final Throwable cause;
+
+ public NettyChannelEvent(NettyChannelEventType type, Channel channel) {
+ this(type, channel, null);
+ }
+
+ public NettyChannelEvent(NettyChannelEventType type, Channel channel, Throwable cause) {
+ this.type = type;
+ this.channel = channel;
+ this.cause = cause;
+ }
+
+ public NettyChannelEventType getType() {
+ return type;
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyChannelEventType.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyChannelEventType.java
new file mode 100644
index 0000000..1bf2277
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyChannelEventType.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.remoting.impl.netty;
+
+public enum NettyChannelEventType {
+ ACTIVE,
+ INACTIVE,
+ IDLE,
+ EXCEPTION
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingAbstract.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingAbstract.java
new file mode 100644
index 0000000..82b17f4
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingAbstract.java
@@ -0,0 +1,642 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.rocketmq.remoting.api.AsyncHandler;
+import org.apache.rocketmq.remoting.api.RemotingEndPoint;
+import org.apache.rocketmq.remoting.api.RemotingService;
+import org.apache.rocketmq.remoting.api.RequestProcessor;
+import org.apache.rocketmq.remoting.api.channel.ChannelEventListener;
+import org.apache.rocketmq.remoting.api.channel.RemotingChannel;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.command.RemotingCommandFactory;
+import org.apache.rocketmq.remoting.api.command.TrafficType;
+import org.apache.rocketmq.remoting.api.exception.RemoteTimeoutException;
+import org.apache.rocketmq.remoting.api.interceptor.ExceptionContext;
+import org.apache.rocketmq.remoting.api.interceptor.Interceptor;
+import org.apache.rocketmq.remoting.api.interceptor.InterceptorGroup;
+import org.apache.rocketmq.remoting.api.interceptor.RequestContext;
+import org.apache.rocketmq.remoting.api.interceptor.ResponseContext;
+import org.apache.rocketmq.remoting.api.protocol.ProtocolFactory;
+import org.apache.rocketmq.remoting.api.serializable.Serializer;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+import org.apache.rocketmq.remoting.common.ChannelEventListenerGroup;
+import org.apache.rocketmq.remoting.common.Pair;
+import org.apache.rocketmq.remoting.common.RemotingCommandFactoryMeta;
+import org.apache.rocketmq.remoting.common.ResponseResult;
+import org.apache.rocketmq.remoting.common.SemaphoreReleaseOnlyOnce;
+import org.apache.rocketmq.remoting.config.RemotingConfig;
+import org.apache.rocketmq.remoting.external.ThreadUtils;
+import org.apache.rocketmq.remoting.impl.channel.NettyChannelImpl;
+import org.apache.rocketmq.remoting.impl.command.RemotingCommandFactoryImpl;
+import org.apache.rocketmq.remoting.impl.protocol.ProtocolFactoryImpl;
+import org.apache.rocketmq.remoting.impl.protocol.serializer.SerializerFactoryImpl;
+import org.apache.rocketmq.remoting.internal.UIDGenerator;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class NettyRemotingAbstract implements RemotingService {
+ protected static final Logger LOG = LoggerFactory.getLogger(NettyRemotingAbstract.class);
+ protected final ProtocolFactory protocolFactory = new ProtocolFactoryImpl();
+ protected final SerializerFactory serializerFactory = new SerializerFactoryImpl();
+ protected final ChannelEventExecutor channelEventExecutor = new ChannelEventExecutor("ChannelEventExecutor");
+ private final Semaphore semaphoreOneway;
+ private final Semaphore semaphoreAsync;
+ private final Map<Integer, ResponseResult> ackTables = new ConcurrentHashMap<Integer, ResponseResult>(256);
+ private final Map<String, Pair<RequestProcessor, ExecutorService>> processorTables = new ConcurrentHashMap<String, Pair<RequestProcessor, ExecutorService>>();
+ private final AtomicLong responseCounter = new AtomicLong(0);
+ private final RemotingCommandFactory remotingCommandFactory;
+ private final String remotingInstanceId = UIDGenerator.instance().createUID();
+
+ private final ExecutorService publicExecutor;
+ protected ScheduledExecutorService houseKeepingService = ThreadUtils.newSingleThreadScheduledExecutor("HouseKeepingService", true);
+ private InterceptorGroup interceptorGroup = new InterceptorGroup();
+ private ChannelEventListenerGroup channelEventListenerGroup = new ChannelEventListenerGroup();
+
+ NettyRemotingAbstract(RemotingConfig clientConfig) {
+ this(clientConfig, new RemotingCommandFactoryMeta());
+ }
+
+ NettyRemotingAbstract(RemotingConfig clientConfig, RemotingCommandFactoryMeta remotingCommandFactoryMeta) {
+ this.semaphoreOneway = new Semaphore(clientConfig.getClientOnewayInvokeSemaphore(), true);
+ this.semaphoreAsync = new Semaphore(clientConfig.getClientAsyncInvokeSemaphore(), true);
+ this.publicExecutor = ThreadUtils.newFixedThreadPool(
+ clientConfig.getClientAsyncCallbackExecutorThreads(),
+ 10000, "Remoting-PublicExecutor", true);
+ this.remotingCommandFactory = new RemotingCommandFactoryImpl(remotingCommandFactoryMeta);
+ }
+
+ public SerializerFactory getSerializerFactory() {
+ return serializerFactory;
+ }
+
+ protected void putNettyEvent(final NettyChannelEvent event) {
+ this.channelEventExecutor.putNettyEvent(event);
+ }
+
+ protected void startUpHouseKeepingService() {
+ this.houseKeepingService.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ scanResponseTable();
+ }
+ }, 3000, 1000, TimeUnit.MICROSECONDS);
+ }
+
+ @Override
+ public void start() {
+ if (this.channelEventListenerGroup.size() > 0) {
+ this.channelEventExecutor.start();
+ }
+ }
+
+ @Override
+ public void stop() {
+ ThreadUtils.shutdownGracefully(publicExecutor, 2000, TimeUnit.MILLISECONDS);
+ ThreadUtils.shutdownGracefully(channelEventExecutor);
+ }
+
+ protected void processMessageReceived(ChannelHandlerContext ctx, RemotingCommand command) throws Exception {
+ if (command != null) {
+ switch (command.trafficType()) {
+ case REQUEST_ONEWAY:
+ case REQUEST_ASYNC:
+ case REQUEST_SYNC:
+ processRequestCommand(ctx, command);
+ break;
+ case RESPONSE:
+ processResponseCommand(ctx, command);
+ break;
+ default:
+ LOG.warn("Not supported The traffic type {} !", command.trafficType());
+ break;
+ }
+ }
+ }
+
+ public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {
+ Pair<RequestProcessor, ExecutorService> processorExecutorPair = this.processorTables.get(cmd.opCode());
+
+ RemotingChannel channel = new NettyChannelImpl(ctx.channel());
+
+ Runnable run = buildProcessorTask(ctx, cmd, processorExecutorPair, channel);
+
+ try {
+ processorExecutorPair.getRight().submit(run);
+ } catch (RejectedExecutionException e) {
+ if ((System.currentTimeMillis() % 10000) == 0) {
+ LOG.warn(String.format("Request %s from %s Rejected by server executor %s !", cmd,
+ extractRemoteAddress(ctx.channel()), processorExecutorPair.getRight().toString()));
+ }
+
+ if (cmd.trafficType() != TrafficType.REQUEST_ONEWAY) {
+ interceptorGroup.onException(new ExceptionContext(RemotingEndPoint.RESPONSE,
+ extractRemoteAddress(ctx.channel()), cmd, e, "FLOW_CONTROL"));
+
+ RemotingCommand response = remotingCommandFactory.createResponse(cmd);
+ response.opCode(RemotingCommand.CommandFlag.ERROR.flag());
+ response.remark("SYSTEM_BUSY");
+ writeAndFlush(ctx.channel(), response);
+ }
+ }
+ }
+
+ @NotNull
+ private Runnable buildProcessorTask(final ChannelHandlerContext ctx, final RemotingCommand cmd,
+ final Pair<RequestProcessor, ExecutorService> processorExecutorPair, final RemotingChannel channel) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ try {
+ interceptorGroup.beforeRequest(new RequestContext(RemotingEndPoint.RESPONSE,
+ extractRemoteAddress(ctx.channel()), cmd));
+
+ RemotingCommand response = processorExecutorPair.getLeft().processRequest(channel, cmd);
+
+ interceptorGroup.afterResponseReceived(new ResponseContext(RemotingEndPoint.RESPONSE,
+ extractRemoteAddress(ctx.channel()), cmd, response));
+
+ handleResponse(response, cmd, ctx);
+ } catch (Throwable e) {
+ LOG.error(String.format("Process request %s error !", cmd.toString()), e);
+
+ handleException(e, cmd, ctx);
+ }
+ }
+ };
+ }
+
+ private void handleException(Throwable e, RemotingCommand cmd, ChannelHandlerContext ctx) {
+ if (cmd.trafficType() != TrafficType.REQUEST_ONEWAY) {
+ //FiXME Exception interceptor can not throw exception
+ interceptorGroup.onException(new ExceptionContext(RemotingEndPoint.RESPONSE, extractRemoteAddress(ctx.channel()), cmd, e, ""));
+ RemotingCommand response = remotingCommandFactory.createResponse(cmd);
+ response.opCode(RemotingCommand.CommandFlag.ERROR.flag());
+ response.remark(serializeException(cmd.serializerType(), e));
+ response.property("Exception", e.getClass().getName());
+ ctx.writeAndFlush(response);
+ }
+ }
+
+ private String serializeException(byte serializeType, Throwable exception) {
+ final Serializer serialization = getSerializerFactory().get(serializeType);
+ return serialization.encode(exception).toString();
+ }
+
+ private void handleResponse(RemotingCommand response, RemotingCommand cmd, ChannelHandlerContext ctx) {
+ if (cmd.trafficType() != TrafficType.REQUEST_ONEWAY) {
+ if (response != null) {
+ try {
+ writeAndFlush(ctx.channel(), response);
+ } catch (Throwable e) {
+ LOG.error(String.format("Process request %s success, but transfer response %s failed !",
+ cmd.toString(), response.toString()), e);
+ }
+ }
+ }
+
+ }
+
+ private void processResponseCommand(ChannelHandlerContext ctx, RemotingCommand cmd) {
+ final ResponseResult responseResult = ackTables.get(cmd.requestID());
+ if (responseResult != null) {
+ responseResult.setResponseCommand(cmd);
+ responseResult.release();
+
+ long time = System.currentTimeMillis();
+ ackTables.remove(cmd.requestID());
+ if (responseCounter.incrementAndGet() % 5000 == 0) {
+ LOG.info("REQUEST ID:{}, cost time:{}, ackTables.size:{}", cmd.requestID(), time - responseResult.getBeginTimestamp(),
+ ackTables.size());
+ }
+ if (responseResult.getAsyncHandler() != null) {
+ boolean sameThread = false;
+ ExecutorService executor = this.getCallbackExecutor();
+ if (executor != null) {
+ try {
+ executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ responseResult.executeCallbackArrived(responseResult.getResponseCommand());
+ } catch (Throwable e) {
+ LOG.warn("Execute callback error !", e);
+ }
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ sameThread = true;
+ LOG.warn("Execute submit error !", e);
+ }
+ } else {
+ sameThread = true;
+ }
+
+ if (sameThread) {
+ try {
+ responseResult.executeCallbackArrived(responseResult.getResponseCommand());
+ } catch (Throwable e) {
+ LOG.warn("Execute callback in response thread error !", e);
+ }
+ }
+ } else {
+ responseResult.putResponse(cmd);
+ }
+ } else {
+ LOG.warn("request {} from {} has not matched response !", cmd, extractRemoteAddress(ctx.channel()));
+ }
+ }
+
+ private void writeAndFlush(final Channel channel, final Object msg, final ChannelFutureListener listener) {
+ channel.writeAndFlush(msg).addListener(listener);
+ }
+
+ private void writeAndFlush(final Channel channel, final Object msg) {
+ channel.writeAndFlush(msg);
+ }
+
+ public ExecutorService getCallbackExecutor() {
+ return this.publicExecutor;
+ }
+
+ void scanResponseTable() {
+ /*
+ Iterator<Map.Entry<Integer, ResponseResult>> iterator = this.ackTables.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<Integer, ResponseResult> next = iterator.next();
+ ResponseResult result = next.getValue();
+
+ if ((result.getBeginTimestamp() + result.getTimeoutMillis()) <= System.currentTimeMillis()) {
+ iterator.remove();
+ try {
+ long timeoutMillis = result.getTimeoutMillis();
+ long costTimeMillis = System.currentTimeMillis() - result.getBeginTimestamp();
+ result.onTimeout(timeoutMillis, costTimeMillis);
+ LOG.error("scan response table command {} failed", result.getRequestId());
+ } catch (Throwable e) {
+ LOG.warn("Error occurred when execute timeout callback !", e);
+ } finally {
+ result.release();
+ LOG.warn("Removed timeout request {} ", result);
+ }
+ }
+ }
+ */
+ }
+
+ public RemotingCommand invokeWithInterceptor(final Channel channel, final RemotingCommand request,
+ long timeoutMillis) {
+ request.trafficType(TrafficType.REQUEST_SYNC);
+
+ final String remoteAddr = extractRemoteAddress(channel);
+
+ //FIXME try catch here
+ this.interceptorGroup.beforeRequest(new RequestContext(RemotingEndPoint.REQUEST, remoteAddr, request));
+
+ RemotingCommand responseCommand = this.invoke0(remoteAddr, channel, request, timeoutMillis);
+
+ this.interceptorGroup.afterResponseReceived(new ResponseContext(RemotingEndPoint.REQUEST,
+ extractRemoteAddress(channel), request, responseCommand));
+
+ return responseCommand;
+ }
+
+ private RemotingCommand invoke0(final String remoteAddr, final Channel channel, final RemotingCommand request,
+ final long timeoutMillis) {
+ try {
+ final int opaque = request.requestID();
+ final ResponseResult responseResult = new ResponseResult(opaque, timeoutMillis);
+ responseResult.setRequestCommand(request);
+ //FIXME one interceptor for all case ?
+ responseResult.setInterceptorGroup(this.interceptorGroup);
+ responseResult.setRemoteAddr(remoteAddr);
+
+ this.ackTables.put(opaque, responseResult);
+
+ ChannelFutureListener listener = new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture f) throws Exception {
+ if (f.isSuccess()) {
+ responseResult.setSendRequestOK(true);
+ return;
+ } else {
+ responseResult.setSendRequestOK(false);
+
+ ackTables.remove(opaque);
+ responseResult.setCause(f.cause());
+ responseResult.putResponse(null);
+
+ LOG.warn("Send request command to {} failed !", remoteAddr);
+ }
+ }
+ };
+
+ this.writeAndFlush(channel, request, listener);
+
+ RemotingCommand responseCommand = responseResult.waitResponse(timeoutMillis);
+
+ if (null == responseCommand) {
+ if (responseResult.isSendRequestOK()) {
+ throw new RemoteTimeoutException(extractRemoteAddress(channel), timeoutMillis, responseResult.getCause());
+ }
+ /*
+ else {
+ throw new RemoteAccessException(extractRemoteAddress(channel), responseResult.getCause());
+ }*/
+ }
+
+ return responseCommand;
+ } finally {
+ this.ackTables.remove(request.requestID());
+ }
+ }
+
+ public void invokeAsyncWithInterceptor(final Channel channel, final RemotingCommand request,
+ final AsyncHandler invokeCallback, long timeoutMillis) {
+ request.trafficType(TrafficType.REQUEST_ASYNC);
+
+ final String remoteAddr = extractRemoteAddress(channel);
+
+ this.interceptorGroup.beforeRequest(new RequestContext(RemotingEndPoint.REQUEST, remoteAddr, request));
+
+ Exception exception = null;
+
+ try {
+ this.invokeAsync0(remoteAddr, channel, request, timeoutMillis, invokeCallback);
+ } catch (InterruptedException e) {
+ exception = e;
+ } finally {
+ if (null != exception) {
+ try {
+ this.interceptorGroup.onException(new ExceptionContext(RemotingEndPoint.REQUEST, extractRemoteAddress(channel), request, exception, "REMOTING_EXCEPTION"));
+ } catch (Throwable e) {
+ LOG.warn("onException ", e);
+ }
+ }
+ }
+ }
+
+ private void invokeAsync0(final String remoteAddr, final Channel channel, final RemotingCommand request,
+ final long timeoutMillis, final AsyncHandler invokeCallback) throws InterruptedException {
+ boolean acquired = this.semaphoreAsync.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
+ if (acquired) {
+ final int requestID = request.requestID();
+
+ SemaphoreReleaseOnlyOnce once = new SemaphoreReleaseOnlyOnce(this.semaphoreAsync);
+
+ final ResponseResult responseResult = new ResponseResult(request.requestID(), timeoutMillis, invokeCallback, once);
+ responseResult.setRequestCommand(request);
+ responseResult.setInterceptorGroup(this.interceptorGroup);
+ responseResult.setRemoteAddr(remoteAddr);
+
+ this.ackTables.put(request.requestID(), responseResult);
+ try {
+ ChannelFutureListener listener = new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture f) throws Exception {
+ responseResult.setSendRequestOK(f.isSuccess());
+ if (f.isSuccess()) {
+ return;
+ }
+
+ responseResult.putResponse(null);
+ ackTables.remove(requestID);
+ try {
+ responseResult.executeRequestSendFailed();
+ } catch (Throwable e) {
+ LOG.warn("Execute callback error !", e);
+ } finally {
+ responseResult.release();
+ }
+
+ LOG.warn("Send request command to channel failed.", remoteAddr);
+ }
+ };
+
+ this.writeAndFlush(channel, request, listener);
+ } catch (Exception e) {
+ responseResult.release();
+ LOG.error("Send request command to channel " + channel + " error !", e);
+ }
+ } else {
+ String info = String.format("Semaphore tryAcquire %d ms timeout for request %s ,waiting thread nums: %d,availablePermits: %d",
+ timeoutMillis, request.toString(), semaphoreAsync.getQueueLength(), this.semaphoreAsync.availablePermits());
+ LOG.error(info);
+ throw new RemoteTimeoutException(info);
+ }
+ }
+
+ public void invokeOnewayWithInterceptor(final Channel channel, final RemotingCommand request, long timeoutMillis) {
+ request.trafficType(TrafficType.REQUEST_ONEWAY);
+
+ this.interceptorGroup.beforeRequest(new RequestContext(RemotingEndPoint.REQUEST, extractRemoteAddress(channel), request));
+
+ Exception exception = null;
+
+ try {
+ this.invokeOneway0(channel, request, timeoutMillis);
+ } catch (InterruptedException e) {
+ exception = e;
+ } finally {
+ if (null != exception) {
+ try {
+ this.interceptorGroup.onException(new ExceptionContext(RemotingEndPoint.REQUEST, extractRemoteAddress(channel), request, exception, "REMOTING_EXCEPTION"));
+ } catch (Throwable e) {
+ LOG.warn("onException ", e);
+ }
+ }
+ }
+ }
+
+ private void invokeOneway0(final Channel channel, final RemotingCommand request,
+ final long timeoutMillis) throws InterruptedException {
+ boolean acquired = this.semaphoreOneway.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
+ if (acquired) {
+ final SemaphoreReleaseOnlyOnce once = new SemaphoreReleaseOnlyOnce(this.semaphoreOneway);
+ try {
+ final SocketAddress socketAddress = channel.remoteAddress();
+
+ ChannelFutureListener listener = new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture f) throws Exception {
+ once.release();
+ if (!f.isSuccess()) {
+ LOG.warn("Send request command to channel {} failed !", socketAddress);
+ }
+ }
+ };
+
+ this.writeAndFlush(channel, request, listener);
+ } catch (Exception e) {
+ once.release();
+ LOG.error("Send request command to channel " + channel + " error !", e);
+ }
+ } else {
+ String info = String.format("Semaphore tryAcquire %d ms timeout for request %s ,waiting thread nums: %d,availablePermits: %d",
+ timeoutMillis, request.toString(), semaphoreAsync.getQueueLength(), this.semaphoreAsync.availablePermits());
+ LOG.error(info);
+ throw new RemoteTimeoutException(info);
+ }
+ }
+
+ public String getRemotingInstanceId() {
+ return remotingInstanceId;
+ }
+
+ @Override
+ public ProtocolFactory protocolFactory() {
+ return this.protocolFactory;
+ }
+
+ @Override
+ public SerializerFactory serializerFactory() {
+ return this.serializerFactory;
+ }
+
+ @Override
+ public RemotingCommandFactory commandFactory() {
+ return this.remotingCommandFactory;
+ }
+
+ @Override
+ public void registerRequestProcessor(String requestCode, RequestProcessor processor, ExecutorService executor) {
+ Pair<RequestProcessor, ExecutorService> pair = new Pair<RequestProcessor, ExecutorService>(processor, executor);
+ if (!this.processorTables.containsKey(requestCode)) {
+ this.processorTables.put(requestCode, pair);
+ }
+ }
+
+ @Override
+ public void registerRequestProcessor(String requestCode, RequestProcessor processor) {
+ this.registerRequestProcessor(requestCode, processor, publicExecutor);
+ }
+
+ @Override
+ public void unregisterRequestProcessor(String requestCode) {
+ this.processorTables.remove(requestCode);
+ }
+
+ @Override
+ public String remotingInstanceId() {
+ return this.getRemotingInstanceId();
+ }
+
+ @Override
+ public void registerInterceptor(Interceptor interceptor) {
+ this.interceptorGroup.registerInterceptor(interceptor);
+ }
+
+ @Override
+ public void registerChannelEventListener(ChannelEventListener listener) {
+ this.channelEventListenerGroup.registerChannelEventListener(listener);
+ }
+
+ @Override
+ public Pair<RequestProcessor, ExecutorService> processor(String requestCode) {
+ return processorTables.get(requestCode);
+ }
+
+ protected String extractRemoteAddress(Channel channel) {
+ return ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
+ }
+
+ class ChannelEventExecutor extends Thread {
+ private final static int MAX_SIZE = 10000;
+ private final LinkedBlockingQueue<NettyChannelEvent> eventQueue = new LinkedBlockingQueue<NettyChannelEvent>();
+ private String name;
+
+ public ChannelEventExecutor(String nettyEventExector) {
+ super(nettyEventExector);
+ this.name = nettyEventExector;
+ }
+ //private final AtomicBoolean isStopped = new AtomicBoolean(true);
+
+ public void putNettyEvent(final NettyChannelEvent event) {
+ if (this.eventQueue.size() <= MAX_SIZE) {
+ this.eventQueue.add(event);
+ } else {
+ LOG.warn("event queue size[{}] enough, so drop this event {}", this.eventQueue.size(), event.toString());
+ }
+ }
+
+ @Override
+ public void run() {
+ LOG.info(this.name + " service started");
+
+ ChannelEventListenerGroup listener = NettyRemotingAbstract.this.channelEventListenerGroup;
+
+ while (true) {
+ try {
+ NettyChannelEvent event = this.eventQueue.poll(3000, TimeUnit.MILLISECONDS);
+ if (event != null && listener != null) {
+ RemotingChannel channel = new NettyChannelImpl(event.getChannel());
+
+ LOG.warn("Channel Event, {}", event);
+
+ switch (event.getType()) {
+ case IDLE:
+ listener.onChannelIdle(channel);
+ break;
+ case INACTIVE:
+ listener.onChannelClose(channel);
+ break;
+ case ACTIVE:
+ listener.onChannelConnect(channel);
+ break;
+ case EXCEPTION:
+ listener.onChannelException(channel);
+ break;
+ default:
+ break;
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("error", e);
+ break;
+ }
+ }
+ }
+
+ }
+
+ protected class EventDispatcher extends SimpleChannelInboundHandler<RemotingCommand> {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
+ processMessageReceived(ctx, msg);
+ }
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingClient.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingClient.java
new file mode 100644
index 0000000..7481574
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingClient.java
@@ -0,0 +1,499 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.WriteBufferWaterMark;
+import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollSocketChannel;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http2.Http2SecurityUtil;
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.SupportedCipherSuiteFilter;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.concurrent.DefaultEventExecutorGroup;
+import io.netty.util.concurrent.EventExecutorGroup;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.net.ssl.SSLException;
+import org.apache.rocketmq.remoting.api.AsyncHandler;
+import org.apache.rocketmq.remoting.api.RemotingClient;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.command.TrafficType;
+import org.apache.rocketmq.remoting.api.exception.RemoteConnectFailureException;
+import org.apache.rocketmq.remoting.api.exception.RemoteTimeoutException;
+import org.apache.rocketmq.remoting.api.protocol.Protocol;
+import org.apache.rocketmq.remoting.common.RemotingCommandFactoryMeta;
+import org.apache.rocketmq.remoting.config.RemotingConfig;
+import org.apache.rocketmq.remoting.external.ThreadUtils;
+import org.apache.rocketmq.remoting.impl.netty.handler.Decoder;
+import org.apache.rocketmq.remoting.impl.netty.handler.Encoder;
+import org.apache.rocketmq.remoting.impl.netty.handler.ExceptionHandler;
+import org.apache.rocketmq.remoting.impl.netty.handler.Http2Handler;
+import org.apache.rocketmq.remoting.internal.JvmUtils;
+
+public class NettyRemotingClient extends NettyRemotingAbstract implements RemotingClient {
+ private static final long LOCK_TIMEOUT_MILLIS = 3000;
+ private final Bootstrap clientBootstrap = new Bootstrap();
+ private final EventLoopGroup ioGroup;
+ private final Class<? extends SocketChannel> socketChannelClass;
+
+ private final RemotingConfig clientConfig;
+
+ private final ConcurrentHashMap<String, ChannelWrapper> channelTables = new ConcurrentHashMap<String, ChannelWrapper>();
+ private final Lock lockChannelTables = new ReentrantLock();
+ private EventExecutorGroup workerGroup;
+ private SslContext sslContext;
+
+ NettyRemotingClient(final RemotingConfig clientConfig) {
+ super(clientConfig, new RemotingCommandFactoryMeta(clientConfig.getProtocolName(), clientConfig.getSerializerName()));
+ this.clientConfig = clientConfig;
+
+ if (JvmUtils.isLinux() && this.clientConfig.isClientNativeEpollEnable()) {
+ this.ioGroup = new EpollEventLoopGroup(clientConfig.getClientWorkerThreads(), ThreadUtils.newGenericThreadFactory("NettyClientEpollIoThreads",
+ clientConfig.getClientWorkerThreads()));
+ socketChannelClass = EpollSocketChannel.class;
+ } else {
+ this.ioGroup = new NioEventLoopGroup(clientConfig.getClientWorkerThreads(), ThreadUtils.newGenericThreadFactory("NettyClientNioIoThreads",
+ clientConfig.getClientWorkerThreads()));
+ socketChannelClass = NioSocketChannel.class;
+ }
+
+ this.workerGroup = new DefaultEventExecutorGroup(clientConfig.getClientWorkerThreads(),
+ ThreadUtils.newGenericThreadFactory("NettyClientWorkerThreads", clientConfig.getClientWorkerThreads()));
+
+ if (Protocol.HTTP2.equals(clientConfig.getProtocolName())) {
+ buildSslContext();
+ }
+ }
+
+ private void applyOptions(Bootstrap bootstrap) {
+ if (null != clientConfig) {
+ if (clientConfig.getTcpSoLinger() > 0) {
+ bootstrap.option(ChannelOption.SO_LINGER, clientConfig.getTcpSoLinger());
+ }
+
+ if (clientConfig.getTcpSoSndBufSize() > 0) {
+ bootstrap.option(ChannelOption.SO_SNDBUF, clientConfig.getTcpSoSndBufSize());
+ }
+ if (clientConfig.getTcpSoRcvBufSize() > 0) {
+ bootstrap.option(ChannelOption.SO_RCVBUF, clientConfig.getTcpSoRcvBufSize());
+ }
+
+ bootstrap.option(ChannelOption.SO_REUSEADDR, clientConfig.isTcpSoReuseAddress()).
+ option(ChannelOption.SO_KEEPALIVE, clientConfig.isTcpSoKeepAlive()).
+ option(ChannelOption.TCP_NODELAY, clientConfig.isTcpSoNoDelay()).
+ option(ChannelOption.CONNECT_TIMEOUT_MILLIS, clientConfig.getTcpSoTimeout()).
+ option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(clientConfig.getWriteBufLowWaterMark(),
+ clientConfig.getWriteBufHighWaterMark()));
+ }
+ }
+
+ @Override
+ public void start() {
+ super.start();
+
+ this.clientBootstrap.group(this.ioGroup).channel(socketChannelClass)
+ .handler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ if (Protocol.HTTP2.equals(clientConfig.getProtocolName())) {
+ ch.pipeline().addFirst(sslContext.newHandler(ch.alloc()), Http2Handler.newHandler(false));
+ }
+ ch.pipeline().addLast(workerGroup, new Decoder(), new Encoder(), new IdleStateHandler(clientConfig.getConnectionChannelReaderIdleSeconds(),
+ clientConfig.getConnectionChannelWriterIdleSeconds(), clientConfig.getConnectionChannelIdleSeconds()),
+ new ClientConnectionHandler(), new EventDispatcher(), new ExceptionHandler());
+ }
+ });
+
+ applyOptions(clientBootstrap);
+
+ startUpHouseKeepingService();
+ }
+
+ private void buildSslContext() {
+ SslProvider provider = OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK;
+ try {
+ sslContext = SslContextBuilder.forClient()
+ .sslProvider(provider)
+ /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification.
+ * Please refer to the HTTP/2 specification for cipher requirements. */
+ .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
+ .trustManager(InsecureTrustManagerFactory.INSTANCE)
+ .build();
+ } catch (SSLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void stop() {
+ // try {
+ ThreadUtils.shutdownGracefully(houseKeepingService, 3000, TimeUnit.MILLISECONDS);
+
+ for (ChannelWrapper cw : this.channelTables.values()) {
+ this.closeChannel(null, cw.getChannel());
+ }
+
+ this.channelTables.clear();
+
+ this.ioGroup.shutdownGracefully();
+
+ ThreadUtils.shutdownGracefully(channelEventExecutor);
+
+ this.workerGroup.shutdownGracefully();
+ /*
+ } catch (Exception e) {
+ LOG.error("RemotingClient stopped error !", e);
+ }
+ */
+
+ super.stop();
+ }
+
+ private void closeChannel(final String addr, final Channel channel) {
+ if (null == channel)
+ return;
+
+ final String addrRemote = null == addr ? extractRemoteAddress(channel) : addr;
+ try {
+ if (this.lockChannelTables.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
+ try {
+ boolean removeItemFromTable = true;
+ ChannelWrapper prevCW = this.channelTables.get(addrRemote);
+ //Workaround for null
+ if (null == prevCW) {
+ return;
+ }
+
+ LOG.info("Begin to close the remote address {} channel {}", addrRemote, prevCW);
+
+ if (prevCW.getChannel() != channel) {
+ LOG.info("Channel {} has been closed,this is a new channel.", prevCW.getChannel(), channel);
+ removeItemFromTable = false;
+ }
+
+ if (removeItemFromTable) {
+ this.channelTables.remove(addrRemote);
+ LOG.info("Channel {} has been removed !", addrRemote);
+ }
+
+ channel.close().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ LOG.warn("Close channel {} {}", channel, future.isSuccess());
+ }
+ });
+ } catch (Exception e) {
+ LOG.error("Close channel error !", e);
+ } finally {
+ this.lockChannelTables.unlock();
+ }
+ } else {
+ LOG.warn("Can not lock channel table in {} ms", LOCK_TIMEOUT_MILLIS);
+ }
+ } catch (InterruptedException e) {
+ LOG.error("Close channel error !", e);
+ }
+ }
+
+ private Channel createIfAbsent(final String addr) {
+ ChannelWrapper cw = this.channelTables.get(addr);
+ if (cw != null && cw.isActive()) {
+ return cw.getChannel();
+ }
+ return this.createChannel(addr);
+ }
+
+ //FIXME need test to verify
+ private Channel createChannel(final String addr) {
+ ChannelWrapper cw = null;
+ try {
+ if (this.lockChannelTables.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
+ try {
+ boolean createNewConnection;
+ cw = this.channelTables.get(addr);
+ if (cw != null) {
+ if (cw.isActive()) {
+ return cw.getChannel();
+ } else if (!cw.getChannelFuture().isDone()) {
+ createNewConnection = false;
+ } else {
+ this.channelTables.remove(addr);
+ createNewConnection = true;
+ }
+ } else {
+ createNewConnection = true;
+ }
+
+ if (createNewConnection) {
+ String[] s = addr.split(":");
+ SocketAddress socketAddress = new InetSocketAddress(s[0], Integer.valueOf(s[1]));
+ ChannelFuture channelFuture = this.clientBootstrap.connect(socketAddress);
+ LOG.info("createChannel: begin to connect remote host[{}] asynchronously", addr);
+ cw = new ChannelWrapper(channelFuture);
+ this.channelTables.put(addr, cw);
+ }
+ } catch (Exception e) {
+ LOG.error("createChannel: create channel exception", e);
+ } finally {
+ this.lockChannelTables.unlock();
+ }
+ } else {
+ LOG.warn("createChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ if (cw != null) {
+ ChannelFuture channelFuture = cw.getChannelFuture();
+ if (channelFuture.awaitUninterruptibly(this.clientConfig.getClientConnectionFutureAwaitTimeoutMillis())) {
+ if (cw.isActive()) {
+ LOG.info("createChannel: connect remote host[{}] success, {}", addr, channelFuture.toString());
+ return cw.getChannel();
+ } else {
+ LOG.warn("createChannel: connect remote host[" + addr + "] failed, and destroy the channel" + channelFuture.toString(), channelFuture.cause());
+ this.closeChannel(addr, cw.getChannel());
+ }
+ } else {
+ LOG.warn("createChannel: connect remote host[{}] timeout {}ms, {}, and destroy the channel", addr, this.clientConfig.getClientConnectionFutureAwaitTimeoutMillis(),
+ channelFuture.toString());
+ this.closeChannel(addr, cw.getChannel());
+ }
+ }
+ return null;
+ }
+
+ private void closeChannel(final Channel channel) {
+ if (null == channel)
+ return;
+
+ try {
+ if (this.lockChannelTables.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
+ try {
+ boolean removeItemFromTable = true;
+ ChannelWrapper prevCW = null;
+ String addrRemote = null;
+
+ for (Map.Entry<String, ChannelWrapper> entry : channelTables.entrySet()) {
+ ChannelWrapper prev = entry.getValue();
+ if (prev.getChannel() != null) {
+ if (prev.getChannel() == channel) {
+ prevCW = prev;
+ addrRemote = entry.getKey();
+ break;
+ }
+ }
+ }
+
+ if (null == prevCW) {
+ LOG.info("eventCloseChannel: the channel[{}] has been removed from the channel table before", addrRemote);
+ removeItemFromTable = false;
+ }
+
+ if (removeItemFromTable) {
+ this.channelTables.remove(addrRemote);
+ LOG.info("closeChannel: the channel[{}] was removed from channel table", addrRemote);
+ //RemotingHelper.closeChannel(channel);
+ }
+ } catch (Exception e) {
+ LOG.error("closeChannel: close the channel exception", e);
+ } finally {
+ this.lockChannelTables.unlock();
+ }
+ } else {
+ LOG.warn("closeChannel: try to lock channel table, but timeout, {}ms", LOCK_TIMEOUT_MILLIS);
+ }
+ } catch (InterruptedException e) {
+ LOG.error("closeChannel exception", e);
+ }
+ }
+
+ @Override
+ public RemotingCommand invoke(final String address, final RemotingCommand request, final long timeoutMillis) {
+ request.trafficType(TrafficType.REQUEST_SYNC);
+
+ Channel channel = this.createIfAbsent(address);
+ if (channel != null && channel.isActive()) {
+ try {
+ return this.invokeWithInterceptor(channel, request, timeoutMillis);
+
+ } catch (RemoteTimeoutException e) {
+ if (this.clientConfig.isClientCloseSocketIfTimeout()) {
+ LOG.warn("invoke: timeout, so close the socket {} ms, {}", timeoutMillis, address);
+ this.closeChannel(address, channel);
+ }
+
+ LOG.warn("invoke: wait response timeout<{}ms> exception, so close the channel[{}]", timeoutMillis, address);
+ throw e;
+ } finally {
+ /*
+ if (this.clientConfig.isClientShortConnectionEnable()) {
+ this.closeChannel(addr, channel);
+ }
+ */
+ }
+ } else {
+ this.closeChannel(address, channel);
+ throw new RemoteConnectFailureException(address);
+ }
+
+ }
+
+ @Override
+ public void invokeAsync(final String address, final RemotingCommand request, final AsyncHandler asyncHandler,
+ final long timeoutMillis) {
+
+ final Channel channel = this.createIfAbsent(address);
+ if (channel != null && channel.isActive()) {
+ // We support Netty's channel-level backpressure thereby respecting slow receivers on the other side.
+ if (!channel.isWritable()) {
+ // Note: It's up to the layer above a transport to decide whether or not to requeue a canceled write.
+ LOG.warn("Channel statistics - bytesBeforeUnwritable:{},bytesBeforeWritable:{}", channel.bytesBeforeUnwritable(), channel.bytesBeforeWritable());
+ }
+ this.invokeAsyncWithInterceptor(channel, request, asyncHandler, timeoutMillis);
+ } else {
+ this.closeChannel(address, channel);
+ }
+ }
+
+ @Override
+ public void invokeOneWay(final String address, final RemotingCommand request, final long timeoutMillis) {
+ final Channel channel = this.createIfAbsent(address);
+ if (channel != null && channel.isActive()) {
+ if (!channel.isWritable()) {
+ //if (this.clientConfig.isSocketFlowControl()) {
+ LOG.warn("Channel statistics - bytesBeforeUnwritable:{},bytesBeforeWritable:{}", channel.bytesBeforeUnwritable(), channel.bytesBeforeWritable());
+ //throw new ServiceInvocationFailureException(String.format("Channel[%s] is not writable now", channel.toString()));
+ }
+ this.invokeOnewayWithInterceptor(channel, request, timeoutMillis);
+ } else {
+ this.closeChannel(address, channel);
+ }
+ }
+
+ private class ChannelWrapper {
+ private final ChannelFuture channelFuture;
+
+ ChannelWrapper(ChannelFuture channelFuture) {
+ this.channelFuture = channelFuture;
+ }
+
+ boolean isActive() {
+ return this.channelFuture.channel() != null && this.channelFuture.channel().isActive();
+ }
+
+ boolean isWriteable() {
+ return this.channelFuture.channel().isWritable();
+ }
+
+ private Channel getChannel() {
+ return this.channelFuture.channel();
+ }
+
+ ChannelFuture getChannelFuture() {
+ return channelFuture;
+ }
+ }
+
+ private class ClientConnectionHandler extends ChannelDuplexHandler {
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ LOG.warn("Channel {} channelWritabilityChanged event triggered - bytesBeforeUnwritable:{},bytesBeforeWritable:{}", ctx.channel(),
+ ctx.channel().bytesBeforeUnwritable(), ctx.channel().bytesBeforeWritable());
+ }
+
+ @Override
+ public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
+ ChannelPromise promise)
+ throws Exception {
+ LOG.info("Connected from {} to {}.", localAddress, remoteAddress);
+ super.connect(ctx, remoteAddress, localAddress, promise);
+
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.ACTIVE, ctx.channel()));
+ }
+
+ @Override
+ public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
+ LOG.info("Remote address {} disconnect channel {}.", ctx.channel().remoteAddress(), ctx.channel());
+
+ closeChannel(ctx.channel());
+
+ super.disconnect(ctx, promise);
+
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.INACTIVE, ctx.channel()));
+ }
+
+ @Override
+ public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
+ LOG.info("Remote address {} close channel {}.", ctx.channel().remoteAddress(), ctx.channel());
+
+ closeChannel(ctx.channel());
+
+ super.close(ctx, promise);
+
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.INACTIVE, ctx.channel()));
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ IdleStateEvent event = (IdleStateEvent) evt;
+ if (event.state().equals(IdleState.ALL_IDLE)) {
+ LOG.info("Close channel {} because of idle event {} ", ctx.channel(), event);
+ closeChannel(ctx.channel());
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.IDLE, ctx.channel()));
+ }
+ }
+
+ ctx.fireUserEventTriggered(evt);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ LOG.info("Close channel {} because of error {} ", ctx.channel(), cause);
+
+ closeChannel(ctx.channel());
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.EXCEPTION, ctx.channel()));
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingServer.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingServer.java
new file mode 100644
index 0000000..d875f95
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/NettyRemotingServer.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollServerSocketChannel;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.ServerSocketChannel;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.http2.Http2SecurityUtil;
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.SupportedCipherSuiteFilter;
+import io.netty.handler.ssl.util.SelfSignedCertificate;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.concurrent.DefaultEventExecutorGroup;
+import io.netty.util.concurrent.EventExecutorGroup;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+import org.apache.rocketmq.remoting.api.AsyncHandler;
+import org.apache.rocketmq.remoting.api.RemotingServer;
+import org.apache.rocketmq.remoting.api.channel.RemotingChannel;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.config.RemotingConfig;
+import org.apache.rocketmq.remoting.external.ThreadUtils;
+import org.apache.rocketmq.remoting.impl.channel.NettyChannelImpl;
+import org.apache.rocketmq.remoting.impl.netty.handler.ChannelStatistics;
+import org.apache.rocketmq.remoting.impl.netty.handler.ProtocolSelector;
+import org.apache.rocketmq.remoting.internal.JvmUtils;
+
+public class NettyRemotingServer extends NettyRemotingAbstract implements RemotingServer {
+ private final RemotingConfig serverConfig;
+
+ private final ServerBootstrap serverBootstrap;
+ private final EventLoopGroup bossGroup;
+ private final EventLoopGroup ioGroup;
+ private EventExecutorGroup workerGroup;
+ private Class<? extends ServerSocketChannel> socketChannelClass;
+
+ private int port;
+ private SslContext sslContext;
+
+ NettyRemotingServer(final RemotingConfig serverConfig) {
+ super(serverConfig);
+
+ this.serverBootstrap = new ServerBootstrap();
+ this.serverConfig = serverConfig;
+
+ if (JvmUtils.isLinux() && this.serverConfig.isServerNativeEpollEnable()) {
+ this.ioGroup = new EpollEventLoopGroup(serverConfig.getServerIoThreads(), ThreadUtils.newGenericThreadFactory("NettyEpollIoThreads",
+ serverConfig.getServerIoThreads()));
+
+ this.bossGroup = new EpollEventLoopGroup(serverConfig.getServerAcceptorThreads(), ThreadUtils.newGenericThreadFactory("NettyBossThreads",
+ serverConfig.getServerAcceptorThreads()));
+
+ this.socketChannelClass = EpollServerSocketChannel.class;
+ } else {
+ this.bossGroup = new NioEventLoopGroup(serverConfig.getServerAcceptorThreads(), ThreadUtils.newGenericThreadFactory("NettyBossThreads",
+ serverConfig.getServerAcceptorThreads()));
+
+ this.ioGroup = new NioEventLoopGroup(serverConfig.getServerIoThreads(), ThreadUtils.newGenericThreadFactory("NettyNioIoThreads",
+ serverConfig.getServerIoThreads()));
+
+ this.socketChannelClass = NioServerSocketChannel.class;
+ }
+
+ this.workerGroup = new DefaultEventExecutorGroup(serverConfig.getServerWorkerThreads(),
+ ThreadUtils.newGenericThreadFactory("NettyWorkerThreads", serverConfig.getServerWorkerThreads()));
+
+ buildHttp2SslContext();
+ }
+
+ private void buildHttp2SslContext() {
+ try {
+ SslProvider provider = OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK;
+ SelfSignedCertificate ssc;
+ //NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification.
+ //Please refer to the HTTP/2 specification for cipher requirements.
+ ssc = new SelfSignedCertificate();
+ sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
+ .sslProvider(provider)
+ .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE).build();
+ } catch (Exception e) {
+ LOG.error("Can not build SSL context !", e);
+ }
+ }
+
+ private void applyOptions(ServerBootstrap bootstrap) {
+ //option() is for the NioServerSocketChannel that accepts incoming connections.
+ //childOption() is for the Channels accepted by the parent ServerChannel, which is NioServerSocketChannel in this case
+ if (null != serverConfig) {
+ if (serverConfig.getTcpSoBacklogSize() > 0) {
+ bootstrap.option(ChannelOption.SO_BACKLOG, serverConfig.getTcpSoBacklogSize());
+ }
+
+ if (serverConfig.getTcpSoLinger() > 0) {
+ bootstrap.option(ChannelOption.SO_LINGER, serverConfig.getTcpSoLinger());
+ }
+
+ if (serverConfig.getTcpSoSndBufSize() > 0) {
+ bootstrap.childOption(ChannelOption.SO_SNDBUF, serverConfig.getTcpSoSndBufSize());
+ }
+ if (serverConfig.getTcpSoRcvBufSize() > 0) {
+ bootstrap.childOption(ChannelOption.SO_RCVBUF, serverConfig.getTcpSoRcvBufSize());
+ }
+
+ bootstrap.option(ChannelOption.SO_REUSEADDR, serverConfig.isTcpSoReuseAddress()).
+ childOption(ChannelOption.SO_KEEPALIVE, serverConfig.isTcpSoKeepAlive()).
+ childOption(ChannelOption.TCP_NODELAY, serverConfig.isTcpSoNoDelay()).
+ option(ChannelOption.CONNECT_TIMEOUT_MILLIS, serverConfig.getTcpSoTimeout());
+ }
+
+ if (serverConfig.isServerPooledBytebufAllocatorEnable()) {
+ bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+ }
+ }
+
+ @Override
+ public void start() {
+ super.start();
+
+ final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+ this.serverBootstrap.group(this.bossGroup, this.ioGroup).
+ channel(socketChannelClass).childHandler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ public void initChannel(SocketChannel ch) throws Exception {
+ channels.add(ch);
+
+ ChannelPipeline cp = ch.pipeline();
+
+ cp.addLast(ChannelStatistics.NAME, new ChannelStatistics(channels));
+
+ cp.addFirst(ProtocolSelector.NAME, new ProtocolSelector(sslContext));
+ cp.addLast(workerGroup, new IdleStateHandler(serverConfig.getConnectionChannelReaderIdleSeconds(),
+ serverConfig.getConnectionChannelWriterIdleSeconds(),
+ serverConfig.getConnectionChannelIdleSeconds()),
+ new ServerConnectionHandler(),
+ new EventDispatcher());
+ }
+ });
+
+ applyOptions(serverBootstrap);
+
+ ChannelFuture channelFuture = this.serverBootstrap.bind(this.serverConfig.getServerListenPort()).syncUninterruptibly();
+ this.port = ((InetSocketAddress) channelFuture.channel().localAddress()).getPort();
+
+ startUpHouseKeepingService();
+ }
+
+ @Override
+ public void stop() {
+ try {
+
+ ThreadUtils.shutdownGracefully(houseKeepingService, 3000, TimeUnit.MILLISECONDS);
+
+ ThreadUtils.shutdownGracefully(channelEventExecutor);
+
+ this.bossGroup.shutdownGracefully().syncUninterruptibly();
+
+ this.ioGroup.shutdownGracefully().syncUninterruptibly();
+
+ this.workerGroup.shutdownGracefully().syncUninterruptibly();
+ } catch (Exception e) {
+ LOG.error("RemotingServer stopped error !", e);
+ }
+
+ super.stop();
+ }
+
+ @Override
+ public int localListenPort() {
+ return this.port;
+ }
+
+ @Override
+ public RemotingCommand invoke(final RemotingChannel remotingChannel, final RemotingCommand request,
+ final long timeoutMillis) {
+ return invokeWithInterceptor(((NettyChannelImpl) remotingChannel).getChannel(), request, timeoutMillis);
+ }
+
+ @Override
+ public void invokeAsync(final RemotingChannel remotingChannel, final RemotingCommand request,
+ final AsyncHandler asyncHandler,
+ final long timeoutMillis) {
+ invokeAsyncWithInterceptor(((NettyChannelImpl) remotingChannel).getChannel(), request, asyncHandler, timeoutMillis);
+ }
+
+ @Override
+ public void invokeOneWay(final RemotingChannel remotingChannel, final RemotingCommand request,
+ final long timeoutMillis) {
+ invokeOnewayWithInterceptor(((NettyChannelImpl) remotingChannel).getChannel(), request, timeoutMillis);
+ }
+
+ private class ServerConnectionHandler extends ChannelDuplexHandler {
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ LOG.warn("Channel {} channelWritabilityChanged event triggered - bytesBeforeUnwritable:{},bytesBeforeWritable:{}", ctx.channel(),
+ ctx.channel().bytesBeforeUnwritable(), ctx.channel().bytesBeforeWritable());
+ }
+
+ @Override
+ public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
+ super.channelRegistered(ctx);
+ }
+
+ @Override
+ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
+ super.channelUnregistered(ctx);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.ACTIVE, ctx.channel()));
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.INACTIVE, ctx.channel()));
+ }
+
+ @Override
+ public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ final IdleStateEvent event = (IdleStateEvent) evt;
+ if (event.state().equals(IdleState.ALL_IDLE)) {
+ ctx.channel().close().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ LOG.warn("Close channel {} because of event {},result is {}", ctx.channel(), event, future.isSuccess());
+ }
+ });
+
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.IDLE, ctx.channel()));
+ }
+ }
+ ctx.fireUserEventTriggered(evt);
+ }
+
+ @Override
+ public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
+ putNettyEvent(new NettyChannelEvent(NettyChannelEventType.EXCEPTION, ctx.channel(), cause));
+
+ ctx.channel().close().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ LOG.warn("Close channel {} because of error {},result is {}", ctx.channel(), cause, future.isSuccess());
+ }
+ });
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/RemotingBootstrapFactory.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/RemotingBootstrapFactory.java
new file mode 100644
index 0000000..4dd502c
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/RemotingBootstrapFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty;
+
+import java.util.Properties;
+import org.apache.rocketmq.remoting.api.RemotingClient;
+import org.apache.rocketmq.remoting.config.RemotingConfig;
+import org.apache.rocketmq.remoting.internal.BeanUtils;
+import org.apache.rocketmq.remoting.internal.PropertyUtils;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Remoting Bootstrap entrance.
+ */
+public final class RemotingBootstrapFactory {
+ public static RemotingClient createRemotingClient(@NotNull final String fileName) {
+ Properties prop = PropertyUtils.loadProps(fileName);
+ RemotingConfig config = BeanUtils.populate(prop, RemotingConfig.class);
+ return new NettyRemotingClient(config);
+ }
+
+ public static RemotingClient createRemotingClient(@NotNull final RemotingConfig config) {
+ return new NettyRemotingClient(config);
+ }
+
+ public static RemotingClient createRemotingClient(@NotNull final Properties properties) {
+ RemotingConfig config = BeanUtils.populate(properties, RemotingConfig.class);
+ return new NettyRemotingClient(config);
+ }
+
+ public static NettyRemotingServer createRemotingServer(@NotNull final String fileName) {
+ Properties prop = PropertyUtils.loadProps(fileName);
+ RemotingConfig config = BeanUtils.populate(prop, RemotingConfig.class);
+ return new NettyRemotingServer(config);
+ }
+
+ public static NettyRemotingServer createRemotingServer(@NotNull final Properties properties) {
+ RemotingConfig config = BeanUtils.populate(properties, RemotingConfig.class);
+ return new NettyRemotingServer(config);
+ }
+
+ public static NettyRemotingServer createRemotingServer(@NotNull final RemotingConfig config) {
+ return new NettyRemotingServer(config);
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ChannelStatistics.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ChannelStatistics.java
new file mode 100755
index 0000000..ff0f9c9
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ChannelStatistics.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.remoting.impl.netty.handler;
+
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.group.ChannelGroup;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.rocketmq.remoting.common.metrics.ChannelMetrics;
+
+public class ChannelStatistics extends ChannelDuplexHandler implements ChannelMetrics {
+ public static final String NAME = ChannelStatistics.class.getSimpleName();
+ private final AtomicInteger channelCount = new AtomicInteger(0);
+ private final ChannelGroup allChannels;
+
+ public ChannelStatistics(ChannelGroup allChannels) {
+ this.allChannels = allChannels;
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ // connect
+ channelCount.incrementAndGet();
+ allChannels.add(ctx.channel());
+ super.channelActive(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ // disconnect
+ channelCount.decrementAndGet();
+ allChannels.remove(ctx.channel());
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ public Integer getChannelCount() {
+ return channelCount.get();
+ }
+
+ @Override
+ public ChannelGroup getChannels() {
+ return allChannels;
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Decoder.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Decoder.java
new file mode 100644
index 0000000..ec1d69d
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Decoder.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import java.nio.ByteBuffer;
+import java.util.List;
+import org.apache.rocketmq.remoting.api.buffer.ByteBufferWrapper;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.exception.RemoteCodecException;
+import org.apache.rocketmq.remoting.impl.buffer.NettyByteBufferWrapper;
+import org.apache.rocketmq.remoting.impl.command.CodecHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Decoder extends ByteToMessageDecoder {
+ private static final Logger LOG = LoggerFactory.getLogger(Decoder.class);
+
+ public Decoder() {
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+ if (!in.isReadable()) {
+ return;
+ }
+
+ NettyByteBufferWrapper wrapper = new NettyByteBufferWrapper(in);
+
+ Object msg = this.decode(ctx, wrapper);
+ if (msg != null) {
+ out.add(msg);
+ }
+ }
+
+ private Object decode(final ChannelHandlerContext ctx, ByteBufferWrapper wrapper) throws Exception {
+ int originReaderIndex = wrapper.readerIndex();
+
+ byte type = wrapper.readByte();
+ try {
+ RemotingCommand cmd = decode(wrapper, originReaderIndex);
+ if (cmd != null) {
+ cmd.protocolType(type);
+ }
+ return cmd;
+ } catch (final RemoteCodecException e) {
+ LOG.warn("Decode error {}, close the channel {}", e.getMessage(), ctx.channel());
+ ctx.channel().close().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ LOG.warn("Close channel {} because of error {},result is {}", ctx.channel(), e, future.isSuccess());
+ }
+ });
+ }
+ return null;
+ }
+
+ public RemotingCommand decode(final ByteBufferWrapper wrapper, final int originReaderIndex) {
+ // Full message isn't available yet, return nothing for now
+ if (wrapper.readableBytes() < CodecHelper.MIN_PROTOCOL_LEN - 1) {
+ wrapper.setReaderIndex(originReaderIndex);
+ return null;
+ }
+
+ int totalLength = wrapper.readInt();
+
+ if (totalLength <= 0) {
+ throw new IllegalArgumentException("Illegal total length " + totalLength);
+ }
+
+ if (totalLength > CodecHelper.PACKET_MAX_LEN) {
+ throw new IllegalArgumentException(String.format("Total length %d is more than limit %d", totalLength, CodecHelper.PACKET_MAX_LEN));
+ }
+
+ if (wrapper.readableBytes() < totalLength) {
+ wrapper.setReaderIndex(originReaderIndex);
+ return null;
+ }
+
+ ByteBuffer totalBuffer = ByteBuffer.allocate(totalLength);
+
+ wrapper.readBytes(totalBuffer);
+
+ totalBuffer.flip();
+
+ return CodecHelper.decode(totalBuffer);
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Encoder.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Encoder.java
new file mode 100644
index 0000000..10aa504
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Encoder.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 org.apache.rocketmq.remoting.impl.netty.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import org.apache.rocketmq.remoting.api.buffer.ByteBufferWrapper;
+import org.apache.rocketmq.remoting.api.command.RemotingCommand;
+import org.apache.rocketmq.remoting.api.serializable.Serializer;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+import org.apache.rocketmq.remoting.impl.buffer.NettyByteBufferWrapper;
+import org.apache.rocketmq.remoting.impl.command.CodecHelper;
+import org.apache.rocketmq.remoting.impl.protocol.serializer.SerializerFactoryImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Encoder extends MessageToByteEncoder<RemotingCommand> {
+ private static final Logger LOG = LoggerFactory.getLogger(Encoder.class);
+
+ private final SerializerFactory serializerFactory = new SerializerFactoryImpl();
+
+ public Encoder() {
+ }
+
+ @Override
+ public void encode(final ChannelHandlerContext ctx, RemotingCommand remotingCommand, ByteBuf out) throws Exception {
+ try {
+ ByteBufferWrapper wrapper = new NettyByteBufferWrapper(out);
+
+ encode(serializerFactory, remotingCommand, wrapper);
+ } catch (final Exception e) {
+ LOG.error("Error occurred when encoding response for channel " + ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress(), e);
+ if (remotingCommand != null) {
+ LOG.error(remotingCommand.toString());
+ }
+ ctx.channel().close().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ LOG.warn("Close channel {} because of error {},result is {}", ctx.channel(), e, future.isSuccess());
+ }
+ });
+ }
+ }
+
+ public void encode(final SerializerFactory serializerFactory, final RemotingCommand remotingCommand,
+ final ByteBufferWrapper out) {
+ ByteBuffer encodeParameter = null;
+ if (remotingCommand.parameterBytes() != null) {
+ encodeParameter = ByteBuffer.wrap(remotingCommand.parameterBytes());
+ } else if (remotingCommand.parameter() != null) {
+ final Serializer serialization = serializerFactory.get(remotingCommand.serializerType());
+ encodeParameter = serialization.encode(remotingCommand.parameter());
+ }
+
+ int parameterLength = encodeParameter != null ? encodeParameter.limit() : 0;
+ int extBodyLength = remotingCommand.extraPayload() != null ? remotingCommand.extraPayload().length : 0;
+
+ ByteBuffer header = CodecHelper.encodeHeader(remotingCommand, parameterLength, extBodyLength);
+ out.writeBytes(header);
+
+ if (encodeParameter != null) {
+ out.writeBytes(encodeParameter);
+ }
+
+ if (remotingCommand.extraPayload() != null) {
+ out.writeBytes(remotingCommand.extraPayload());
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ExceptionHandler.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ExceptionHandler.java
new file mode 100644
index 0000000..52563f4
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ExceptionHandler.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.remoting.impl.netty.handler;
+
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ChannelHandler.Sharable
+public class ExceptionHandler extends ChannelDuplexHandler {
+ private final static Logger LOG = LoggerFactory.getLogger(ExceptionHandler.class);
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ // Uncaught exceptions from inbound handlers will propagate up to this handler
+ LOG.error(String.format("channel exception %s occurred ! ", ctx.channel()), cause);
+ ctx.close();
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Http2Handler.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Http2Handler.java
new file mode 100644
index 0000000..7cdb976
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/Http2Handler.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http2.DefaultHttp2Connection;
+import io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder;
+import io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder;
+import io.netty.handler.codec.http2.DefaultHttp2FrameReader;
+import io.netty.handler.codec.http2.DefaultHttp2FrameWriter;
+import io.netty.handler.codec.http2.DefaultHttp2Headers;
+import io.netty.handler.codec.http2.DefaultHttp2HeadersDecoder;
+import io.netty.handler.codec.http2.DefaultHttp2LocalFlowController;
+import io.netty.handler.codec.http2.Http2Connection;
+import io.netty.handler.codec.http2.Http2ConnectionDecoder;
+import io.netty.handler.codec.http2.Http2ConnectionEncoder;
+import io.netty.handler.codec.http2.Http2ConnectionHandler;
+import io.netty.handler.codec.http2.Http2Exception;
+import io.netty.handler.codec.http2.Http2FrameAdapter;
+import io.netty.handler.codec.http2.Http2FrameReader;
+import io.netty.handler.codec.http2.Http2FrameWriter;
+import io.netty.handler.codec.http2.Http2Headers;
+import io.netty.handler.codec.http2.Http2HeadersDecoder;
+import io.netty.handler.codec.http2.Http2Settings;
+import io.netty.handler.codec.http2.StreamBufferingEncoder;
+
+import static io.netty.handler.codec.http.HttpResponseStatus.OK;
+import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO;
+
+public class Http2Handler extends Http2ConnectionHandler {
+
+ private boolean isServer;
+ private int lastStreamId;
+
+ private Http2Handler(final Http2ConnectionDecoder decoder, final Http2ConnectionEncoder encoder,
+ final Http2Settings initialSettings, final boolean isServer) {
+ super(decoder, encoder, initialSettings);
+ decoder.frameListener(new FrameListener());
+ this.isServer = isServer;
+ }
+
+ public static Http2Handler newHandler(final boolean isServer) {
+
+ Http2HeadersDecoder headersDecoder = new DefaultHttp2HeadersDecoder(true);
+ Http2FrameReader frameReader = new DefaultHttp2FrameReader(headersDecoder);
+ Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter();
+
+ Http2Connection connection = new DefaultHttp2Connection(isServer);
+
+ Http2ConnectionEncoder encoder = new StreamBufferingEncoder(
+ new DefaultHttp2ConnectionEncoder(connection, frameWriter));
+
+ connection.local().flowController(new DefaultHttp2LocalFlowController(connection,
+ DEFAULT_WINDOW_UPDATE_RATIO, true));
+
+ Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder,
+ frameReader);
+
+ Http2Settings settings = new Http2Settings();
+
+ if (!isServer)
+ settings.pushEnabled(true);
+
+ settings.initialWindowSize(1048576 * 10); //10MiB
+ settings.maxConcurrentStreams(Integer.MAX_VALUE);
+
+ return newHandler(decoder, encoder, settings, isServer);
+ }
+
+ private static Http2Handler newHandler(final Http2ConnectionDecoder decoder, final Http2ConnectionEncoder encoder,
+ final Http2Settings settings, boolean isServer) {
+ return new Http2Handler(decoder, encoder, settings, isServer);
+ }
+
+ @Override
+ public void write(final ChannelHandlerContext ctx, final Object msg,
+ final ChannelPromise promise) throws Exception {
+ if (isServer) {
+ assert msg instanceof ByteBuf;
+ sendAPushPromise(ctx, lastStreamId, lastStreamId + 1, (ByteBuf) msg);
+ } else {
+
+ final Http2Headers headers = new DefaultHttp2Headers();
+
+ try {
+ long threadId = Thread.currentThread().getId();
+ long streamId = (threadId % 2 == 0) ? threadId + 1 : threadId + 2;
+ encoder().writeHeaders(ctx, (int) streamId, headers, 0, false, promise);
+ encoder().writeData(ctx, (int) streamId, (ByteBuf) msg, 0, false, ctx.newPromise());
+ ctx.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ private void sendAPushPromise(ChannelHandlerContext ctx, int streamId, int pushPromiseStreamId,
+ ByteBuf payload) throws Http2Exception {
+
+ encoder().writePushPromise(ctx, streamId, pushPromiseStreamId,
+ new DefaultHttp2Headers().status(OK.codeAsText()), 0, ctx.newPromise());
+
+ //Http2Stream stream = connection.local().reservePushStream(pushPromiseStreamId, connection.connectionStream());
+ Http2Headers headers = new DefaultHttp2Headers();
+ headers.status(OK.codeAsText());
+ encoder().writeHeaders(ctx, pushPromiseStreamId, headers, 0, false, ctx.newPromise());
+ encoder().writeData(ctx, pushPromiseStreamId, payload, 0, false, ctx.newPromise());
+ }
+
+ private class FrameListener extends Http2FrameAdapter {
+ @Override
+ public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
+ boolean endOfStream) throws Http2Exception {
+ //Http2Handler.this.onDataRead(ctx, streamId, data, endOfStream);
+ data.retain();
+ Http2Handler.this.lastStreamId = streamId;
+ ctx.fireChannelRead(data);
+ return data.readableBytes() + padding;
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ProtocolSelector.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ProtocolSelector.java
new file mode 100644
index 0000000..e00a213
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/netty/handler/ProtocolSelector.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.netty.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.ssl.SslContext;
+import org.apache.rocketmq.remoting.api.channel.ChannelHandlerContextWrapper;
+import org.apache.rocketmq.remoting.api.protocol.Protocol;
+import org.apache.rocketmq.remoting.api.protocol.ProtocolFactory;
+import org.apache.rocketmq.remoting.impl.channel.ChannelHandlerContextWrapperImpl;
+import org.apache.rocketmq.remoting.impl.protocol.ProtocolFactoryImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProtocolSelector extends SimpleChannelInboundHandler<ByteBuf> {
+ public static final String NAME = ProtocolSelector.class.getSimpleName();
+ private static final Logger LOG = LoggerFactory.getLogger(ProtocolSelector.class);
+ private ProtocolFactory protocolFactory;
+
+ public ProtocolSelector(final SslContext sslContext) {
+ this.protocolFactory = new ProtocolFactoryImpl(sslContext);
+ }
+
+ @Override
+ protected void channelRead0(final ChannelHandlerContext ctx, final ByteBuf msg) throws Exception {
+ if (msg.readableBytes() < 1) {
+ return;
+ }
+ msg.markReaderIndex();
+ Protocol protocol = protocolFactory.get(msg.readByte());
+ if (protocol == null) {
+ ctx.channel().close().addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ LOG.warn("Close channel {},result is {}", ctx.channel(), future.isSuccess());
+ }
+ });
+ return;
+ }
+ ChannelHandlerContextWrapper chcw = new ChannelHandlerContextWrapperImpl(ctx);
+ protocol.assembleHandler(chcw);
+ msg.resetReaderIndex();
+ ctx.pipeline().remove(this);
+ ctx.fireChannelRead(msg.retain());
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/Httpv2Protocol.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/Httpv2Protocol.java
new file mode 100644
index 0000000..1dc371d
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/Httpv2Protocol.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.remoting.impl.protocol;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.ssl.SslContext;
+import org.apache.rocketmq.remoting.api.channel.ChannelHandlerContextWrapper;
+import org.apache.rocketmq.remoting.impl.netty.handler.Http2Handler;
+import org.apache.rocketmq.remoting.impl.netty.handler.ProtocolSelector;
+
+public class Httpv2Protocol extends RemotingCoreProtocol {
+ private SslContext sslContext;
+
+ public Httpv2Protocol(final SslContext sslContext) {
+ this.sslContext = sslContext;
+ }
+
+ @Override
+ public String name() {
+ return HTTP2;
+ }
+
+ @Override
+ public byte type() {
+ return HTTP_2_MAGIC;
+ }
+
+ @Override
+ public void assembleHandler(final ChannelHandlerContextWrapper ctx) {
+ super.assembleHandler(ctx);
+ ChannelHandlerContext chx = (ChannelHandlerContext) ctx.getContext();
+
+ chx.pipeline().addAfter(ProtocolSelector.NAME, "sslHandler", sslContext.newHandler(chx.alloc()));
+ chx.pipeline().addAfter("sslHandler", "http2Handler", Http2Handler.newHandler(true));
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/ProtocolFactoryImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/ProtocolFactoryImpl.java
new file mode 100644
index 0000000..15322be
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/ProtocolFactoryImpl.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.remoting.impl.protocol;
+
+import io.netty.handler.ssl.SslContext;
+import org.apache.rocketmq.remoting.api.protocol.Protocol;
+import org.apache.rocketmq.remoting.api.protocol.ProtocolFactory;
+
+public class ProtocolFactoryImpl implements ProtocolFactory {
+ private static final int MAX_COUNT = 0x0FF;
+ private final Protocol[] tables = new Protocol[MAX_COUNT];
+
+ private SslContext sslContext;
+
+ public ProtocolFactoryImpl(final SslContext sslContext) {
+ this.sslContext = sslContext;
+ this.register(new RemotingCoreProtocol());
+ this.register(new Httpv2Protocol(sslContext));
+ this.register(new WebSocketProtocol());
+ }
+
+ public ProtocolFactoryImpl() {
+ this.register(new RemotingCoreProtocol());
+ this.register(new Httpv2Protocol(sslContext));
+ this.register(new WebSocketProtocol());
+ }
+
+ @Override
+ public void register(Protocol protocol) {
+ if (tables[protocol.type() & MAX_COUNT] != null) {
+ throw new RuntimeException("protocol header's sign is overlapped");
+ }
+ tables[protocol.type() & MAX_COUNT] = protocol;
+ }
+
+ @Override
+ public void resetAll(final Protocol protocol) {
+ for (int i = 0; i < MAX_COUNT; i++) {
+ tables[i] = protocol;
+ }
+ }
+
+ @Override
+ public byte type(final String protocolName) {
+
+ for (int i = 0; i < this.tables.length; i++) {
+ if (this.tables[i] != null) {
+ if (this.tables[i].name().equalsIgnoreCase(protocolName)) {
+ return this.tables[i].type();
+ }
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("the protocol: %s not exist", protocolName));
+ }
+
+ @Override
+ public Protocol get(byte type) {
+ return tables[type & MAX_COUNT];
+ }
+
+ @Override
+ public void clearAll() {
+ for (int i = 0; i < this.tables.length; i++) {
+ this.tables[i] = null;
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/RemotingCoreProtocol.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/RemotingCoreProtocol.java
new file mode 100644
index 0000000..317b24f
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/RemotingCoreProtocol.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.remoting.impl.protocol;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.rocketmq.remoting.api.channel.ChannelHandlerContextWrapper;
+import org.apache.rocketmq.remoting.api.protocol.Protocol;
+import org.apache.rocketmq.remoting.impl.netty.handler.Decoder;
+import org.apache.rocketmq.remoting.impl.netty.handler.Encoder;
+import org.apache.rocketmq.remoting.impl.netty.handler.ProtocolSelector;
+
+public class RemotingCoreProtocol implements Protocol {
+ @Override
+ public String name() {
+ return MVP;
+ }
+
+ @Override
+ public byte type() {
+ return MVP_MAGIC;
+ }
+
+ @Override
+ public void assembleHandler(final ChannelHandlerContextWrapper ctx) {
+
+ ChannelHandlerContext chx = (ChannelHandlerContext) ctx.getContext();
+
+ chx.pipeline().addAfter(ProtocolSelector.NAME, "decoder", new Decoder());
+ chx.pipeline().addAfter("decoder", "encoder", new Encoder());
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/WebSocketProtocol.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/WebSocketProtocol.java
new file mode 100644
index 0000000..18a3a11
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/WebSocketProtocol.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.remoting.impl.protocol;
+
+import org.apache.rocketmq.remoting.api.channel.ChannelHandlerContextWrapper;
+import org.apache.rocketmq.remoting.api.protocol.Protocol;
+
+public class WebSocketProtocol implements Protocol {
+ @Override
+ public String name() {
+ return WEBSOCKET;
+ }
+
+ @Override
+ public byte type() {
+ return WEBSOCKET_MAGIC;
+ }
+
+ @Override
+ public void assembleHandler(final ChannelHandlerContextWrapper ctx) {
+
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/compression/CompressorFactoryImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/compression/CompressorFactoryImpl.java
new file mode 100644
index 0000000..10e97ba
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/compression/CompressorFactoryImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.protocol.compression;
+
+import org.apache.rocketmq.remoting.api.compressable.Compressor;
+import org.apache.rocketmq.remoting.api.compressable.CompressorFactory;
+
+public class CompressorFactoryImpl implements CompressorFactory {
+ private static final int MAX_COUNT = 0x0FF;
+ private final Compressor[] tables = new Compressor[MAX_COUNT];
+
+ public CompressorFactoryImpl() {
+ this.register(new GZipCompressor());
+ }
+
+ @Override
+ public void register(Compressor compressor) {
+ if (tables[compressor.type() & MAX_COUNT] != null) {
+ throw new RuntimeException("compressor header's sign is overlapped");
+ }
+ tables[compressor.type() & MAX_COUNT] = compressor;
+ }
+
+ @Override
+ public byte type(String compressionName) {
+ for (Compressor table : this.tables) {
+ if (table != null) {
+ if (table.name().equalsIgnoreCase(compressionName)) {
+ return table.type();
+ }
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("the compressor: %s not exist", compressionName));
+
+ }
+
+ @Override
+ public Compressor get(byte type) {
+ return tables[type & MAX_COUNT];
+ }
+
+ @Override
+ public void clearAll() {
+ for (int i = 0; i < this.tables.length; i++) {
+ this.tables[i] = null;
+ }
+ }
+
+ public Compressor[] getTables() {
+ return tables;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/compression/GZipCompressor.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/compression/GZipCompressor.java
new file mode 100644
index 0000000..fc33f4c
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/compression/GZipCompressor.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.protocol.compression;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import org.apache.rocketmq.remoting.api.compressable.Compressor;
+
+public class GZipCompressor implements Compressor {
+ public static final int BUFFER = 1024;
+ public static final String COMPRESSOR_NAME = GZipCompressor.class.getSimpleName();
+ public static final byte COMPRESSOR_TYPE = 'G';
+
+ @Override
+ public String name() {
+ return COMPRESSOR_NAME;
+ }
+
+ @Override
+ public byte type() {
+ return COMPRESSOR_TYPE;
+ }
+
+ @Override
+ public byte[] compress(byte[] content) throws Exception {
+ if (content == null)
+ return new byte[0];
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(content);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ compress(bais, baos);
+ byte[] output = baos.toByteArray();
+ baos.flush();
+ baos.close();
+ bais.close();
+ return output;
+
+ }
+
+ private void compress(InputStream is, OutputStream os) throws Exception {
+ GZIPOutputStream gos = new GZIPOutputStream(os);
+
+ int count;
+ byte data[] = new byte[BUFFER];
+ while ((count = is.read(data, 0, BUFFER)) != -1) {
+ gos.write(data, 0, count);
+ }
+ gos.finish();
+ gos.flush();
+ gos.close();
+ }
+
+ @Override
+ public byte[] deCompress(byte[] content) throws Exception {
+ if (content == null)
+ return new byte[0];
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(content);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ decompress(bais, baos);
+ content = baos.toByteArray();
+ baos.flush();
+ baos.close();
+ bais.close();
+ return content;
+ }
+
+ private void decompress(InputStream is, OutputStream os) throws Exception {
+ GZIPInputStream gis = new GZIPInputStream(is);
+
+ int count;
+ byte data[] = new byte[BUFFER];
+ while ((count = gis.read(data, 0, BUFFER)) != -1) {
+ os.write(data, 0, count);
+ }
+ gis.close();
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/JsonSerializer.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/JsonSerializer.java
new file mode 100644
index 0000000..c85d44b
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/JsonSerializer.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.protocol.serializer;
+
+import com.alibaba.fastjson.JSON;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+import org.apache.rocketmq.remoting.api.serializable.Serializer;
+import org.apache.rocketmq.remoting.common.TypePresentation;
+import org.apache.rocketmq.remoting.impl.command.CodecHelper;
+
+public class JsonSerializer implements Serializer {
+ public static final String SERIALIZER_NAME = JsonSerializer.class.getSimpleName();
+ public static final byte SERIALIZER_TYPE = 'J';
+
+ public JsonSerializer() {
+ }
+
+ @Override
+ public String name() {
+ return SERIALIZER_NAME;
+ }
+
+ @Override
+ public byte type() {
+ return SERIALIZER_TYPE;
+ }
+
+ @Override
+ public <T> T decode(final byte[] content, final Class<T> c) {
+ if (content != null) {
+ try {
+ final String jsonString = new String(content, CodecHelper.REMOTING_CHARSET);
+ return JSON.parseObject(jsonString, c);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public <T> T decode(final byte[] content, final TypePresentation<T> typePresentation) {
+ return decode(content, typePresentation.getType());
+ }
+
+ @Override
+ public <T> T decode(byte[] content, Type type) {
+ if (content != null) {
+ try {
+ final String jsonString = new String(content, CodecHelper.REMOTING_CHARSET);
+ return JSON.parseObject(jsonString, type);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ByteBuffer encode(final Object object) {
+ if (object != null) {
+ String jsonString = JSON.toJSONString(object);
+ byte[] bytes = jsonString.getBytes(CodecHelper.REMOTING_CHARSET);
+ try {
+ return ByteBuffer.wrap(bytes, 0, bytes.length);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/Kryo3Serializer.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/Kryo3Serializer.java
new file mode 100644
index 0000000..06ea217
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/Kryo3Serializer.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.protocol.serializer;
+
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+import org.apache.rocketmq.remoting.api.serializable.Serializer;
+import org.apache.rocketmq.remoting.common.TypePresentation;
+
+public class Kryo3Serializer implements Serializer {
+ public static final String SERIALIZER_NAME = Kryo3Serializer.class.getSimpleName();
+ public static final byte SERIALIZER_TYPE = 'K';
+
+ public Kryo3Serializer() {
+ }
+
+ @Override
+ public String name() {
+ return SERIALIZER_NAME;
+ }
+
+ @Override
+ public byte type() {
+ return SERIALIZER_TYPE;
+ }
+
+ @Override
+ public <T> T decode(final byte[] content, final Class<T> c) {
+ if (content != null) {
+ Input input = null;
+ try {
+ input = new Input(content);
+ return (T) ThreadSafeKryo.getKryoInstance().readClassAndObject(input);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ input.close();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public <T> T decode(final byte[] content, final TypePresentation<T> typePresentation) {
+ return decode(content, typePresentation.getType());
+ }
+
+ @Override
+ public <T> T decode(byte[] content, Type type) {
+ if (type instanceof ParameterizedType) {
+ return decode(content, (Class<? extends T>) ((ParameterizedType) type).getRawType());
+ } else if (type instanceof Class) {
+ return decode(content, (Class<? extends T>) type);
+ }
+ return null;
+ }
+
+ @Override
+ public ByteBuffer encode(final Object object) {
+ if (object != null) {
+ try (Output output = new Output(1024, 1024 * 1024 * 6)) {
+ ThreadSafeKryo.getKryoInstance().writeClassAndObject(output, object);
+ return ByteBuffer.wrap(output.getBuffer(), 0, output.position());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/MsgPackSerializer.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/MsgPackSerializer.java
new file mode 100644
index 0000000..1097f8f
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/MsgPackSerializer.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.protocol.serializer;
+
+import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
+import org.apache.rocketmq.remoting.api.serializable.Serializer;
+import org.apache.rocketmq.remoting.common.TypePresentation;
+import org.msgpack.MessagePack;
+import org.msgpack.template.Template;
+
+public class MsgPackSerializer implements Serializer {
+ public static final String SERIALIZER_NAME = MsgPackSerializer.class.getSimpleName();
+ public static final byte SERIALIZER_TYPE = 'M';
+ private final MessagePack messagePack = new MessagePack();
+
+ public MsgPackSerializer() {
+ }
+
+ @Override
+ public String name() {
+ return SERIALIZER_NAME;
+ }
+
+ @Override
+ public byte type() {
+ return SERIALIZER_TYPE;
+ }
+
+ @Override
+ public <T> T decode(final byte[] content, final Class<T> c) {
+ try {
+ return messagePack.read(content, c);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public <T> T decode(final byte[] content, final TypePresentation<T> typePresentation) {
+ return decode(content, typePresentation.getType());
+ }
+
+ @Override
+ public <T> T decode(byte[] content, Type type) {
+ Template<T> template = (Template<T>) messagePack.lookup(type);
+ try {
+ return messagePack.read(content, template);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ByteBuffer encode(final Object object) {
+ try {
+ byte[] data = messagePack.write(object);
+ return ByteBuffer.wrap(data);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/SerializerFactoryImpl.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/SerializerFactoryImpl.java
new file mode 100644
index 0000000..632b61f
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/SerializerFactoryImpl.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.remoting.impl.protocol.serializer;
+
+import org.apache.rocketmq.remoting.api.serializable.Serializer;
+import org.apache.rocketmq.remoting.api.serializable.SerializerFactory;
+
+public class SerializerFactoryImpl implements SerializerFactory {
+ private static final int MAX_COUNT = 0x0FF;
+ private final Serializer[] tables = new Serializer[MAX_COUNT];
+
+ public SerializerFactoryImpl() {
+ this.register(new JsonSerializer());
+ this.register(new Kryo3Serializer());
+ this.register(new MsgPackSerializer());
+ }
+
+ @Override
+ public void register(Serializer serialization) {
+ if (tables[serialization.type() & MAX_COUNT] != null) {
+ throw new RuntimeException("serialization header's sign is overlapped");
+ }
+ tables[serialization.type() & MAX_COUNT] = serialization;
+ }
+
+ @Override
+ public byte type(final String serializationName) {
+ for (Serializer table : this.tables) {
+ if (table != null) {
+ if (table.name().equalsIgnoreCase(serializationName)) {
+ return table.type();
+ }
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("the serialization: %s not exist", serializationName));
+ }
+
+ @Override
+ public Serializer get(byte type) {
+ return tables[type & MAX_COUNT];
+ }
+
+ @Override
+ public void clearAll() {
+ for (int i = 0; i < this.tables.length; i++) {
+ this.tables[i] = null;
+ }
+ }
+
+ public Serializer[] getTables() {
+ return tables;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/ThreadSafeKryo.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/ThreadSafeKryo.java
new file mode 100644
index 0000000..cadfc27
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/impl/protocol/serializer/ThreadSafeKryo.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.impl.protocol.serializer;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.KryoSerializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Currency;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.objenesis.strategy.StdInstantiatorStrategy;
+
+public class ThreadSafeKryo {
+ private static final ThreadLocal<Kryo> KRYOS = new ThreadLocal<Kryo>() {
+ protected Kryo initialValue() {
+ Kryo kryo = new Kryo();
+
+ kryo.register(byte[].class);
+ kryo.register(char[].class);
+ kryo.register(short[].class);
+ kryo.register(int[].class);
+ kryo.register(long[].class);
+ kryo.register(float[].class);
+ kryo.register(double[].class);
+ kryo.register(boolean[].class);
+ kryo.register(String[].class);
+ kryo.register(Object[].class);
+ kryo.register(KryoSerializable.class);
+ kryo.register(BigInteger.class);
+ kryo.register(BigDecimal.class);
+ kryo.register(Class.class);
+ kryo.register(Date.class);
+ // kryo.register(Enum.class);
+ kryo.register(EnumSet.class);
+ kryo.register(Currency.class);
+ kryo.register(StringBuffer.class);
+ kryo.register(StringBuilder.class);
+ kryo.register(Collections.EMPTY_LIST.getClass());
+ kryo.register(Collections.EMPTY_MAP.getClass());
+ kryo.register(Collections.EMPTY_SET.getClass());
+ kryo.register(Collections.singletonList(null).getClass());
+ kryo.register(Collections.singletonMap(null, null).getClass());
+ kryo.register(Collections.singleton(null).getClass());
+ kryo.register(TreeSet.class);
+ kryo.register(Collection.class);
+ kryo.register(TreeMap.class);
+ kryo.register(Map.class);
+ try {
+ kryo.register(Class.forName("sun.util.calendar.ZoneInfo"));
+ } catch (ClassNotFoundException e) {
+ // Noop
+ }
+ kryo.register(Calendar.class);
+ kryo.register(Locale.class);
+
+ kryo.register(BitSet.class);
+ kryo.register(HashMap.class);
+ kryo.register(Timestamp.class);
+ kryo.register(ArrayList.class);
+
+ // kryo.setRegistrationRequired(true);
+ kryo.setReferences(false);
+ kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
+
+ return kryo;
+ }
+ };
+
+ public static Kryo getKryoInstance() {
+ return KRYOS.get();
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/BeanUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/BeanUtils.java
new file mode 100644
index 0000000..0177990
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/BeanUtils.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.internal;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BeanUtils {
+ private final static Logger LOG = LoggerFactory.getLogger(BeanUtils.class);
+
+ /**
+ * <p>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 <Code>String</Code>, <Code>boolean</Code>,
+ * <Code>int</Code>, <Code>long</Code>, <Code>float</Code>, and
+ * <Code>double</Code>.</p>
+ *
+ * <p>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.</p>
+ *
+ * <p><strong>NOTE</strong>: It is contrary to the JavaBeans Specification
+ * to have more than one setter method (with different argument
+ * signatures) for the same property.</p>
+ *
+ * @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 <T> Class type
+ * @return Class instance
+ */
+ public static <T> T populate(final Properties properties, final Class<T> clazz) {
+ T obj = null;
+ try {
+ obj = clazz.newInstance();
+ return populate(properties, obj);
+ } catch (Throwable e) {
+ LOG.warn("Error occurs !", e);
+ }
+ return obj;
+ }
+
+ private static <T> void setField(final Field field, final Properties properties, final T obj) throws Exception {
+ Type fieldType = field.getType();
+ String fieldName = field.getName();
+
+ String value = null;
+ String configName = convertToConfigName(fieldName);
+ String envName = convertToEnvName(fieldName);
+
+ if (properties.containsKey(envName)) {
+ value = properties.getProperty(envName);
+ }
+
+ if (properties.containsKey(configName)) {
+ value = properties.getProperty(configName);
+ }
+
+ if (value == null) {
+ return;
+ }
+
+ if (fieldType == Boolean.TYPE) {
+ field.set(obj, Boolean.valueOf(value));
+ } else if (fieldType == Integer.TYPE) {
+ field.set(obj, Integer.valueOf(value));
+ } else if (fieldType == Double.TYPE) {
+ field.set(obj, Double.valueOf(value));
+ } else if (fieldType == Float.TYPE) {
+ field.set(obj, Float.valueOf(value));
+ } else if (fieldType == Long.TYPE) {
+ field.set(obj, Long.valueOf(value));
+ } else
+ field.set(obj, value);
+ }
+
+ private static String convertToConfigName(String variableName) {
+ StringBuilder sb = new StringBuilder();
+ for (char c : variableName.toCharArray()) {
+ if (Character.isUpperCase(c)) {
+ sb.append('.');
+ }
+ sb.append(Character.toLowerCase(c));
+ }
+ return sb.toString();
+ }
+
+ private static String convertToEnvName(String variableName) {
+ StringBuilder sb = new StringBuilder();
+ for (char c : variableName.toCharArray()) {
+ if (Character.isUpperCase(c)) {
+ sb.append('_');
+ }
+ sb.append(Character.toUpperCase(c));
+ }
+ return sb.toString();
+ }
+
+ public static <T> T populate(final Properties properties, final T obj) {
+ Class<?> clazz = obj.getClass();
+ List<Field> allFields = new ArrayList<>();
+ allFields = getAllFields(allFields, clazz);
+ Properties fullProp = extractProperties(properties);
+
+ try {
+ for (Field field : allFields) {
+ if (!Modifier.isStatic(field.getModifiers())) {
+ field.setAccessible(true);
+ setField(field, fullProp, obj);
+ }
+ }
+ } catch (Exception e) {
+ LOG.warn("Error occurs !", e);
+ }
+ return obj;
+ }
+
+ public static String configObjectToString(final Object object) {
+ List<Field> allFields = new ArrayList<>();
+ getAllFields(allFields, object.getClass());
+ StringBuilder sb = new StringBuilder();
+ for (Field field : allFields) {
+ if (!Modifier.isStatic(field.getModifiers())) {
+ String name = field.getName();
+ if (!name.startsWith("this")) {
+ Object value = null;
+ try {
+ field.setAccessible(true);
+ value = field.get(object);
+ if (null == value) {
+ value = "";
+ }
+ } catch (IllegalAccessException ignored) {
+ }
+ sb.append(name).append("=").append(value).append("%n");
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private static List<Field> getAllFields(List<Field> fields, Class<?> type) {
+ fields.addAll(Arrays.asList(type.getDeclaredFields()));
+ if (type.getSuperclass() != null) {
+ getAllFields(fields, type.getSuperclass());
+ }
+ return fields;
+ }
+
+ private static Properties extractProperties(final Properties properties) {
+ Properties newPro = new Properties();
+
+ Map<String, String> envMap = System.getenv();
+ for (final Map.Entry<String, String> entry : envMap.entrySet()) {
+ newPro.setProperty(entry.getKey().toUpperCase(), entry.getValue());
+ newPro.setProperty(entry.getKey().toLowerCase(), entry.getValue()); //EnvProp supports A_B_C and a.b.c
+ }
+
+ Properties systemProp = System.getProperties(); //SystemProp supports a.b.c
+ for (final Map.Entry<Object, Object> entry : systemProp.entrySet()) {
+ newPro.setProperty(String.valueOf(entry.getKey()).toLowerCase(), String.valueOf(entry.getValue()));
+ }
+
+ Properties inner = null;
+ try {
+ Field field = Properties.class.getDeclaredField("defaults");
+ field.setAccessible(true);
+ inner = (Properties) field.get(properties);
+ } catch (Exception ignore) {
+ }
+
+ if (inner != null) {
+ for (final Map.Entry<Object, Object> entry : inner.entrySet()) {
+ newPro.setProperty(String.valueOf(entry.getKey()).toLowerCase(), String.valueOf(entry.getValue()));
+ }
+ }
+
+ for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
+ newPro.setProperty(String.valueOf(entry.getKey()).toLowerCase(), String.valueOf(entry.getValue()));
+ }
+
+ return newPro;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/ByteUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/ByteUtils.java
new file mode 100644
index 0000000..c298ce7
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/ByteUtils.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.internal;
+
+/**
+ * Copy from Bouncy Castle Crypto APIs
+ *
+ * This class is a utility class for manipulating byte arrays.
+ */
+public final class ByteUtils {
+
+ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * Default constructor (private)
+ */
+ private ByteUtils() {
+ // empty
+ }
+
+ /**
+ * Compare two byte arrays (perform null checks beforehand).
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[] left, byte[] right) {
+ if (left == null) {
+ return right == null;
+ }
+ if (right == null) {
+ return false;
+ }
+
+ if (left.length != right.length) {
+ return false;
+ }
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--) {
+ result &= left[i] == right[i];
+ }
+ return result;
+ }
+
+ /**
+ * Compare two two-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][] left, byte[][] right) {
+ if (left.length != right.length) {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--) {
+ result &= ByteUtils.equals(left[i], right[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compare two three-dimensional byte arrays. No null checks are performed.
+ *
+ * @param left the first byte array
+ * @param right the second byte array
+ * @return the result of the comparison
+ */
+ public static boolean equals(byte[][][] left, byte[][][] right) {
+ if (left.length != right.length) {
+ return false;
+ }
+
+ boolean result = true;
+ for (int i = left.length - 1; i >= 0; i--) {
+ if (left[i].length != right[i].length) {
+ return false;
+ }
+ for (int j = left[i].length - 1; j >= 0; j--) {
+ result &= ByteUtils.equals(left[i][j], right[i][j]);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a one-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[] array) {
+ int result = 1;
+ for (int i = 0; i < array.length; i++) {
+ result = 31 * result + array[i];
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a two-dimensional byte array
+ * rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][] array) {
+ int result = 1;
+ for (int i = 0; i < array.length; i++) {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Computes a hashcode based on the contents of a three-dimensional byte
+ * array rather than its identity.
+ *
+ * @param array the array to compute the hashcode of
+ * @return the hashcode
+ */
+ public static int deepHashCode(byte[][][] array) {
+ int result = 1;
+ for (int i = 0; i < array.length; i++) {
+ result = 31 * result + deepHashCode(array[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Return a clone of the given byte array (performs null check beforehand).
+ *
+ * @param array the array to clone
+ * @return the clone of the given array, or <tt>null</tt> if the array is
+ * <tt>null</tt>
+ */
+ public static byte[] clone(byte[] array) {
+ if (array == null) {
+ return null;
+ }
+ byte[] result = new byte[array.length];
+ System.arraycopy(array, 0, result, 0, array.length);
+ return result;
+ }
+
+ /**
+ * Convert a string containing hexadecimal characters to a byte-array.
+ *
+ * @param s a hex string
+ * @return a byte array with the corresponding value
+ */
+ public static byte[] fromHexString(String s) {
+ char[] rawChars = s.toUpperCase().toCharArray();
+
+ int hexChars = 0;
+ for (int i = 0; i < rawChars.length; i++) {
+ if ((rawChars[i] >= '0' && rawChars[i] <= '9')
+ || (rawChars[i] >= 'A' && rawChars[i] <= 'F')) {
+ hexChars++;
+ }
+ }
+
+ byte[] byteString = new byte[(hexChars + 1) >> 1];
+
+ int pos = hexChars & 1;
+
+ for (int i = 0; i < rawChars.length; i++) {
+ if (rawChars[i] >= '0' && rawChars[i] <= '9') {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - '0';
+ } else if (rawChars[i] >= 'A' && rawChars[i] <= 'F') {
+ byteString[pos >> 1] <<= 4;
+ byteString[pos >> 1] |= rawChars[i] - 'A' + 10;
+ } else {
+ continue;
+ }
+ pos++;
+ }
+
+ return byteString;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hexstring.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding hexstring
+ */
+ public static String toHexString(byte[] input) {
+ String result = "";
+ for (int i = 0; i < input.length; i++) {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding hex string.
+ *
+ * @param input the byte array to be converted
+ * @param prefix the prefix to put at the beginning of the hex string
+ * @param seperator a separator string
+ * @return the corresponding hex string
+ */
+ public static String toHexString(byte[] input, String prefix,
+ String seperator) {
+ String result = new String(prefix);
+ for (int i = 0; i < input.length; i++) {
+ result += HEX_CHARS[(input[i] >>> 4) & 0x0f];
+ result += HEX_CHARS[(input[i]) & 0x0f];
+ if (i < input.length - 1) {
+ result += seperator;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convert a byte array to the corresponding bit string.
+ *
+ * @param input the byte array to be converted
+ * @return the corresponding bit string
+ */
+ public static String toBinaryString(byte[] input) {
+ String result = "";
+ int i;
+ for (i = 0; i < input.length; i++) {
+ int e = input[i];
+ for (int ii = 0; ii < 8; ii++) {
+ int b = (e >>> ii) & 1;
+ result += b;
+ }
+ if (i != input.length - 1) {
+ result += " ";
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Compute the bitwise XOR of two arrays of bytes. The arrays have to be of
+ * same length. No length checking is performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return x1 XOR x2
+ */
+ public static byte[] xor(byte[] x1, byte[] x2) {
+ byte[] out = new byte[x1.length];
+
+ for (int i = x1.length - 1; i >= 0; i--) {
+ out[i] = (byte) (x1[i] ^ x2[i]);
+ }
+ return out;
+ }
+
+ /**
+ * Concatenate two byte arrays. No null checks are performed.
+ *
+ * @param x1 the first array
+ * @param x2 the second array
+ * @return (x2||x1) (little-endian order, i.e. x1 is at lower memory
+ * addresses)
+ */
+ public static byte[] concatenate(byte[] x1, byte[] x2) {
+ byte[] result = new byte[x1.length + x2.length];
+
+ System.arraycopy(x1, 0, result, 0, x1.length);
+ System.arraycopy(x2, 0, result, x1.length, x2.length);
+
+ return result;
+ }
+
+ /**
+ * Convert a 2-dimensional byte array into a 1-dimensional byte array by
+ * concatenating all entries.
+ *
+ * @param array a 2-dimensional byte array
+ * @return the concatenated input array
+ */
+ public static byte[] concatenate(byte[][] array) {
+ int rowLength = array[0].length;
+ byte[] result = new byte[array.length * rowLength];
+ int index = 0;
+ for (int i = 0; i < array.length; i++) {
+ System.arraycopy(array[i], 0, result, index, rowLength);
+ index += rowLength;
+ }
+ return result;
+ }
+
+ /**
+ * Split a byte array <tt>input</tt> into two arrays at <tt>index</tt>,
+ * i.e. the first array will have the lower <tt>index</tt> bytes, the
+ * second one the higher <tt>input.length - index</tt> bytes.
+ *
+ * @param input the byte array to be split
+ * @param index the index where the byte array is split
+ * @return the splitted input array as an array of two byte arrays
+ * @throws ArrayIndexOutOfBoundsException if <tt>index</tt> is out of bounds
+ */
+ public static byte[][] split(byte[] input, int index)
+ throws ArrayIndexOutOfBoundsException {
+ if (index > input.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ byte[][] result = new byte[2][];
+ result[0] = new byte[index];
+ result[1] = new byte[input.length - index];
+ System.arraycopy(input, 0, result[0], 0, index);
+ System.arraycopy(input, index, result[1], 0, input.length - index);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @param end the end index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt>
+ * (inclusively) to <tt>end</tt> (exclusively)
+ */
+ public static byte[] subArray(byte[] input, int start, int end) {
+ byte[] result = new byte[end - start];
+ System.arraycopy(input, start, result, 0, end - start);
+ return result;
+ }
+
+ /**
+ * Generate a subarray of a given byte array.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @return a subarray of <tt>input</tt>, ranging from <tt>start</tt> to
+ * the end of the array
+ */
+ public static byte[] subArray(byte[] input, int start) {
+ return subArray(input, start, input.length);
+ }
+
+ /**
+ * Rewrite a byte array as a char array
+ *
+ * @param input -
+ * the byte array
+ * @return char array
+ */
+ public static char[] toCharArray(byte[] input) {
+ char[] result = new char[input.length];
+ for (int i = 0; i < input.length; i++) {
+ result[i] = (char) input[i];
+ }
+ return result;
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/ExceptionUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/ExceptionUtils.java
new file mode 100644
index 0000000..6386ca0
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/ExceptionUtils.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.remoting.internal;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class ExceptionUtils {
+
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ /**
+ * <p>Gets the stack trace from a Throwable as a String.</p>
+ *
+ * <p>The result of this method vary by JDK version as this method
+ * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
+ * On JDK1.3 and earlier, the cause exception will not be shown
+ * unless the specified throwable alters printStackTrace.</p>
+ *
+ * @param throwable the <code>Throwable</code> to be examined
+ * @return the stack trace as generated by the exception's
+ * <code>printStackTrace(PrintWriter)</code> method
+ */
+ public static String getStackTrace(final Throwable throwable) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw, true);
+ throwable.printStackTrace(pw);
+ return sw.getBuffer().toString();
+ }
+
+ /**
+ * <p>Produces a <code>List</code> of stack frames - the message
+ * is not included. Only the trace of the specified exception is
+ * returned, any caused by trace is stripped.</p>
+ *
+ * <p>This works in most cases - it will only fail if the exception
+ * message contains a line that starts with:
+ * <code>" at".</code></p>
+ *
+ * @param t is any throwable
+ * @return List of stack frames
+ */
+ static List<String> getStackFrameList(final Throwable t) {
+ final String stackTrace = getStackTrace(t);
+ final String linebreak = LINE_SEPARATOR;
+ final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
+ final List<String> list = new ArrayList<String>();
+ boolean traceStarted = false;
+ while (frames.hasMoreTokens()) {
+ final String token = frames.nextToken();
+ // Determine if the line starts with <whitespace>at
+ final int at = token.indexOf("at");
+ if (at != -1 && token.substring(0, at).trim().isEmpty()) {
+ traceStarted = true;
+ list.add(token);
+ } else if (traceStarted) {
+ break;
+ }
+ }
+ return list;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/JvmUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/JvmUtils.java
new file mode 100644
index 0000000..fb97a14
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/JvmUtils.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.remoting.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Random;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class JvmUtils {
+ public static final String OS_NAME = System.getProperty("os.name").toLowerCase();
+ private final static Logger LOG = LoggerFactory.getLogger(JvmUtils.class);
+ //public static final String OS_VERSION = System.getProperty("os.version").toLowerCase();
+
+ /**
+ * A constructor to stop this class being constructed.
+ */
+ private JvmUtils() {
+ // Unused
+ }
+
+ public static boolean isWindows() {
+ return OS_NAME.startsWith("win");
+ }
+
+ public static boolean isWindows10() {
+ return OS_NAME.startsWith("win") && OS_NAME.endsWith("10");
+ }
+
+ public static boolean isMacOSX() {
+ return OS_NAME.contains("mac");
+ }
+
+ public static boolean isLinux() {
+ return OS_NAME.startsWith("linux");
+ }
+
+ public static boolean isUnix() {
+ return OS_NAME.contains("nix") ||
+ OS_NAME.contains("nux") ||
+ OS_NAME.contains("aix") ||
+ OS_NAME.contains("bsd") ||
+ OS_NAME.contains("sun") ||
+ OS_NAME.contains("hpux");
+ }
+
+ public static boolean isSolaris() {
+ return OS_NAME.startsWith("sun");
+ }
+
+ public static int getProcessId() {
+ String pid = null;
+ final File self = new File("/proc/self");
+ try {
+ if (self.exists()) {
+ pid = self.getCanonicalFile().getName();
+ }
+ } catch (IOException ignored) {
+ //Ignore it
+ }
+
+ if (pid == null) {
+ pid = ManagementFactory.getRuntimeMXBean().getName().split("@", 0)[0];
+ }
+
+ if (pid == null) {
+ int rpid = new Random().nextInt(1 << 16);
+ LOG.warn("Unable to determine PID, picked a random number {}", rpid);
+
+ return rpid;
+ } else {
+ return Integer.parseInt(pid);
+ }
+ }
+
+}
+
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/NetworkUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/NetworkUtils.java
new file mode 100644
index 0000000..0e8ae21
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/NetworkUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.internal;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import org.jetbrains.annotations.NotNull;
+
+public final class NetworkUtils {
+
+ public static final String DEFAULT_LOCAL_ADDRESS = "127.0.0.1";
+ public static final String DEFAULT_LOCAL_HOSTNAME = "localhost";
+
+ /**
+ * A constructor to stop this class being constructed.
+ */
+ private NetworkUtils() {
+ // Unused
+ }
+
+ public static InetAddress getLoopbackAddress() {
+ try {
+ return InetAddress.getByName(null);
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static boolean isLocalhost(@NotNull String host) {
+ return host.equalsIgnoreCase(DEFAULT_LOCAL_HOSTNAME) || host.equals(DEFAULT_LOCAL_ADDRESS);
+ }
+
+ public static String getLocalHostIp() {
+ try {
+ for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
+ NetworkInterface iface = ifaces.nextElement();
+ // Workaround for docker0 bridge
+ if ("docker0".equals(iface.getName()) || !iface.isUp()) {
+ continue;
+ }
+ InetAddress ia;
+ for (Enumeration<InetAddress> ips = iface.getInetAddresses(); ips.hasMoreElements(); ) {
+ ia = ips.nextElement();
+ if (ia instanceof Inet4Address) {
+ // Check if the address is any local or loop back(127.0.0.1 or ::1)
+ if (!ia.isLoopbackAddress() && ia.getHostAddress().indexOf(':') == -1) {
+ if (ia.isSiteLocalAddress()) {
+ return ia.getHostAddress();
+ } else if (!ia.isLinkLocalAddress() && !ia.isAnyLocalAddress()
+ && !ia.isMulticastAddress()) {
+ return ia.getHostAddress();
+ }
+ }
+ }
+ }
+ }
+ } catch (SocketException e) {
+ throw new RuntimeException("Could not get local host ip", e);
+ }
+ return DEFAULT_LOCAL_ADDRESS;
+ }
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/PropertyUtils.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/PropertyUtils.java
new file mode 100644
index 0000000..7e76a7e
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/PropertyUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.remoting.internal;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class PropertyUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(PropertyUtils.class);
+
+ public static String getPropertyIgnoreCase(final Properties properties, final String key) {
+ String value = null;
+ if (properties != null) {
+ for (Map.Entry<Object, Object> next : properties.entrySet()) {
+ if (next.getKey().toString().equalsIgnoreCase(key)) {
+ return next.getValue().toString();
+ }
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Read a properties file from the given path
+ *
+ * @param filename The path of the file to read
+ * @return Property file instance
+ */
+ public static Properties loadProps(String filename) {
+ Properties props = new Properties();
+ try (InputStream propStream = new FileInputStream(filename)) {
+ props.load(propStream);
+ } catch (IOException e) {
+ LOG.error(String.format("Loading properties from file %s error !", filename), e);
+ System.exit(1);
+ }
+ return props;
+ }
+
+}
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/UIDGenerator.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/UIDGenerator.java
new file mode 100644
index 0000000..a4b1293
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/internal/UIDGenerator.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.remoting.internal;
+
+import java.nio.ByteBuffer;
+import java.util.Calendar;
+
+public class UIDGenerator {
+ private static ThreadLocal<UIDGenerator> generatorLocal = new ThreadLocal<UIDGenerator>() {
+ @Override
+ protected UIDGenerator initialValue() {
+ return new UIDGenerator();
+ }
+ };
+ private short counter;
+ private int basePos = 0;
+ private long startTime;
+ private long nextStartTime;
+ private StringBuilder sb = null;
+ private ByteBuffer buffer = ByteBuffer.allocate(6);
+
+ private UIDGenerator() {
+ int len = 4 + 2 + 4 + 4 + 2;
+
+ sb = new StringBuilder(len * 2);
+ ByteBuffer tempBuffer = ByteBuffer.allocate(len - buffer.limit());
+ tempBuffer.position(2);
+ tempBuffer.putInt(JvmUtils.getProcessId());
+ tempBuffer.position(0);
+ try {
+ tempBuffer.put((byte) 1);
+ } catch (Exception e) {
+ tempBuffer.put(createFakeIP());
+ }
+ tempBuffer.position(6);
+ tempBuffer.putInt(UIDGenerator.class.getClassLoader().hashCode());
+ sb.append(ByteUtils.toHexString(tempBuffer.array()));
+ basePos = sb.length();
+ setStartTime(System.currentTimeMillis());
+ counter = 0;
+ }
+
+ public static UIDGenerator instance() {
+ return generatorLocal.get();
+ }
+
+ public String createUID() {
+ long current = System.currentTimeMillis();
+ if (current >= nextStartTime) {
+ setStartTime(current);
+ }
+ buffer.position(0);
+ sb.setLength(basePos);
+ buffer.putInt((int) (System.currentTimeMillis() - startTime));
+ buffer.putShort(counter++);
+ sb.append(ByteUtils.toHexString(buffer.array()));
+ return sb.toString();
+ }
+
+ private void setStartTime(long millis) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(millis);
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.set(Calendar.HOUR, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ startTime = cal.getTimeInMillis();
+ cal.add(Calendar.MONTH, 1);
+ nextStartTime = cal.getTimeInMillis();
+ }
+
+ public byte[] createFakeIP() {
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ bb.putLong(System.currentTimeMillis());
+ bb.position(4);
+ byte[] fakeIP = new byte[4];
+ bb.get(fakeIP);
+ return fakeIP;
+ }
+}
+
diff --git a/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/package-info.java b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/package-info.java
new file mode 100644
index 0000000..e64f66b
--- /dev/null
+++ b/remoting-core/remoting-impl/src/main/java/org/apache/rocketmq/remoting/package-info.java
@@ -0,0 +1,49 @@
+/*
+ * 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 package contains all the transport classes that can be reused any times.
+ *
+ * Remoting wire-format protocol description:
+ *
+ * <pre>
+ * 2015-04-29 16:07:14 v1.0
+ * 2016-04-23 16:18:05 v2.0
+ * 2016-05-31 09:33:11 v3.0
+ * 2016-11-10 09:33:11 v3.1 remove deprecated tag field
+ *
+ *
+ * 1.Protocol Type 1 byte
+ * 2.Total Length 4 byte,exclude protocol type size
+ * 3.RequestID 4 byte,used for repeatable requests,connection reuse.an requestID string
+ * representing a client-generated, globally unique for some time unit, identifier for the request
+ * 4.Serializer Type 1 byte
+ * 5.Traffic Type 1 byte,0-sync;1-async;2-oneway;3-response
+ * 6.OpCode Length 2 byte
+ * 7.OpCode variant length,utf8 string
+ * 8.Remark Length 2 byte
+ * 9.Remark variant length,utf8 string
+ * 10.Properties Size 2 byte
+ * Property Length 2 byte
+ * Property Body variant length,utf8,Key\nValue
+ * 11.Inbound or OutBound payload length 4 byte
+ * 12.Inbound or OutBound payload variant length, max size limitation is 16M
+ * 13.Extra payload variant length
+ *
+ * </pre>
+ */
+package org.apache.rocketmq.remoting;
\ No newline at end of file
diff --git a/style/copyright/Apache.xml b/style/copyright/Apache.xml
new file mode 100644
index 0000000..7ca71b5
--- /dev/null
+++ b/style/copyright/Apache.xml
@@ -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.
+ -->
+
+<component name="CopyrightManager">
+ <copyright>
+ <option name="myName" value="Apache"/>
+ <option name="notice"
+ value="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/lice [...]
+ </copyright>
+</component>
\ No newline at end of file
diff --git a/style/copyright/profiles_settings.xml b/style/copyright/profiles_settings.xml
new file mode 100644
index 0000000..d326b8c
--- /dev/null
+++ b/style/copyright/profiles_settings.xml
@@ -0,0 +1,64 @@
+<!--
+ 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.
+ -->
+
+<component name="CopyrightManager">
+ <settings default="Apache">
+ <module2copyright>
+ <element module="All" copyright="Apache"/>
+ </module2copyright>
+ <LanguageOptions name="GSP">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="prefixLines" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="HTML">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="prefixLines" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="JAVA">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="addBlankAfter" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="JSP">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="prefixLines" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="JSPX">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="prefixLines" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="MXML">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="prefixLines" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="Properties">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="block" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="SPI">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="block" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="XML">
+ <option name="fileTypeOverride" value="3"/>
+ <option name="prefixLines" value="false"/>
+ </LanguageOptions>
+ <LanguageOptions name="__TEMPLATE__">
+ <option name="separateBefore" value="true"/>
+ <option name="lenBefore" value="1"/>
+ </LanguageOptions>
+ </settings>
+</component>
\ No newline at end of file
diff --git a/style/rmq_checkstyle.xml b/style/rmq_checkstyle.xml
new file mode 100644
index 0000000..e93b353
--- /dev/null
+++ b/style/rmq_checkstyle.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+<!--Refer http://checkstyle.sourceforge.net/reports/google-java-style.html#s2.2-file-encoding -->
+<module name="Checker">
+
+ <property name="localeLanguage" value="en"/>
+
+ <!--To configure the check to report on the first instance in each file-->
+ <module name="FileTabCharacter"/>
+
+ <!-- header -->
+ <module name="RegexpHeader">
+ <property name="header" value="/\*\nLicensed to the Apache Software Foundation*"/>
+ <property name="fileExtensions" value="java"/>
+ </module>
+
+ <module name="RegexpHeader">
+ <property name="header" value="#[\s]*Licensed to the Apache Software Foundation*"/>
+ <property name="fileExtensions" value="properties"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="System\.out\.println"/>
+ <property name="message" value="Prohibit invoking System.out.println in source code !"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="//FIXME"/>
+ <property name="message" value="Recommended fix FIXME task !"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="//TODO"/>
+ <property name="message" value="Recommended fix TODO task !"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="@alibaba"/>
+ <property name="message" value="Recommended remove @alibaba keyword!"/>
+ </module>
+ <module name="RegexpSingleline">
+ <property name="format" value="@taobao"/>
+ <property name="message" value="Recommended remove @taobao keyword!"/>
+ </module>
+ <module name="RegexpSingleline">
+ <property name="format" value="@author"/>
+ <property name="message" value="Recommended remove @author tag in javadoc!"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format"
+ value=".*[\u3400-\u4DB5\u4E00-\u9FA5\u9FA6-\u9FBB\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFF00-\uFFEF\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF]+.*"/>
+ <property name="message" value="Not allow chinese character !"/>
+ </module>
+
+ <module name="FileLength">
+ <property name="max" value="3000"/>
+ </module>
+
+ <module name="TreeWalker">
+
+ <module name="UnusedImports">
+ <property name="processJavadoc" value="true"/>
+ </module>
+ <module name="RedundantImport"/>
+
+ <!--<module name="IllegalImport" />-->
+
+ <!--Checks that classes that override equals() also override hashCode()-->
+ <module name="EqualsHashCode"/>
+ <!--Checks for over-complicated boolean expressions. Currently finds code like if (topic == true), topic || true, !false, etc.-->
+ <module name="SimplifyBooleanExpression"/>
+ <module name="OneStatementPerLine"/>
+ <module name="UnnecessaryParentheses"/>
+ <!--Checks for over-complicated boolean return statements. For example the following code-->
+ <module name="SimplifyBooleanReturn"/>
+
+ <!--Check that the default is after all the cases in producerGroup switch statement-->
+ <module name="DefaultComesLast"/>
+ <!--Detects empty statements (standalone ";" semicolon)-->
+ <module name="EmptyStatement"/>
+ <!--Checks that long constants are defined with an upper ell-->
+ <module name="UpperEll"/>
+ <module name="ConstantName">
+ <property name="format" value="(^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)|(^log$)"/>
+ </module>
+ <!--Checks that local, non-final variable names conform to producerGroup format specified by the format property-->
+ <module name="LocalVariableName"/>
+ <!--Validates identifiers for local, final variables, including catch parameters-->
+ <module name="LocalFinalVariableName"/>
+ <!--Validates identifiers for non-static fields-->
+ <module name="MemberName"/>
+ <!--Validates identifiers for class type parameters-->
+ <module name="ClassTypeParameterName">
+ <property name="format" value="^[A-Z0-9]*$"/>
+ </module>
+ <!--Validates identifiers for method type parameters-->
+ <module name="MethodTypeParameterName">
+ <property name="format" value="^[A-Z0-9]*$"/>
+ </module>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+ <!--Checks that there are no import statements that use the * notation-->
+ <module name="AvoidStarImport"/>
+
+ <!--whitespace-->
+ <module name="GenericWhitespace"/>
+ <!--<module name="NoWhitespaceBefore"/>-->
+ <!--<module name="NoWhitespaceAfter"/>-->
+ <module name="WhitespaceAround">
+ <property name="allowEmptyConstructors" value="true"/>
+ <property name="allowEmptyMethods" value="true"/>
+ </module>
+ <module name="Indentation"/>
+ <module name="MethodParamPad"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ </module>
+</module>
diff --git a/style/rmq_codeStyle.xml b/style/rmq_codeStyle.xml
new file mode 100644
index 0000000..7c7ce54
--- /dev/null
+++ b/style/rmq_codeStyle.xml
@@ -0,0 +1,143 @@
+<!--
+ 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.
+ -->
+
+<code_scheme name="rocketmq">
+ <option name="USE_SAME_INDENTS" value="true"/>
+ <option name="IGNORE_SAME_INDENTS_FOR_LANGUAGES" value="true"/>
+ <option name="OTHER_INDENT_OPTIONS">
+ <value>
+ <option name="INDENT_SIZE" value="4"/>
+ <option name="CONTINUATION_INDENT_SIZE" value="4"/>
+ <option name="TAB_SIZE" value="4"/>
+ <option name="USE_TAB_CHARACTER" value="false"/>
+ <option name="SMART_TABS" value="false"/>
+ <option name="LABEL_INDENT_SIZE" value="0"/>
+ <option name="LABEL_INDENT_ABSOLUTE" value="false"/>
+ <option name="USE_RELATIVE_INDENTS" value="false"/>
+ </value>
+ </option>
+ <option name="PREFER_LONGER_NAMES" value="false"/>
+ <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000"/>
+ <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000"/>
+ <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
+ <value/>
+ </option>
+ <option name="IMPORT_LAYOUT_TABLE">
+ <value>
+ <package name="" withSubpackages="true" static="false"/>
+ <emptyLine/>
+ <package name="" withSubpackages="true" static="true"/>
+ </value>
+ </option>
+ <option name="JD_ALIGN_PARAM_COMMENTS" value="false"/>
+ <option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false"/>
+ <option name="JD_P_AT_EMPTY_LINES" value="false"/>
+ <option name="JD_KEEP_INVALID_TAGS" value="false"/>
+ <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true"/>
+ <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/>
+ <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/>
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
+ <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
+ <option name="WHILE_ON_NEW_LINE" value="true"/>
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
+ <option name="ALIGN_MULTILINE_FOR" value="false"/>
+ <option name="SPACE_AFTER_TYPE_CAST" value="true"/>
+ <option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/>
+ <option name="METHOD_PARAMETERS_WRAP" value="1"/>
+ <option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true"/>
+ <option name="LABELED_STATEMENT_WRAP" value="1"/>
+ <option name="WRAP_COMMENTS" value="true"/>
+ <option name="METHOD_ANNOTATION_WRAP" value="1"/>
+ <option name="CLASS_ANNOTATION_WRAP" value="1"/>
+ <option name="FIELD_ANNOTATION_WRAP" value="1"/>
+ <JavaCodeStyleSettings>
+ <option name="CLASS_NAMES_IN_JAVADOC" value="3"/>
+ </JavaCodeStyleSettings>
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true"/>
+ </XML>
+ <ADDITIONAL_INDENT_OPTIONS fileType="haml">
+ <option name="INDENT_SIZE" value="2"/>
+ </ADDITIONAL_INDENT_OPTIONS>
+ <codeStyleSettings language="Groovy">
+ <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/>
+ <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/>
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
+ <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
+ <option name="ALIGN_MULTILINE_FOR" value="false"/>
+ <option name="METHOD_PARAMETERS_WRAP" value="1"/>
+ <option name="METHOD_ANNOTATION_WRAP" value="1"/>
+ <option name="CLASS_ANNOTATION_WRAP" value="1"/>
+ <option name="FIELD_ANNOTATION_WRAP" value="1"/>
+ <option name="PARENT_SETTINGS_INSTALLED" value="true"/>
+ <indentOptions>
+ <option name="CONTINUATION_INDENT_SIZE" value="4"/>
+ </indentOptions>
+ </codeStyleSettings>
+ <codeStyleSettings language="HOCON">
+ <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
+ <option name="PARENT_SETTINGS_INSTALLED" value="true"/>
+ </codeStyleSettings>
+ <codeStyleSettings language="JAVA">
+ <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/>
+ <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/>
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
+ <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
+ <option name="WHILE_ON_NEW_LINE" value="true"/>
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
+ <option name="ALIGN_MULTILINE_FOR" value="false"/>
+ <option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/>
+ <option name="METHOD_PARAMETERS_WRAP" value="1"/>
+ <option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true"/>
+ <option name="LABELED_STATEMENT_WRAP" value="1"/>
+ <option name="METHOD_ANNOTATION_WRAP" value="1"/>
+ <option name="CLASS_ANNOTATION_WRAP" value="1"/>
+ <option name="FIELD_ANNOTATION_WRAP" value="1"/>
+ <option name="PARENT_SETTINGS_INSTALLED" value="true"/>
+ <indentOptions>
+ <option name="CONTINUATION_INDENT_SIZE" value="4"/>
+ </indentOptions>
+ </codeStyleSettings>
+ <codeStyleSettings language="JSON">
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
+ <option name="PARENT_SETTINGS_INSTALLED" value="true"/>
+ </codeStyleSettings>
+ <codeStyleSettings language="Scala">
+ <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/>
+ <option name="KEEP_BLANK_LINES_IN_CODE" value="1"/>
+ <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/>
+ <option name="WHILE_ON_NEW_LINE" value="true"/>
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false"/>
+ <option name="ALIGN_MULTILINE_FOR" value="false"/>
+ <option name="METHOD_PARAMETERS_WRAP" value="1"/>
+ <option name="METHOD_ANNOTATION_WRAP" value="1"/>
+ <option name="CLASS_ANNOTATION_WRAP" value="1"/>
+ <option name="FIELD_ANNOTATION_WRAP" value="1"/>
+ <option name="PARENT_SETTINGS_INSTALLED" value="true"/>
+ <indentOptions>
+ <option name="INDENT_SIZE" value="4"/>
+ <option name="CONTINUATION_INDENT_SIZE" value="4"/>
+ <option name="TAB_SIZE" value="4"/>
+ </indentOptions>
+ </codeStyleSettings>
+ <codeStyleSettings language="XML">
+ <indentOptions>
+ <option name="CONTINUATION_INDENT_SIZE" value="4"/>
+ </indentOptions>
+ </codeStyleSettings>
+</code_scheme>
\ No newline at end of file