This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-java-contrib.git


The following commit(s) were added to refs/heads/main by this push:
     new c1bd256  Initial Setup: Apache Pulsar Contributor Repository Structure 
(#2)
c1bd256 is described below

commit c1bd256639d2dafff9c86bd23033b6c67270c32f
Author: Xiangying Meng <[email protected]>
AuthorDate: Fri Aug 23 16:09:29 2024 +0800

    Initial Setup: Apache Pulsar Contributor Repository Structure (#2)
---
 .asf.yaml                                          |  23 +-
 .gitattributes                                     |  34 ++
 .github/dependabot.yml                             |  28 ++
 .github/pull_request_template.md                   |  51 +++
 .github/workflows/contrib-ci.yml                   |  48 +++
 .gitignore                                         |  18 +
 .mvn/wrapper/maven-wrapper.properties              |  19 +
 LICENSE                                            | 201 ++++++++++
 README.md                                          |  27 +-
 SECURITY.md                                        |   2 +
 best-practice-blogs/consume-best-practice.md       | 229 +++++++++++
 .../img/blog-consume-best-practice/Ack-hole.png    | Bin 0 -> 189219 bytes
 .../img/blog-consume-best-practice/AckTimeout.png  | Bin 0 -> 127045 bytes
 .../static/img/blog-consume-best-practice/DLQ.png  | Bin 0 -> 144988 bytes
 .../acknowledgement-types.png                      | Bin 0 -> 111861 bytes
 .../cumulative-ack-problem.png                     | Bin 0 -> 82471 bytes
 .../subscription-types.png                         | Bin 0 -> 158919 bytes
 contributedFeatures.md                             |  23 ++
 contributionGuides.md                              | 100 +++++
 customizationFeatures.md                           |  11 +
 etc/APACHE-2.txt                                   |  11 +
 etc/checkstyle.xml                                 | 217 +++++++++++
 mvnw                                               | 259 +++++++++++++
 mvnw.cmd                                           | 149 +++++++
 pom.xml                                            | 219 +++++++++++
 pulsar-auth-contrib/pom.xml                        |  30 ++
 pulsar-bookkeeper-contrib/pom.xml                  |  31 ++
 pulsar-client-common-contrib/pom.xml               |  39 ++
 .../pulsar/client/api/MessageListenerExecutor.java |  38 ++
 .../api/impl/CommonMessageListenerExecutor.java    |  51 +++
 .../api/impl/KeySharedMessageListenerExecutor.java |  45 +++
 .../PartitionOrderMessageListenerExecutor.java     |  39 ++
 .../pulsar/client/api/impl/package-info.java       |  14 +
 .../org/apache/pulsar/client/api/package-info.java |  14 +
 .../src/test/java/DemoTest.java                    |  24 ++
 pulsar-connector-contrib/pom.xml                   |  29 ++
 pulsar-function-contrib/pom.xml                    |  30 ++
 pulsar-interceptor-contrib/pom.xml                 |  32 ++
 pulsar-loadbalance-contrib/pom.xml                 |  30 ++
 pulsar-metrics-contrib/pom.xml                     |  29 ++
 pulsar-transaction-contrib/pom.xml                 |  30 ++
 src/main/resources/checkstyle.xml                  | 429 +++++++++++++++++++++
 src/main/resources/suppressions.xml                |  35 ++
 43 files changed, 2622 insertions(+), 16 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
index 6287bde..4948e4d 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -1,20 +1,15 @@
 #
-# 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
+# Licensed 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
+# http://www.apache.org/licenses/LICENSE-2.0
 #
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 #
 
 github:
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..5dd03cf
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,34 @@
+# Default to linux endings
+* text eol=lf
+
+# OS specific files
+###################
+
+*.sh eol=lf
+*.bat eol=crlf
+
+# Binary files
+##############
+
+# Image files
+*.png binary
+*.jpg binary
+*.gif binary
+*.bmp binary
+*.ico binary
+
+# Audio files
+*.wav binary
+*.mp3 binary
+*.ogg binary
+
+# Other binary files
+*.pdf binary
+*.xls binary
+*.xlsx binary
+
+# GitHub syntax highlighting
+############################
+
+# To prevent GitHub to presume those optaplanner-examples/data files are 
Objective-C++ code.
+*.mm linguist-language=Text
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..3107375
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,28 @@
+version: 2
+updates:
+- package-ecosystem: maven
+  directory: "/"
+  schedule:
+    interval: daily
+    time: '03:00'
+  open-pull-requests-limit: 0
+  target-branch: "development"
+  commit-message:
+    prefix: "[bot][dev]"
+- package-ecosystem: maven
+  directory: "/"
+  schedule:
+    interval: daily
+    time: '04:00'
+  open-pull-requests-limit: 0
+  target-branch: "8.13.x"
+  commit-message:
+    prefix: "[bot][8.13.x]"
+- package-ecosystem: "github-actions"
+  directory: "/"
+  schedule:
+    interval: daily
+    time: '05:00'
+  target-branch: "development"
+  commit-message:
+    prefix: "[bot][dev]"
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..beba7ba
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,51 @@
+<!--
+### Contribution Checklist
+  
+  - PR title format should be *[type][component] summary*. For details, see 
*[Guideline - Pulsar PR Naming 
Convention](https://pulsar.apache.org/contribute/develop-semantic-title/)*. 
+
+  - Fill out the template below to describe the changes contributed by the 
pull request. That will give reviewers the context they need to do the review.
+  
+  - Each pull request should address only one issue, not mix up code from 
multiple issues.
+  
+  - Each commit in the pull request has a meaningful commit message
+
+  - Once all items of the checklist are addressed, remove the above text and 
this checklist, leaving only the filled out template below.
+-->
+
+<!-- Either this PR fixes an issue, -->
+
+Fixes #xyz
+
+<!-- or this PR is one task of an issue -->
+
+Main Issue: #xyz
+
+<!-- If the PR belongs to a PIP, please add the PIP link here -->
+
+### Motivation
+
+<!-- Explain here the context, and why you're making that change. What is the 
problem you're trying to solve. -->
+
+### Modifications
+
+<!-- Describe the modifications you've done. -->
+
+### Verifying this change
+
+- [ ] Make sure that the change passes the CI checks.
+
+*(Please pick either of the following options)*
+
+This change is a trivial rework / code cleanup without any test coverage.
+
+*(or)*
+
+This change is already covered by existing tests, such as *(please describe 
tests)*.
+
+*(or)*
+
+This change added tests and can be verified as follows:
+
+*(example:)*
+- *Added integration tests for end-to-end deployment with large payloads 
(10MB)*
+- *Extended integration test for recovery after broker failure*
\ No newline at end of file
diff --git a/.github/workflows/contrib-ci.yml b/.github/workflows/contrib-ci.yml
new file mode 100644
index 0000000..b37bd52
--- /dev/null
+++ b/.github/workflows/contrib-ci.yml
@@ -0,0 +1,48 @@
+# This workflow will build a Java project with Maven, and cache/restore any 
dependencies to improve the workflow execution time
+# For more information see: 
https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+name: Java CI with Maven
+
+on:
+  push:
+    branches: [ "main" ]
+  pull_request:
+    branches: [ "main" ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: true
+
+    steps:
+    - uses: actions/checkout@v4
+    - name: Set up JDK 17
+      uses: actions/setup-java@v4
+      with:
+        java-version: '17'
+        distribution: 'temurin'
+        cache: maven
+    - name: Build with Maven
+      run: mvn -B package --file pom.xml
+    - name: Unit test
+      run: mvn clean verify
+  unit-test:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4
+        with:
+          java-version: '17'
+          distribution: 'temurin'
+          cache: maven
+      - name: Unit test
+        run: mvn clean verify
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..59edf66
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+/target
+/local
+
+# Eclipse, Netbeans and IntelliJ files
+/.*
+!/.github
+!/.ci
+!.gitignore
+!.gitattributes
+!/.mvn
+/nbproject
+/*.ipr
+/*.iws
+/*.iml
+.project
+
+# Repository wide ignore mac DS_Store files
+.DS_Store
diff --git a/.mvn/wrapper/maven-wrapper.properties 
b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..7479338
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,19 @@
+# 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.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=http://mvn.devops.xiaohongshu.com/repository/maven-public//org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/README.md b/README.md
index abbf60a..d114e13 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,25 @@
-# pulsar-java-contrib
-Apache pulsar
+# Apache Pulsar Java Contrib
+
+Pulsar java contrib is to provide a non-core code maintenance repository to 
collect plugin implementations, personalized features, experimental features, 
and best practices from users.
+
+- [Plugin Contribution Guide](contributionGuides) lists the core interfaces in 
Pulsar that can be implemented by contributors, and provides implementation 
guidelines for each type of interface.
+
+- [Plugin Implementation List](contributedFeatures.md) lists the implemented 
plugins. Users can select the ones they need for reuse.
+
+- [Personalization Features](customizationFeatures.md) lists the customized 
features and experimental features that require modification to the Pulsar 
source code.
+
+- [Best Practices]([best-practice-blogs](best-practice-blogs)) lists the best 
practices for each function summarized by community contributions.
+  - [consume-best-practice.md](best-practice-blogs%2Fconsume-best-practice.md)
+
+This project follows the terms of **Apache License 2.0**.
+You can format the code by `mvn spotless:apply` and generate license headers 
by `mvn license:format`.
+Please note that the code formatted by Spotless may still not meet the 
formatting requirements. Please run `mvn checkstyle:check` for inspection.
+
+## Contributing
+
+pulsar-java-contrib is actively in development.  If you have some common use 
cases for plugins, please contact us and we'll be happy to support.
+Please [open an 
issue](https://github.com/apache/pulsar-java-contrib/issues/new) to share your 
idea or
+suggestion.  PRs are always welcome and greatly appreciated, but for larger 
functional changes a pre-coding introduction
+can be helpful to ensure this is the correct place and that active or 
conflicting efforts don't exist.
+
+Learn more about roles in the [community 
repository](https://github.com/pulsar/pulsar-java-contrib).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..50e550e
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,2 @@
+# Security Policy
+See https://pulsar.apache.org/security/.
\ No newline at end of file
diff --git a/best-practice-blogs/consume-best-practice.md 
b/best-practice-blogs/consume-best-practice.md
new file mode 100644
index 0000000..0c6553d
--- /dev/null
+++ b/best-practice-blogs/consume-best-practice.md
@@ -0,0 +1,229 @@
+# Consume Best Practice
+
+## Background Knowledge
+
+### Subscription Types
+
+Pulsar is a distributed message system where messages can be sent to topics by 
producers and consumed by consumers.
+Consumers can subscribe to the topics in four ways (subscription types):
+
+* **Exclusive**
+* **Failover**
+* **Shared**
+* **Key-shared**
+
+The messages are consumed in order for a single partition in Exclusive and 
Failover modes and out of order for Shared and
+Key-shared mode. The main difference between Exclusive and Failover is that in 
Exclusive mode, the consumer is exclusive
+to the entire topic, while in Failover mode, the consumer is exclusive to only 
one partition. Failover mode allows backup
+consumer connections that are not consumed. The main difference between Shared 
and Key-shared is whether their dispatch
+strategy is implemented via a key. For more information about subscription 
type, refer to the [Pulsar 
website](https://pulsar.apache.org/docs/next/concepts-messaging/).
+![img.png](static/img/blog-consume-best-practice/subscription-types.png)
+
+### Acknowledgment
+
+The messages should be acknowledged after they are fully consumed and 
processed, and then the messages would not be received
+for the same subscription again. Pulsar provides two ways to acknowledge 
messages:
+
+* **Cumulative acknowledgment**
+* **Individual acknowledgment**
+
+Cumulative acknowledgment receives a message or a message id as a parameter 
and marks the messages before the message as
+consumed for this subscription. For multiple-partition topics, the cumulative 
acknowledgment will work for the single
+partition without impacting other partitions. Individual acknowledgment 
receives a message or a message id as a parameter
+and only marks this message as consumed for this subscription.
+![img_1.png](static/img/blog-consume-best-practice/acknowledgement-types.png)
+
+### Messages Redeliver Mechanism
+
+There might be instances where the received messages cannot be processed at 
this time or some errors happened during processing.
+The client needs to redeliver the unacknowledged messages or a particular 
message immediately or after a delay.
+Pulsar provides at-least-once semantics when the client does not enable 
transaction because the client may cache some
+messages out of Pulsar when redelivering messages.
+
+Pulsar Consumer API provides four ways to reconsume the unacknowledged 
messages later:
+
+* **ackTimeout**
+* **deadLetterPolicy**
+    * **reconsumeLaterCumulative**
+    * **reconsumeLater**
+* **negativeAcknowledge**
+* **redeliverUnacknowledgedMessages**
+
+The **ackTimeout** is an automatic protection mechanism. If a consumer 
configured ackTimeout, the messages will be
+auto-redelivered when the received messages are not acknowledged after a long 
time. It works at the client side, ensuring
+the unacknowledged messages will be redelivered to another consumer when the 
connection of a consumer remains active
+but the business system gets stuck or misses to ack some messages. If the 
consumer gets disconnected when the client crashes,
+the messages will be auto-redelivered by the broker too. However, this 
mechanism does not wait for the response of acknowledgment,
+so if an acknowledgment fails on the broker side or proxy side, an ack hole 
may occur.
+
+The **deadLetterPolicy** is a policy in the message queue used to handle 
messages that cannot be processed properly.
+In many message queue systems (such as RabbitMQ, Google Pub/Sub, Apache 
Pulsar, etc.), this strategy is implemented.
+In Pulsar, the deadLetterPolicy is implemented at the client side, it creates 
a new retry letter topic and dead letter
+topic when building a consumer with `deadLetterPolicy` configuration. When a 
consumer calls `reconsumeLaterCumulative`
+or `reconsumeLater`, the message (method parameter) will be produced to the 
retry letter topic until the retry time reaches
+the `maxRedeliverCount`. The message will be produced to the dead letter topic 
when the retry time reaches the `
+maxRedeliverCount`. The main difference between them is that 
`reconsumeLaterCumulative` will cumulative ack the
+message (method parameter) after it is produced and `reconsumeLater` will 
individual ack the message (method parameter)
+after it is produced.
+
+The `negativeAcknowledge` is used to redeliver certain unacknowledged messages 
while `redeliverUnacknowledgedMessages`
+is used to redeliver all the unacknowledged messages received by this 
consumer. The main difference between them and
+deadLetterPolicy is that there is no new topic created, and there is an 
unlimited number of redeliveries.
+
+## Best Practice Suggestion
+
+Different scenarios require different best practices. Users who value the 
order of partition messages and wish to batch
+process data should choose or implement an appropriate routerPolicy to send a 
batch of ordered messages to the same
+partition. They should also select either Exclusive or Failover subscription 
modes. For users who do not care about
+message order and those in stream processing scenarios, they can opt to use 
Shared and Key-shared subscription modes.
+
+### Shared && Key-shared
+#### At-least-once
+The term `At least once` ensures that the message is processed at least once, 
and there is a risk of duplicate messages,
+but the performance is better than the `Exactly once` semantics. For the `At 
least once` semantics in shared or
+key-shared mode, the most important matter is avoiding ack-hole as much as 
possible. The ack-hole refers to instances
+where some single messages are missed for acknowledgment. This can occur in 
the following cases (All of these are the cases
+where the connection is not interrupted, and reconnection will automatically 
resend the message.):
+
+1. Consumer receives messages but fails to process due to business system 
error.
+2. Consumer receives messages but misses to process or the business system 
gets stuck.
+3. Consumer acknowledges the message, but the acknowledge request is lost when 
sending to the broker. The possibility of
+4. data loss exists in the case of TCP long connections, although the 
probability is extremely low.
+   ![img_8.png](static/img/blog-consume-best-practice/Ack-hole.png)
+
+For case 1, configuring `deadLetterPolicy` is a good solution. When the 
business system receives a signal of message
+processing failure, it can immediately check and decide whether to retry after 
a period of time. After they call
+`reconsumeLater` API, the message will be acknowledged and resent to the retry 
letter topic that is automatically
+subscribed by the consumer. When it reaches `maxRedeliverCount`, the message 
will be sent to the dead letter topic.
+Messages sent to the dead letter topic should be considered as non-retryable 
messages, and we recommend setting an
+initialSubscriptionName to avoid being deleted by the retention policy and 
then, let maintenance personnel regularly
+handle non-retryable messages in the dead letter topic.
+![img_2.png](static/img/blog-consume-best-practice/DLQ.png)
+```java
+        Consumer<Integer> consumer = pulsarClient.newConsumer(Schema.INT32)
+               .deadLetterPolicy(DeadLetterPolicy.builder()
+                 .maxRedeliverCount(maxRedeliverCount)
+                 .deadLetterTopic(deadLetterTopic)
+                 .retryLetterTopic(retryLetterTopic)
+                 .initialSubscriptionName(initialSubscriptionName)
+                 .build())
+              .subscriptionType(SubscriptionType.Shared)
+              .enableRetry(true)
+              
.subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
+              .topic(topic)
+              .subscriptionName(sub)
+              .subscribe();
+        Message<Integer> message = consumer.receive();
+        try{
+            // Process message
+            consumer.acknowledge(message);
+        }catch(Exception e){
+            // Check whether to redeliver the message again.
+            if(e instanceof RetryException){
+                consumer.reconsumeLater(message, delay, timeunit);
+            }
+            consumer.reconsumeLater(message, delay, timeunit);
+        }
+```
+
+For case 2, where the business system gets stuck or an error occurs which 
causes the received message to be missed for
+processing, configuring ack timeout is a good solution. The consumer will 
record every message received on the client side.
+If these messages have not been acknowledged after the specified time, the 
consumer will request the broker to resend these
+messages to other brokers.
+**Suggestion:** The ack timeout should be set slightly longer based on the 
message processing speed of the business system.
+If the business system is still processing messages, but the processing time 
is too long or the timeout is set too small,
+it may result in duplicate message consumption.
+![img_3.png](static/img/blog-consume-best-practice/AckTimeout.png)
+```java
+        Consumer<Integer> consumer = pulsarClient.newConsumer(Schema.INT32)
+                .ackTimeout(tiemout, timeunit)
+                .subscriptionType(SubscriptionType.Shared)
+                
.subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
+                .topic(topic)
+                .subscriptionName(sub)
+                .subscribe();
+```
+
+For case 3, there are no effective preventive measures. This is because all 
methods of redelivery are triggered by the
+client when the connection is not disconnected, and the client does not wait 
for an ack response by default.
+
+`Short-term solution`: This should be an extreme case where users can unload 
the topic to resolve after observing
+an abnormal ack hole (existing for more than 3 * ack time out).
+`Long-term solution`: Modify the ack-timeout mechanism to wait for the 
acknowledgment response.
+
+#### Exactly-once
+If the users cannot tolerate message repetition, they can acknowledge messages 
with a transaction. Transaction can
+prevent repeated consumption. If a message has been acknowledged, it will wait 
for a response and throw
+`TransactionConflictException` when the client acknowledges the message with a 
transaction.
+
+**Notices:** When using transactions, do not configure DeadLetterPolicy, but 
instead use negativeAcknowledge to resend messages.
+
+```java
+        Consumer<Integer> consumer = pulsarClient.newConsumer(Schema.INT32)
+                .ackTimeout(tiemout, timeunit)
+                .subscriptionType(SubscriptionType.Shared)
+                
.subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
+                .topic(topic)
+                .subscriptionName(sub)
+                .subscribe();
+        Transaction transaction = 
pulsarClient.newTransaction().withTransactionTimeout(timeout, 
timeunit).build().get();
+
+        Message<Integer> message = consumer.receive();
+        try {
+            // process message
+            consumer.acknowledgeAsync(message.getMessageId(), transaction);
+            transaction.commit().get();
+        } catch (Exception e) {
+            if (!(e.getCause() instanceof 
PulsarClientException.TransactionConflictException)) {
+                consumer.negativeAcknowledge(message);
+            }
+            transaction.abort().get();
+        } finally {
+            message.release();
+        }
+```
+### Exclusive && Failover
+#### At-least-once
+For `Exclusive` and `Failover` modes, which follow `At least once` semantics, 
it's crucial to focus on maintaining
+the order of messages while ensuring none are lost. Users are recommended to 
use cumulative acknowledgment in the
+`Exclusive` or `Failover` mode. Pulsar guarantees that the user has received 
all messages prior to a message that will
+be cumulative acknowledged. In this mode, there will be no ack-hole and there 
is no need to redeliver a specific message.
+It is also not recommended to redeliver a specific message as it can cause 
messages to be out of order. When there is a
+problem with the processing of a batch of messages, it is recommended to use 
`redeliverUnacknowledgedMessages` to
+redeliver all unprocessed messages to ensure the orderliness of the messages.
+````java
+        Consumer<Integer> consumer = pulsarClient.newConsumer(Schema.INT32)
+                .subscriptionType(SubscriptionType.Exclusive)
+                
.subscriptionInitialPosition(SubscriptionInitialPosition.Earliest)
+                .subscriptionName(sub)
+                .subscribe();
+        Message<Integer> message = consumer.receive();
+        try {
+            // process message
+            message = consumer.receive();
+            // process message
+            // ......
+            consumer.acknowledgeCumulative(message);
+        } catch (Exception e) {
+            consumer.redeliverUnacknowledgedMessages();
+        } finally {
+            message.release();
+        }
+````
+
+#### Exactly-once (Beta)
+In the `Exclusive` and `Failover` mode, the most troublesome issue for users 
is the problem of duplicate messages caused
+by disconnection. When a connection is reset, the broker will resend all the 
unacknowledged messages to the consumer.
+The consumer may have processed many messages without acknowledging them, 
causing the consumer to unconsciously resume
+these messages repeatedly. Unfortunately, there is currently no effective way 
to prevent this situation from happening.
+Pulsar transaction is not yet sufficient to solve this problem.
+![img_5.png](static/img/blog-consume-best-practice/cumulative-ack-problem.png)
+
+`Long-term solution`: Apply epoch to avoid receiving repeated messages and 
abort transaction in the extreme case
+(consumer change frequently in the failover subscription type).
+
+In conclusion, ensuring the reliable transmission of messages in Pulsar 
involves understanding the different subscription
+types, acknowledgment methods, and message redelivery mechanisms. Different 
scenarios require different best practices,
+and users should consider factors such as the order of partition messages, the 
possibility of duplicate messages,
+and the speed of the business system. The solutions provided here, including 
configuring `deadLetterPolicy` and `ackTimeout`
+, using transactions, and modifying the ack-timeout mechanism, can help users 
to address common issues and optimize their use of Pulsar.
\ No newline at end of file
diff --git 
a/best-practice-blogs/static/img/blog-consume-best-practice/Ack-hole.png 
b/best-practice-blogs/static/img/blog-consume-best-practice/Ack-hole.png
new file mode 100644
index 0000000..eda0b9e
Binary files /dev/null and 
b/best-practice-blogs/static/img/blog-consume-best-practice/Ack-hole.png differ
diff --git 
a/best-practice-blogs/static/img/blog-consume-best-practice/AckTimeout.png 
b/best-practice-blogs/static/img/blog-consume-best-practice/AckTimeout.png
new file mode 100644
index 0000000..1a04ee9
Binary files /dev/null and 
b/best-practice-blogs/static/img/blog-consume-best-practice/AckTimeout.png 
differ
diff --git a/best-practice-blogs/static/img/blog-consume-best-practice/DLQ.png 
b/best-practice-blogs/static/img/blog-consume-best-practice/DLQ.png
new file mode 100644
index 0000000..373621f
Binary files /dev/null and 
b/best-practice-blogs/static/img/blog-consume-best-practice/DLQ.png differ
diff --git 
a/best-practice-blogs/static/img/blog-consume-best-practice/acknowledgement-types.png
 
b/best-practice-blogs/static/img/blog-consume-best-practice/acknowledgement-types.png
new file mode 100644
index 0000000..ebf3d33
Binary files /dev/null and 
b/best-practice-blogs/static/img/blog-consume-best-practice/acknowledgement-types.png
 differ
diff --git 
a/best-practice-blogs/static/img/blog-consume-best-practice/cumulative-ack-problem.png
 
b/best-practice-blogs/static/img/blog-consume-best-practice/cumulative-ack-problem.png
new file mode 100644
index 0000000..7bb3033
Binary files /dev/null and 
b/best-practice-blogs/static/img/blog-consume-best-practice/cumulative-ack-problem.png
 differ
diff --git 
a/best-practice-blogs/static/img/blog-consume-best-practice/subscription-types.png
 
b/best-practice-blogs/static/img/blog-consume-best-practice/subscription-types.png
new file mode 100644
index 0000000..52aa79c
Binary files /dev/null and 
b/best-practice-blogs/static/img/blog-consume-best-practice/subscription-types.png
 differ
diff --git a/contributedFeatures.md b/contributedFeatures.md
new file mode 100644
index 0000000..63e44ba
--- /dev/null
+++ b/contributedFeatures.md
@@ -0,0 +1,23 @@
+# List of implemented plugins
+
+## Client extension interface implementation
+- `org.apache.pulsar.client.api.MessageListenerExecutor.java`
+  - org.apache.pulsar.client.api.impl.KeySharedMessageListenerExecutor
+  - org.apache.pulsar.client.api.impl.CommonMessageListenerExecutor
+  - org.apache.pulsar.client.api.impl.PartitionOrderMessageListenerExecutor
+
+## Load balancer extension interface implementation
+
+## Interceptor extension interface implementation
+
+## Connector interface implementation
+
+## Pulsar function interface implementation
+
+## Bookkeeper related interface implementation
+
+## Transaction related interface implementation
+
+## Metrics related interface
+
+## Authentication and authorization related interface
\ No newline at end of file
diff --git a/contributionGuides.md b/contributionGuides.md
new file mode 100644
index 0000000..74c65c7
--- /dev/null
+++ b/contributionGuides.md
@@ -0,0 +1,100 @@
+# Apache Pulsar Plugin Contribution Guide
+
+## Welcome
+Thank you for your contribution to the Apache Pulsar project. This document 
will guide you to understand and implement Pulsar's extensible interface.
+
+## 1. Introduction
+Apache Pulsar is a distributed messaging and streaming platform. You can view 
detailed information [here](pulsar.apache.org/).
+Pulsar allows users to customize plugins and integrate them into Pulsar to 
meet customized needs by exposing interfaces and configurations.
+Many of these plugins are similar and can be reused. This project aims to 
collect and organize the implementation of various plugins, reduce the 
development cost caused by repeated implementations in the community, and lower 
the threshold for using Pulsar.
+
+## 2. Core interface list
+List the core interfaces in Pulsar that can be implemented by contributors.
+
+### 2.1 Pulsar client common extension interface
+- `org.apache.pulsar.client.api.MessageListenerExecutor.java`
+
+### 2.2 Authentication and authorization related interfaces
+- `org.apache.pulsar.broker.authorization.AuthorizationProvider`
+- `org.apache.pulsar.client.api.AuthenticationDataProvider`
+- `org.apache.pulsar.functions.auth.FunctionAuthProvider`
+- `org.apache.pulsar.functions.auth.KubernetesFunctionAuthProvider`
+- `org.apache.pulsar.functions.secretsprovider.SecretsProvider`
+
+### 2.3 Transaction-related interfaces
+- `org.apache.pulsar.transaction.coordinator.TransactionMetadataStoreProvider`
+- `org.apache.pulsar.broker.transaction.buffer.TransactionBufferProvider`
+- 
`org.apache.pulsar.broker.transaction.pendingack.TransactionPendingAckStoreProvider`
+
+### 2.4 Load Balancer Extension Interface
+- `org.apache.pulsar.broker.loadbalance.ModularLoadManager`
+- `org.apache.pulsar.common.naming.TopicBundleAssignmentStrategy`
+- `org.apache.pulsar.broker.loadbalance.LoadSheddingStrategy`
+- `org.apache.pulsar.broker.loadbalance.ModularLoadManagerStrategy`
+- `org.apache.pulsar.broker.loadbalance.extensions.filter.BrokerFilter`
+
+### 2.5 Interceptor Interface
+- `org.apache.pulsar.client.api.ProducerInterceptor`
+- `org.apache.pulsar.client.api.ConsumerInterceptor`
+- `org.apache.pulsar.client.api.ReaderInterceptor`
+- `org.apache.pulsar.broker.intercept.BrokerInterceptor`
+- `org.apache.pulsar.broker.service.TopicEventsListener`
+- `org.apache.pulsar.client.api.ConsumerEventListener`
+- `org.apache.pulsar.client.impl.transaction.TransactionBufferHandler`
+
+### 2.6 Pulsar Connector Interface
+- `org.apache.pulsar.io.core.Sink`
+- `org.apache.pulsar.io.core.Source`
+
+### 2.7 Pulsar Function Interface
+- `org.apache.pulsar.functions.api.Function`
+
+### 2.8 Bookkeeper related interfaces
+- `org.apache.pulsar.broker.service.schema.SchemaStorageFactory`
+- `org.apache.pulsar.packages.management.core.PackagesStorageProvider`
+- `org.apache.bookkeeper.mledger.ManagedLedger`
+
+### 2.9 Metrics related interfaces
+- `org.apache.pulsar.broker.stats.prometheus.PrometheusRawMetricsProvider`
+
+## 3. Interface Implementation Guide
+Provide implementation guide for each type of interfaces.
+
+### 3.1 Client common extension interface implementation
+- `org.apache.pulsar.client.api.MessageListenerExecutor.java`
+- **Purpose**: Select different message processing threads according to 
business scenarios.
+- **Sample code**: 
org.apache.pulsar.client.api.impl.KeySharedMessageListenerExecutor, 
org.apache.pulsar.client.api.impl.CommonMessageListenerExecutor, 
org.apache.pulsar.client.api.impl.PartitionOrderMessageListenerExecutor
+
+### 3.2 Authentication and authorization related interfaces
+- **Purpose**: Currently Pulsar has only a few default implementations for 
authentication and authorization interfaces. Users can customize the required 
authentication and authorization implementation through this interface.
+- **Sample code**: 
https://github.com/apache/pulsar/tree/master/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization,
 
https://github.com/apache/pulsar/tree/master/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication
+
+### 3.3 Implementation of transaction-related interfaces
+- **Purpose**: Customize transaction components according to different 
business requirements. For example, the Transaction Buffer implemented based on 
the Exactly-once requirement may have different considerations and different 
implementations.
+- **Sample code**: 
https://github.com/apache/pulsar/tree/master/pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/buffer/impl,
 
https://github.com/apache/pulsar/tree/master/pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/pendingack,
 https://github.com/apache/pulsar/tree/master/pul 
sar-transaction/coordinator/src/main/java/org/apache/pulsar/transaction/coordinator
+### 3.4 Load balancer extension interface implementation
+- **Purpose**: According to the business scenario of the user, when the 
existing load balance strategy cannot meet the business needs or is not the 
best strategy, you can inherit the existing strategy and modify it or 
completely customize the load balancer strategy suitable for your business.
+- **Sample code**: See the official existing 
implementation.https://github.com/apache/pulsar/tree/master/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance
+
+### 3.5 Interceptor extension interface implementation
+- **Purpose**: Users can implement Pulsar's interceptor interface to perform 
logging and auditing, message conversion and filtering. These interceptors 
often have similar implementations and can be abstracted and reused. For 
example, a series of interceptors for logging can be fully reused.
+- **Sample code**: None
+
+### 3.6 Connector interface implementation
+- **Purpose**: Connect Pulsar with external systems to import and export data.
+- **Sample code**: https://github.com/apache/pulsar/tree/master/pulsar-io
+
+### 3.7 Pulsar function interface implementation
+- **Purpose**: Implement serverless computing logic to respond to data flow 
changes in Pulsar.
+- **Sample code**: 
https://github.com/apache/pulsar/tree/master/pulsar-functions/java-examples/src/main/java/org/apache/pulsar/functions/api/examples
+
+### 3.8 Bookkeeper related interface implementation
+- **Purpose**: Processing logic related to persistent data
+- **Sample code**: 
https://github.com/apache/pulsar/tree/master/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/impl,
 
https://github.com/apache/pulsar/blob/master/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/BookkeeperSchemaStorageFactory.java,
+
+### 3.9 Metrics related interface
+- **Purpose**: Used to customize the generation of metric data in the 
Prometheus monitoring system
+- **Sample code**:
+```java
+PrometheusRawMetricsProvider rawMetricsProvider = stream -> 
stream.write("test_metrics{label1=\"xyz\"} 10 \n"); 
+```
\ No newline at end of file
diff --git a/customizationFeatures.md b/customizationFeatures.md
new file mode 100644
index 0000000..ded6f0a
--- /dev/null
+++ b/customizationFeatures.md
@@ -0,0 +1,11 @@
+# Customized features in personal projects
+
+**Statement**: The following projects are all from the contributors' personal 
code repositories and are for reference only.
+
+**Example**:
+
+## Unitization solution
+### Introduction
+This project customizes Pulsar's dispatcher so that it can distribute specific 
messages to clients from specific regions based on topic policies.
+### Project address
+https://github.com/xxx/pulsar-xxx/pull/xxx/commits/xxx
\ No newline at end of file
diff --git a/etc/APACHE-2.txt b/etc/APACHE-2.txt
new file mode 100644
index 0000000..86a8bee
--- /dev/null
+++ b/etc/APACHE-2.txt
@@ -0,0 +1,11 @@
+Licensed 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.
\ No newline at end of file
diff --git a/etc/checkstyle.xml b/etc/checkstyle.xml
new file mode 100644
index 0000000..2d95c2b
--- /dev/null
+++ b/etc/checkstyle.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<!--
+
+    Licensed 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
+        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+        "https://checkstyle.org/dtds/configuration_1_3.dtd";>
+<!--
+
+  Checkstyle configuration that firstly conforms to the Google Java Code style
+  and then further incorporates recommendations and conventions from:
+
+    - the Java Language Specification at
+      https://docs.oracle.com/javase/specs/jls/se11/html/index.html
+
+    - the Javadoc guidelines at
+      
https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
+
+    - the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
+
+    - some best practices
+
+  Checkstyle is very configurable. Be sure to read the documentation at
+  https://checkstyle.org (or in your downloaded distribution).
+
+  Most Checks are configurable, be sure to consult the documentation.
+
+  To completely disable a check, just comment it out or delete it from the 
file.
+  To suppress certain violations please review suppression filters.
+
+  Finally, it is worth reading the documentation.
+
+-->
+
+<module name="Checker">
+  <!--
+      If you set the basedir property below, then all reported file
+      names will be relative to the specified directory. See
+      https://checkstyle.org/config.html#Checker
+
+      <property name="basedir" value="${basedir}"/>
+  -->
+  <property name="severity" value="error"/>
+
+  <property name="fileExtensions" value="java, properties, xml"/>
+
+  <!-- Excludes all 'module-info.java' files              -->
+  <!-- See https://checkstyle.org/config_filefilters.html -->
+  <module name="BeforeExecutionExclusionFileFilter">
+    <property name="fileNamePattern" value="module\-info\.java$"/>
+  </module>
+
+  <!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
+  <module name="SuppressionFilter">
+    <property name="file" 
value="${org.checkstyle.sun.suppressionfilter.config}"
+              default="checkstyle-suppressions.xml" />
+    <property name="optional" value="true"/>
+  </module>
+
+  <!-- Checks that a package-info.java file exists for each package.     -->
+  <!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
+  <!--<module name="JavadocPackage"/>-->
+
+  <!-- Checks whether files end with a new line.                        -->
+  <!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
+  <module name="NewlineAtEndOfFile"/>
+
+  <!-- Checks that property files contain the same keys.         -->
+  <!-- See https://checkstyle.org/config_misc.html#Translation -->
+  <module name="Translation"/>
+
+  <!-- Checks for Size Violations.                    -->
+  <!-- See https://checkstyle.org/config_sizes.html -->
+  <module name="FileLength"/>
+  <module name="LineLength">
+    <property name="fileExtensions" value="java"/>
+    <property name="max" value="120"/> <!-- Customization -->
+  </module>
+
+  <!-- Checks for whitespace                               -->
+  <!-- See https://checkstyle.org/config_whitespace.html -->
+  <module name="FileTabCharacter"/>
+
+  <!-- Miscellaneous other checks.                   -->
+  <!-- See https://checkstyle.org/config_misc.html -->
+  <module name="RegexpSingleline">
+    <property name="format" value="\s+$"/>
+    <property name="minimum" value="0"/>
+    <property name="maximum" value="0"/>
+    <property name="message" value="Line has trailing spaces."/>
+  </module>
+
+  <!-- Checks for Headers                                -->
+  <!-- See https://checkstyle.org/config_header.html   -->
+  <!-- <module name="Header"> -->
+  <!--   <property name="headerFile" value="${checkstyle.header.file}"/> -->
+  <!--   <property name="fileExtensions" value="java"/> -->
+  <!-- </module> -->
+
+  <module name="SuppressWarningsFilter"/>
+
+  <module name="TreeWalker">
+    <module name="SuppressWarningsHolder"/>
+
+    <!-- Checks for Javadoc comments.                     -->
+    <!-- See https://checkstyle.org/config_javadoc.html -->
+    <module name="InvalidJavadocPosition"/>
+    <module name="JavadocMethod"/>
+    <module name="JavadocType"/>
+    <!--<module name="JavadocVariable"/>-->
+    <module name="JavadocStyle"/>
+    <!--<module name="MissingJavadocMethod"/>-->
+
+    <!-- Checks for Naming Conventions.                  -->
+    <!-- See https://checkstyle.org/config_naming.html -->
+    <!--<module name="ConstantName"/>-->
+    <module name="LocalFinalVariableName"/>
+    <module name="LocalVariableName"/>
+    <module name="MemberName"/>
+    <module name="MethodName"/>
+    <module name="PackageName"/>
+    <module name="ParameterName"/>
+    <module name="StaticVariableName"/>
+    <module name="TypeName"/>
+
+    <!-- Checks for imports                              -->
+    <!-- See https://checkstyle.org/config_imports.html -->
+    <module name="AvoidStarImport"/>
+    <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+    <module name="RedundantImport"/>
+    <module name="UnusedImports">
+      <property name="processJavadoc" value="false"/>
+    </module>
+
+    <!-- Checks for Size Violations.                    -->
+    <!-- See https://checkstyle.org/config_sizes.html -->
+    <module name="MethodLength"/>
+    <module name="ParameterNumber"/>
+
+    <!-- Checks for whitespace                               -->
+    <!-- See https://checkstyle.org/config_whitespace.html -->
+    <module name="EmptyForIteratorPad">
+      <property name="option" value="space"/>
+    </module>
+    <module name="GenericWhitespace"/>
+    <module name="MethodParamPad"/>
+    <module name="NoWhitespaceAfter"/>
+    <module name="NoWhitespaceBefore"/>
+    <module name="OperatorWrap"/>
+    <module name="ParenPad"/>
+    <module name="TypecastParenPad"/>
+    <module name="WhitespaceAfter"/>
+    <!--<module name="WhitespaceAround"/>-->
+
+    <!-- Modifier Checks                                    -->
+    <!-- See https://checkstyle.org/config_modifier.html -->
+    <module name="ModifierOrder"/>
+    <module name="RedundantModifier"/>
+
+    <!-- Checks for blocks. You know, those {}'s         -->
+    <!-- See https://checkstyle.org/config_blocks.html -->
+    <module name="AvoidNestedBlocks"/>
+    <module name="EmptyBlock"/>
+    <module name="LeftCurly"/>
+    <module name="NeedBraces"/>
+    <module name="RightCurly"/>
+
+    <!-- Checks for common coding problems               -->
+    <!-- See https://checkstyle.org/config_coding.html -->
+    <module name="EmptyStatement"/>
+    <module name="EqualsHashCode"/>
+    <!--<module name="HiddenField"/>-->
+    <module name="IllegalInstantiation"/>
+    <module name="InnerAssignment"/>
+    <!--<module name="MagicNumber"/>-->
+    <module name="MissingSwitchDefault"/>
+    <module name="MultipleVariableDeclarations"/>
+    <module name="SimplifyBooleanExpression"/>
+    <module name="SimplifyBooleanReturn"/>
+
+    <!-- Checks for class design                         -->
+    <!-- See https://checkstyle.org/config_design.html -->
+    <!--<module name="DesignForExtension"/>-->
+    <module name="FinalClass"/>
+    <!--<module name="HideUtilityClassConstructor"/>-->
+    <module name="InterfaceIsType"/>
+    <!--<module name="VisibilityModifier"/>-->
+
+    <!-- Miscellaneous other checks.                   -->
+    <!-- See https://checkstyle.org/config_misc.html -->
+    <module name="ArrayTypeStyle"/>
+    <!--<module name="FinalParameters"/>-->
+    <module name="TodoComment"/>
+    <module name="UpperEll"/>
+
+    <!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
+    <module name="SuppressionXpathFilter">
+      <property name="file" 
value="${org.checkstyle.sun.suppressionxpathfilter.config}"
+                default="checkstyle-xpath-suppressions.xml" />
+      <property name="optional" value="true"/>
+    </module>
+
+  </module>
+
+</module>
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..19529dd
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# 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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+#   JAVA_HOME - location of a JDK home dir, required when download maven via 
java source
+#   MVNW_REPOURL - repo url base for downloading maven distribution
+#   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+#   MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; 
others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+  [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+  native_path() { cygpath --path --windows "$1"; }
+  ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+  # For Cygwin and MinGW, ensure paths are in Unix format before anything is 
touched
+  if [ -n "${JAVA_HOME-}" ]; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+      JAVACCMD="$JAVA_HOME/jre/sh/javac"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+      JAVACCMD="$JAVA_HOME/bin/javac"
+
+      if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+        echo "The JAVA_HOME environment variable is not defined correctly, so 
mvnw cannot run." >&2
+        echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" 
or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+        return 1
+      fi
+    fi
+  else
+    JAVACMD="$(
+      'set' +e
+      'unset' -f command 2>/dev/null
+      'command' -v java
+    )" || :
+    JAVACCMD="$(
+      'set' +e
+      'unset' -f command 2>/dev/null
+      'command' -v javac
+    )" || :
+
+    if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+      echo "The java/javac command does not exist in PATH nor is JAVA_HOME 
set, so mvnw cannot run." >&2
+      return 1
+    fi
+  fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+  str="${1:-}" h=0
+  while [ -n "$str" ]; do
+    char="${str%"${str#?}"}"
+    h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+    str="${str#?}"
+  done
+  printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+  printf %s\\n "$1" >&2
+  exit 1
+}
+
+trim() {
+  # MWRAPPER-139:
+  #   Trims trailing and leading whitespace, carriage returns, tabs, and 
linefeeds.
+  #   Needed for removing poorly interpreted newline sequences when running in 
more
+  #   exotic environments such as mingw bash on Windows.
+  printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires 
.mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+  case "${key-}" in
+  distributionUrl) distributionUrl=$(trim "${value-}") ;;
+  distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+  esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in 
${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+  MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+  case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+  *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+  :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+  :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+  :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+  *)
+    echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use 
pure java version" >&2
+    distributionPlatform=linux-amd64
+    ;;
+  esac
+  distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+  ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: 
~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
+[ -z "${MVNW_REPOURL-}" ] || 
distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string
 "$distributionUrl")"
+
+exec_maven() {
+  unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+  exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec 
$MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+  verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+  exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or 
maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+  clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+  trap clean HUP INT TERM EXIT
+else
+  die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+  distributionUrl="${distributionUrl%.zip}.tar.gz"
+  distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q 
__MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' 
__MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' 
;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+  verbose "Found wget ... using wget"
+  wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O 
"$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch 
$distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+  verbose "Found curl ... using curl"
+  curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o 
"$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: 
Failed to fetch $distributionUrl"
+elif set_java_home; then
+  verbose "Falling back to use Java to download"
+  javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+  targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+  cat >"$javaSource" <<-END
+       public class Downloader extends java.net.Authenticator
+       {
+         protected java.net.PasswordAuthentication getPasswordAuthentication()
+         {
+           return new java.net.PasswordAuthentication( System.getenv( 
"MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+         }
+         public static void main( String[] args ) throws Exception
+         {
+           setDefault( new Downloader() );
+           java.nio.file.Files.copy( java.net.URI.create( args[0] 
).toURL().openStream(), java.nio.file.Paths.get( args[1] 
).toAbsolutePath().normalize() );
+         }
+       }
+       END
+  # For Cygwin/MinGW, switch paths to Windows format before running javac and 
java
+  verbose " - Compiling Downloader.java ..."
+  "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed 
to compile Downloader.java"
+  verbose " - Running Downloader.java ..."
+  "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" 
Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+  distributionSha256Result=false
+  if [ "$MVN_CMD" = mvnd.sh ]; then
+    echo "Checksum validation is not supported for maven-mvnd." >&2
+    echo "Please disable validation by removing 'distributionSha256Sum' from 
your maven-wrapper.properties." >&2
+    exit 1
+  elif command -v sha256sum >/dev/null; then
+    if echo "$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName" | 
sha256sum -c >/dev/null 2>&1; then
+      distributionSha256Result=true
+    fi
+  elif command -v shasum >/dev/null; then
+    if echo "$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName" | 
shasum -a 256 -c >/dev/null 2>&1; then
+      distributionSha256Result=true
+    fi
+  else
+    echo "Checksum validation was requested but neither 'sha256sum' or 
'shasum' are available." >&2
+    echo "Please install either command, or disable validation by removing 
'distributionSha256Sum' from your maven-wrapper.properties." >&2
+    exit 1
+  fi
+  if [ $distributionSha256Result = false ]; then
+    echo "Error: Failed to validate Maven distribution SHA-256, your Maven 
distribution might be compromised." >&2
+    echo "If you updated your Maven version, you need to update the specified 
distributionSha256Sum property." >&2
+    exit 1
+  fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+  unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} 
"$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed 
to unzip"
+else
+  tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} 
"$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed 
to untar"
+fi
+printf %s\\n "$distributionUrl" 
>"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d 
"$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..249bdf3
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM 
----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM 
----------------------------------------------------------------------------
+
+@REM 
----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM   MVNW_REPOURL - repo url base for downloading maven distribution
+@REM   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM   MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM 
----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& 
{$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock 
([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+  IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE 
(echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+  $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw 
"$scriptDir/.mvn/wrapper/maven-wrapper.properties" | 
ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+  Write-Error "cannot read distributionUrl property in 
$scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+  "maven-mvnd-*" {
+    $USE_MVND = $true
+    $distributionUrl = $distributionUrl -replace 
'-bin\.[^.]*$',"-windows-amd64.zip"
+    $MVN_CMD = "mvnd.cmd"
+    break
+  }
+  default {
+    $USE_MVND = $false
+    $MVN_CMD = $script -replace '^mvnw','mvn'
+    break
+  }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: 
~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
+if ($env:MVNW_REPOURL) {
+  $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { 
"/maven/mvnd/" }
+  $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl 
-replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' 
-replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+  $MAVEN_HOME_PARENT = 
"$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = 
([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl)
 | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+  Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+  Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+  exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq 
$distributionUrlNameMain)) {
+  Write-Error "distributionUrl is not valid, must end with *-bin.zip, but 
found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path 
"$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+  if ($TMP_DOWNLOAD_DIR.Exists) {
+    try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+    catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+  }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+  $webclient.Credentials = New-Object 
System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, 
"$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw 
"$scriptDir/.mvn/wrapper/maven-wrapper.properties" | 
ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+  if ($USE_MVND) {
+    Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease 
disable validation by removing 'distributionSha256Sum' from your 
maven-wrapper.properties."
+  }
+  Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function 
Get-FileHash
+  if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm 
SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+    Write-Error "Error: Failed to validate Maven distribution SHA-256, your 
Maven distribution might be compromised. If you updated your Maven version, you 
need to update the specified distributionSha256Sum property."
+  }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath 
"$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName 
$MAVEN_HOME_NAME | Out-Null
+try {
+  Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination 
$MAVEN_HOME_PARENT | Out-Null
+} catch {
+  if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+    Write-Error "fail to move MAVEN_HOME"
+  }
+} finally {
+  try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+  catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..10a1f14
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>29</version>
+    </parent>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-java-contrib</artifactId>
+    <packaging>pom</packaging>
+    <name>Pulsar Java Contrib</name>
+
+    <properties>
+        <lombok.version>1.18.32</lombok.version>
+        <slf4j.version>2.0.13</slf4j.version>
+        <maven.compiler.release>17</maven.compiler.release>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <pulsar.version>3.3.1</pulsar.version>
+        
<maven-checkstyle-plugin.version>3.4.0</maven-checkstyle-plugin.version>
+        <puppycrawl.checkstyle.version>8.45.1</puppycrawl.checkstyle.version>
+        <spotless-maven-plugin.version>2.43.0</spotless-maven-plugin.version>
+        <maven-compiler-plugin>3.13.0</maven-compiler-plugin>
+        <maven-wrapper-plugin.version>3.3.2</maven-wrapper-plugin.version>
+        <license-maven-plugin.version>4.5</license-maven-plugin.version>
+        <org.testing.version>7.10.2</org.testing.version>
+        <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
+    </properties>
+
+    <modules>
+        <module>pulsar-client-common-contrib</module>
+        <module>pulsar-loadbalance-contrib</module>
+        <module>pulsar-interceptor-contrib</module>
+        <module>pulsar-connector-contrib</module>
+        <module>pulsar-function-contrib</module>
+        <module>pulsar-bookkeeper-contrib</module>
+        <module>pulsar-transaction-contrib</module>
+        <module>pulsar-metrics-contrib</module>
+        <module>pulsar-auth-contrib</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-bom</artifactId>
+                <version>${slf4j.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.pulsar</groupId>
+                <artifactId>pulsar-bom</artifactId>
+                <version>${pulsar.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>${lombok.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <version>${org.testing.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-wrapper-plugin</artifactId>
+                <version>${maven-wrapper-plugin.version}</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.version}</version>
+                <configuration>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                            <version>${lombok.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+            <!-- Spotless Plugin -->
+            <plugin>
+                <groupId>com.diffplug.spotless</groupId>
+                <artifactId>spotless-maven-plugin</artifactId>
+                <version>${spotless-maven-plugin.version}</version>
+                <configuration>
+                    <java>
+                        <googleJavaFormat>
+                            <version>1.7</version>
+                            <style>GOOGLE</style>
+                        </googleJavaFormat>
+                    </java>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>apply</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- Checkstyle Plugin -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <version>${maven-checkstyle-plugin.version}</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>com.puppycrawl.tools</groupId>
+                        <artifactId>checkstyle</artifactId>
+                        <version>${puppycrawl.checkstyle.version}</version>
+                    </dependency>
+                </dependencies>
+                <configuration>
+                    
<configLocation>./src/main/resources/checkstyle.xml</configLocation>
+                    
<suppressionsLocation>./src/main/resources/suppressions.xml</suppressionsLocation>
+                    
<includeTestSourceDirectory>true</includeTestSourceDirectory>
+                    <encoding>UTF-8</encoding>
+                    <excludes>**/*.proto</excludes> <!-- Example exclusion -->
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>validate-checkstyle</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <!-- Check that source files have appropriate license headers 
-->
+                <groupId>com.mycila</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <version>${license-maven-plugin.version}</version>
+                <configuration>
+                    <header>etc/APACHE-2.txt</header>
+                    <excludes>
+                        <exclude>LICENSE</exclude>
+                        <exclude>NOTICE</exclude>
+                        <exclude>payload/**</exclude>
+                        <exclude>**/*.pyc</exclude>
+                        <exclude>**/.pydevproject</exclude>
+                        <exclude>.github/**</exclude>
+                    </excludes>
+                    <mapping>
+                        <benchmark>SCRIPT_STYLE</benchmark>
+                        <j2>SCRIPT_STYLE</j2>
+                        <conf>SCRIPT_STYLE</conf>
+                        <yaml>SCRIPT_STYLE</yaml>
+                        <md>XML_STYLE</md>
+                        <lombok.config>SCRIPT_STYLE</lombok.config>
+                        <puml>APOSTROPHE_STYLE</puml>
+                    </mapping>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>com.mycila</groupId>
+                        <artifactId>license-maven-plugin-git</artifactId>
+                        <version>${license-maven-plugin.version}</version>
+                    </dependency>
+                </dependencies>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/pulsar-auth-contrib/pom.xml b/pulsar-auth-contrib/pom.xml
new file mode 100644
index 0000000..15eba44
--- /dev/null
+++ b/pulsar-auth-contrib/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-auth-contrib</artifactId>
+
+</project>
\ No newline at end of file
diff --git a/pulsar-bookkeeper-contrib/pom.xml 
b/pulsar-bookkeeper-contrib/pom.xml
new file mode 100644
index 0000000..8c94b80
--- /dev/null
+++ b/pulsar-bookkeeper-contrib/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-bookkeeper-contrib</artifactId>
+
+
+</project>
\ No newline at end of file
diff --git a/pulsar-client-common-contrib/pom.xml 
b/pulsar-client-common-contrib/pom.xml
new file mode 100644
index 0000000..2619c53
--- /dev/null
+++ b/pulsar-client-common-contrib/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-client-common-contrib</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.pulsar</groupId>
+            <artifactId>pulsar-client-all</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git 
a/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/MessageListenerExecutor.java
 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/MessageListenerExecutor.java
new file mode 100644
index 0000000..bc3870f
--- /dev/null
+++ 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/MessageListenerExecutor.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed 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.pulsar.client.api;
+
+/**
+ * Interface for providing service to execute message listeners. TODO 
Duplicate class, to be deleted
+ * after merging https://github.com/apache/pulsar/pull/22861
+ */
+public interface MessageListenerExecutor {
+
+  /**
+   * select a thread by message to execute the runnable!
+   *
+   * <p>Suggestions:
+   *
+   * <p>1. The message listener task will be submitted to this executor for 
execution, so the
+   * implementations of this interface should carefully consider execution 
order if sequential
+   * consumption is required.
+   *
+   * <p>2. The users should release resources(e.g. threads) of the executor 
after closing the
+   * consumer to avoid leaks.
+   *
+   * @param message the message
+   * @param runnable the runnable to execute, that is, the message listener 
task
+   */
+  void execute(Message<?> message, Runnable runnable);
+}
diff --git 
a/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/CommonMessageListenerExecutor.java
 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/CommonMessageListenerExecutor.java
new file mode 100644
index 0000000..20c92ba
--- /dev/null
+++ 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/CommonMessageListenerExecutor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed 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.pulsar.client.api.impl;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageListenerExecutor;
+
+public class CommonMessageListenerExecutor implements MessageListenerExecutor {
+  private final ExecutorService executorService;
+
+  public CommonMessageListenerExecutor(int numThreads, final String 
subscriptionName) {
+    this.executorService =
+        new ThreadPoolExecutor(
+            numThreads,
+            numThreads,
+            10000L,
+            TimeUnit.MILLISECONDS,
+            new LinkedBlockingQueue<Runnable>(),
+            new ThreadFactory() {
+              private final AtomicInteger threadId = new AtomicInteger(0);
+
+              @Override
+              public Thread newThread(Runnable r) {
+                return new Thread(
+                    r, subscriptionName + "-listener-executor-" + 
threadId.incrementAndGet());
+              }
+            });
+  }
+
+  @Override
+  public void execute(Message<?> message, Runnable runnable) {
+    this.executorService.execute(runnable);
+  }
+}
diff --git 
a/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/KeySharedMessageListenerExecutor.java
 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/KeySharedMessageListenerExecutor.java
new file mode 100644
index 0000000..0762572
--- /dev/null
+++ 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/KeySharedMessageListenerExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed 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.pulsar.client.api.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutorService;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageListenerExecutor;
+import org.apache.pulsar.client.util.ExecutorProvider;
+
+public class KeySharedMessageListenerExecutor implements 
MessageListenerExecutor {
+  ExecutorProvider executorProvider;
+
+  public KeySharedMessageListenerExecutor(int numThreads, String 
subscriptionName) {
+    this.executorProvider =
+        new ExecutorProvider(numThreads, subscriptionName + 
"listener-executor-");
+  }
+
+  @Override
+  public void execute(Message<?> message, Runnable runnable) {
+    byte[] key = "".getBytes(StandardCharsets.UTF_8);
+    if (message.hasKey()) {
+      key = message.getKeyBytes();
+    } else if (message.hasOrderingKey()) {
+      key = message.getOrderingKey();
+    }
+    // select a thread by message key to execute the runnable!
+    // that say, the message listener task with same order key
+    // will be executed by the same thread
+    ExecutorService executorService = executorProvider.getExecutor(key);
+    // executorService is a SingleThreadExecutor
+    executorService.execute(runnable);
+  }
+}
diff --git 
a/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/PartitionOrderMessageListenerExecutor.java
 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/PartitionOrderMessageListenerExecutor.java
new file mode 100644
index 0000000..e18e496
--- /dev/null
+++ 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/PartitionOrderMessageListenerExecutor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed 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.pulsar.client.api.impl;
+
+import java.util.concurrent.ExecutorService;
+import org.apache.pulsar.client.api.Message;
+import org.apache.pulsar.client.api.MessageListenerExecutor;
+import org.apache.pulsar.client.util.ExecutorProvider;
+
+public class PartitionOrderMessageListenerExecutor implements 
MessageListenerExecutor {
+  private final ExecutorProvider executorProvider;
+
+  public PartitionOrderMessageListenerExecutor(int numThreads, String 
subscriptionName) {
+    this.executorProvider =
+        new ExecutorProvider(numThreads, subscriptionName + 
"listener-executor-");
+  }
+
+  @Override
+  public void execute(Message<?> message, Runnable runnable) {
+    // select a thread by partition topic name to execute the runnable!
+    // that say, the message listener task from the same partition topic
+    // will be executed by the same thread
+    ExecutorService executorService =
+        executorProvider.getExecutor(message.getTopicName().getBytes());
+    // executorService is a SingleThreadExecutor
+    executorService.execute(runnable);
+  }
+}
diff --git 
a/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/package-info.java
 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/package-info.java
new file mode 100644
index 0000000..1a92fda
--- /dev/null
+++ 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/impl/package-info.java
@@ -0,0 +1,14 @@
+/*
+ * Licensed 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.pulsar.client.api.impl;
\ No newline at end of file
diff --git 
a/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/package-info.java
 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/package-info.java
new file mode 100644
index 0000000..d940e2c
--- /dev/null
+++ 
b/pulsar-client-common-contrib/src/main/java/org/apache/pulsar/client/api/package-info.java
@@ -0,0 +1,14 @@
+/*
+ * Licensed 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.pulsar.client.api;
\ No newline at end of file
diff --git a/pulsar-client-common-contrib/src/test/java/DemoTest.java 
b/pulsar-client-common-contrib/src/test/java/DemoTest.java
new file mode 100644
index 0000000..7b2a7c4
--- /dev/null
+++ b/pulsar-client-common-contrib/src/test/java/DemoTest.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed 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.
+ */
+import lombok.extern.slf4j.Slf4j;
+import org.testng.annotations.Test;
+
+@Slf4j
+public class DemoTest {
+
+  @Test
+  public void testDemo() {
+    log.info("=== Test started ===");
+  }
+}
diff --git a/pulsar-connector-contrib/pom.xml b/pulsar-connector-contrib/pom.xml
new file mode 100644
index 0000000..1730b45
--- /dev/null
+++ b/pulsar-connector-contrib/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-connector-contrib</artifactId>
+</project>
\ No newline at end of file
diff --git a/pulsar-function-contrib/pom.xml b/pulsar-function-contrib/pom.xml
new file mode 100644
index 0000000..ba77485
--- /dev/null
+++ b/pulsar-function-contrib/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-function-contrib</artifactId>
+
+</project>
\ No newline at end of file
diff --git a/pulsar-interceptor-contrib/pom.xml 
b/pulsar-interceptor-contrib/pom.xml
new file mode 100644
index 0000000..ef5f258
--- /dev/null
+++ b/pulsar-interceptor-contrib/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-interceptor-contrib</artifactId>
+
+
+
+</project>
\ No newline at end of file
diff --git a/pulsar-loadbalance-contrib/pom.xml 
b/pulsar-loadbalance-contrib/pom.xml
new file mode 100644
index 0000000..b582721
--- /dev/null
+++ b/pulsar-loadbalance-contrib/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-loadbalance-contrib</artifactId>
+
+</project>
\ No newline at end of file
diff --git a/pulsar-metrics-contrib/pom.xml b/pulsar-metrics-contrib/pom.xml
new file mode 100644
index 0000000..26043b2
--- /dev/null
+++ b/pulsar-metrics-contrib/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-metrics-contrib</artifactId>
+</project>
\ No newline at end of file
diff --git a/pulsar-transaction-contrib/pom.xml 
b/pulsar-transaction-contrib/pom.xml
new file mode 100644
index 0000000..2dd8dfd
--- /dev/null
+++ b/pulsar-transaction-contrib/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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.
+
+-->
+<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";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>pulsar-java-contrib</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <inceptionYear>2024</inceptionYear>
+
+    <artifactId>pulsar-transaction-contrib</artifactId>
+
+</project>
\ No newline at end of file
diff --git a/src/main/resources/checkstyle.xml 
b/src/main/resources/checkstyle.xml
new file mode 100644
index 0000000..c56dfb2
--- /dev/null
+++ b/src/main/resources/checkstyle.xml
@@ -0,0 +1,429 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed 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";>
+
+<!-- This is a checkstyle configuration file. For descriptions of
+what the following rules do, please see the checkstyle configuration
+page at http://checkstyle.sourceforge.net/config.html -->
+
+<module name="Checker">
+    <!--
+
+    LENGTH and CODING CHECKS
+
+    -->
+
+    <module name="LineLength">
+        <!-- Checks if a line is too long. -->
+        <property name="max" value="120"/>
+        <property name="severity" value="error"/>
+
+        <!-- Checks only for Java files. -->
+        <property name="fileExtensions" value="java"/>
+
+        <!--
+          The default ignore pattern exempts the following elements:
+            - import statements
+            - long URLs inside comments
+        -->
+
+        <property name="ignorePattern"
+                  value="^(package .*;\s*)|(import .*;\s*)|( *\* 
.*https?://.*)$"/>
+    </module>
+
+    <!-- Required to support SuppressWarningsComment -->
+    <!--    <module name="FileContentsHolder"/>-->
+
+    <module name="SuppressWarningsFilter"/>
+
+    <module name="FileTabCharacter">
+        <!-- Checks that there are no tab characters in the file. -->
+    </module>
+
+    <module name="RegexpSingleline">
+        <!-- Checks that TODOs don't have stuff in parenthesis, e.g., 
username. -->
+        <property name="format" value="((//.*)|(\*.*))TODO\("/>
+        <property name="message" value="TODO comments must not include 
usernames." />
+        <property name="severity" value="error" />
+    </module>
+
+    <module name="RegexpSingleline">
+        <property name="format" value="\s+$"/>
+        <property name="message" value="Trailing whitespace"/>
+        <property name="severity" value="error"/>
+    </module>
+
+    <module name="RegexpSingleline">
+        <property name="format" value="Throwables.propagate\("/>
+        <property name="message" value="Throwables.propagate is deprecated"/>
+        <property name="severity" value="error"/>
+    </module>
+
+    <!-- Prevent *Tests.java as tools may not pick them up -->
+    <module name="RegexpOnFilename">
+        <property name="fileNamePattern" value=".*Tests\.java$" />
+    </module>
+
+    <module name="SuppressionFilter">
+        <property name="file" value="${checkstyle.suppressions.file}" 
default="suppressions.xml" />
+    </module>
+
+    <!-- Check that every module has a package-info.java -->
+    <module name="JavadocPackage"/>
+
+    <!-- All Java AST specific tests live under TreeWalker module. -->
+    <module name="TreeWalker">
+
+        <module name="SuppressionCommentFilter">
+            <property name="offCommentFormat" value="CHECKSTYLE.OFF\: 
([\w\|]+)"/>
+            <property name="onCommentFormat" value="CHECKSTYLE.ON\: 
([\w\|]+)"/>
+            <property name="checkFormat" value="$1"/>
+        </module>
+
+        <module name="SuppressWarningsHolder" />
+
+        <module name="TodoComment">
+            <!-- Checks that disallowed strings are not used in comments.  -->
+            <property name="format" value="(FIXME)|(XXX)|(@author)" />
+        </module>
+
+        <!--
+
+        IMPORT CHECKS
+
+        -->
+
+        <module name="RedundantImport">
+            <!-- Checks for redundant import statements. -->
+            <property name="severity" value="error"/>
+            <message key="import.redundancy"
+                     value="Redundant import {0}."/>
+        </module>
+
+        <module name="ImportOrder">
+            <property name="severity" value="error"/>
+            <!-- This ensures that static imports go first. -->
+            <property name="option" value="top"/>
+            <property name="sortStaticImportsAlphabetically" value="true"/>
+            <property name="tokens" value="STATIC_IMPORT, IMPORT"/>
+            <message key="import.ordering"
+                     value="Import {0} appears after other imports that it 
should precede"/>
+        </module>
+
+        <module name="AvoidStarImport">
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="IllegalImport">
+            <property name="illegalPkgs"
+                      value="autovalue.shaded, avro.shaded, bk-shade, 
com.google.api.client.repackaged, com.google.appengine.repackaged, 
org.apache.curator.shaded, org.testcontainers.shaded, org.junit" />
+        </module>
+
+        <module name="RedundantModifier">
+            <!-- Checks for redundant modifiers on various symbol definitions.
+              See: 
http://checkstyle.sourceforge.net/config_modifier.html#RedundantModifier
+            -->
+            <property name="tokens" value="METHOD_DEF, VARIABLE_DEF, 
ANNOTATION_FIELD_DEF, INTERFACE_DEF, CLASS_DEF, ENUM_DEF"/>
+        </module>
+
+        <!--
+            IllegalImport cannot exclude classes, and c.g.api.client.util is 
used for some shaded
+            code and some useful code. So we need to fall back to Regexp.
+        -->
+        <module name="RegexpSinglelineJava">
+            <property name="format" 
value="com\.google\.api\.client\.util\.(ByteStreams|Charsets|Collections2|Joiner|Lists|Maps|Objects|Preconditions|Sets|Strings|Throwables)"/>
+        </module>
+
+        <!--
+             Require static importing from Preconditions.
+        -->
+        <module name="RegexpSinglelineJava">
+            <property name="format" value="^import 
com.google.common.base.Preconditions;$"/>
+            <property name="message" value="Static import functions from Guava 
Preconditions"/>
+        </module>
+
+        <module name="UnusedImports">
+            <property name="severity" value="error"/>
+            <property name="processJavadoc" value="true"/>
+            <message key="import.unused"
+                     value="Unused import: {0}."/>
+        </module>
+
+        <!--
+
+        JAVADOC CHECKS
+
+        -->
+
+        <!-- Checks for Javadoc comments.                     -->
+        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+        <module name="JavadocMethod">
+            <property name="accessModifiers" value="protected"/>
+            <property name="severity" value="error"/>
+            <property name="allowMissingParamTags" value="true"/>
+            <property name="allowMissingReturnTag" value="true"/>
+        </module>
+
+        <!-- Check that paragraph tags are used correctly in Javadoc. -->
+        <!--        <module name="JavadocParagraph"/>-->
+
+        <module name="JavadocType">
+            <property name="scope" value="protected"/>
+            <property name="severity" value="error"/>
+            <property name="allowMissingParamTags" value="true"/>
+        </module>
+
+        <module name="JavadocStyle">
+            <property name="severity" value="error"/>
+            <property name="checkHtml" value="true"/>
+        </module>
+
+        <!--
+
+        NAMING CHECKS
+
+        -->
+
+        <!-- Item 38 - Adhere to generally accepted naming conventions -->
+
+        <module name="PackageName">
+            <!-- Validates identifiers for package names against the
+              supplied expression. -->
+            <!-- Here the default checkstyle rule restricts package name parts 
to
+              seven characters, this is not in line with common practice at 
Google.
+            -->
+            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="TypeNameCheck">
+            <!-- Validates static, final fields against the
+            expression "^[A-Z][a-zA-Z0-9]*$". -->
+            <metadata name="altname" value="TypeName"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="ConstantNameCheck">
+            <!-- Validates non-private, static, final fields against the 
supplied
+            public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
+            <metadata name="altname" value="ConstantName"/>
+            <property name="applyToPublic" value="true"/>
+            <property name="applyToProtected" value="true"/>
+            <property name="applyToPackage" value="true"/>
+            <property name="applyToPrivate" value="false"/>
+            <property name="format" value="^([A-Z][A-Za-z0-9_]*|FLAG_.*)$"/>
+            <message key="name.invalidPattern"
+                     value="Variable ''{0}'' should be in ALL_CAPS (if it is a 
constant) or be private (otherwise)."/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="StaticVariableNameCheck">
+            <!-- Validates static, non-final fields against the supplied
+            expression "^[a-z][a-zA-Z0-9]*_?$". -->
+            <metadata name="altname" value="StaticVariableName"/>
+            <property name="applyToPublic" value="true"/>
+            <property name="applyToProtected" value="true"/>
+            <property name="applyToPackage" value="true"/>
+            <property name="applyToPrivate" value="true"/>
+            <property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="MemberNameCheck">
+            <!-- Validates non-static members against the supplied expression. 
-->
+            <metadata name="altname" value="MemberName"/>
+            <property name="applyToPublic" value="true"/>
+            <property name="applyToProtected" value="true"/>
+            <property name="applyToPackage" value="true"/>
+            <property name="applyToPrivate" value="true"/>
+            <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="MethodNameCheck">
+            <!-- Validates identifiers for method names. -->
+            <metadata name="altname" value="MethodName"/>
+            <property name="format" 
value="(^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$|Void)"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="ParameterName">
+            <!-- Validates identifiers for method parameters against the
+              expression "^[a-z][a-zA-Z0-9]*$". -->
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="LocalFinalVariableName">
+            <!-- Validates identifiers for local final variables against the
+              expression "^[a-z][a-zA-Z0-9]*$". -->
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="LocalVariableName">
+            <!-- Validates identifiers for local variables against the
+              expression "^[a-z][a-zA-Z0-9]*$". -->
+            <property name="severity" value="error"/>
+        </module>
+
+        <!-- Type parameters must be either one of the four blessed letters
+        T, K, V, W, X or else be capital-case terminated with a T,
+        such as MyGenericParameterT -->
+        <module name="ClassTypeParameterName">
+            <property name="format" 
value="^(((T|K|V|W|X|R)[0-9]*)|([A-Z][a-z][a-zA-Z]*))$"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="MethodTypeParameterName">
+            <property name="format" 
value="^(((T|K|V|W|X|R)[0-9]*)|([A-Z][a-z][a-zA-Z]*T))$"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="InterfaceTypeParameterName">
+            <property name="format" 
value="^(((T|K|V|W|X|R)[0-9]*)|([A-Z][a-z][a-zA-Z]*T))$"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="LeftCurly">
+            <!-- Checks for placement of the left curly brace ('{'). -->
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="RightCurly">
+            <!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
+            the same line. e.g., the following example is fine:
+            <pre>
+              if {
+                ...
+              } else
+            </pre>
+            -->
+            <!-- This next example is not fine:
+            <pre>
+              if {
+                ...
+              }
+              else
+            </pre>
+            -->
+            <property name="option" value="same"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <!-- Checks for braces around if and else blocks -->
+        <module name="NeedBraces">
+            <property name="severity" value="error"/>
+            <property name="tokens" value="LITERAL_IF, LITERAL_ELSE, 
LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
+        </module>
+
+        <module name="UpperEll">
+            <!-- Checks that long constants are defined with an upper ell.-->
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="FallThrough">
+            <!-- Warn about falling through to the next case statement.  
Similar to
+            javac -Xlint:fallthrough, but the check is suppressed if a 
single-line comment
+            on the last non-blank line preceding the fallen-into case contains 
'fall through' (or
+            some other variants that we don't publicized to promote 
consistency).
+            -->
+            <property name="reliefPattern"
+                      value="fall through|Fall through|fallthru|Fallthru|falls 
through|Falls through|fallthrough|Fallthrough|No break|NO break|no 
break|continue on"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <!-- Checks for over-complicated boolean expressions. -->
+        <module name="SimplifyBooleanExpression"/>
+
+        <!-- Detects empty statements (standalone ";" semicolon). -->
+        <module name="EmptyStatement"/>
+
+        <!--
+
+        WHITESPACE CHECKS
+
+        -->
+
+        <module name="WhitespaceAround">
+            <!-- Checks that various tokens are surrounded by whitespace.
+                 This includes most binary operators and keywords followed
+                 by regular or curly braces.
+            -->
+            <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
+        BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
+        EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
+        LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
+        LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
+        MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
+        SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="WhitespaceAfter">
+            <!-- Checks that commas, semicolons and typecasts are followed by
+                 whitespace.
+            -->
+            <property name="tokens" value="COMMA, SEMI, TYPECAST"/>
+        </module>
+
+        <module name="NoWhitespaceAfter">
+            <!-- Checks that there is no whitespace after various unary 
operators.
+                 Linebreaks are allowed.
+            -->
+            <property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, 
UNARY_MINUS,
+        UNARY_PLUS"/>
+            <property name="allowLineBreaks" value="true"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="NoWhitespaceBefore">
+            <!-- Checks that there is no whitespace before various unary 
operators.
+                 Linebreaks are allowed.
+            -->
+            <property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
+            <property name="allowLineBreaks" value="true"/>
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="OperatorWrap">
+            <!-- Checks that operators like + and ? appear at newlines rather 
than
+                 at the end of the previous line.
+            -->
+            <property name="option" value="NL"/>
+            <property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL,
+        GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD,
+        NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
+        </module>
+
+        <module name="OperatorWrap">
+            <!-- Checks that assignment operators are at the end of the line. 
-->
+            <property name="option" value="eol"/>
+            <property name="tokens" value="ASSIGN"/>
+        </module>
+
+        <module name="ParenPad">
+            <!-- Checks that there is no whitespace before close parens or 
after
+                 open parens.
+            -->
+            <property name="severity" value="error"/>
+        </module>
+
+        <module name="ModifierOrder"/>
+
+    </module>
+</module>
diff --git a/src/main/resources/suppressions.xml 
b/src/main/resources/suppressions.xml
new file mode 100644
index 0000000..4e341c3
--- /dev/null
+++ b/src/main/resources/suppressions.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!--
+
+    Licensed 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 suppressions PUBLIC
+        "-//Puppy Crawl//DTD Suppressions 1.1//EN"
+        "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd";>
+
+<suppressions>
+    <suppress checks="JavadocPackage" files=".*[\\/]src[\\/]test[\\/].*"/>
+    <suppress checks="JavadocPackage" files=".*[\\/]maven-archetypes[\\/].*"/>
+    <suppress checks="JavadocPackage" files=".*[\\/]examples[\\/].*"/>
+
+    <!-- suppress javadoc check for impl classes and package-info.java -->
+    <suppress checks="JavadocType" files=".*Impl\.java$" />
+    <suppress checks="JavadocStyle" files=".+[\\/]package-info\.java$" />
+
+    <!-- suppress all checks in the generated directories -->
+    <suppress checks=".*" files=".+[\\/]generated[\\/].+\.java"/>
+    <suppress checks=".*" files=".+[\\/]generated-sources[\\/].+\.java"/>
+    <suppress checks=".*" files=".+[\\/]generated-test-sources[\\/].+\.java"/>
+    <suppress checks=".*" files=".*[\\/]LICENSE$"/>
+</suppressions>

Reply via email to