Commit 1d47516d7bff9f9e3d15f4f3417fd91e73c1041e
1 parent
447dda51
从官网COPY:https://github.com/apache/rocketmq-externals/tree/master/rocketmq-spring-boot-starter
Showing
17 changed files
with
1907 additions
and
0 deletions
.project
0 → 100644
LICENSE
0 → 100644
| 1 | + | |
| 2 | + Apache License | |
| 3 | + Version 2.0, January 2004 | |
| 4 | + http://www.apache.org/licenses/ | |
| 5 | + | |
| 6 | + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 7 | + | |
| 8 | + 1. Definitions. | |
| 9 | + | |
| 10 | + "License" shall mean the terms and conditions for use, reproduction, | |
| 11 | + and distribution as defined by Sections 1 through 9 of this document. | |
| 12 | + | |
| 13 | + "Licensor" shall mean the copyright owner or entity authorized by | |
| 14 | + the copyright owner that is granting the License. | |
| 15 | + | |
| 16 | + "Legal Entity" shall mean the union of the acting entity and all | |
| 17 | + other entities that control, are controlled by, or are under common | |
| 18 | + control with that entity. For the purposes of this definition, | |
| 19 | + "control" means (i) the power, direct or indirect, to cause the | |
| 20 | + direction or management of such entity, whether by contract or | |
| 21 | + otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 22 | + outstanding shares, or (iii) beneficial ownership of such entity. | |
| 23 | + | |
| 24 | + "You" (or "Your") shall mean an individual or Legal Entity | |
| 25 | + exercising permissions granted by this License. | |
| 26 | + | |
| 27 | + "Source" form shall mean the preferred form for making modifications, | |
| 28 | + including but not limited to software source code, documentation | |
| 29 | + source, and configuration files. | |
| 30 | + | |
| 31 | + "Object" form shall mean any form resulting from mechanical | |
| 32 | + transformation or translation of a Source form, including but | |
| 33 | + not limited to compiled object code, generated documentation, | |
| 34 | + and conversions to other media types. | |
| 35 | + | |
| 36 | + "Work" shall mean the work of authorship, whether in Source or | |
| 37 | + Object form, made available under the License, as indicated by a | |
| 38 | + copyright notice that is included in or attached to the work | |
| 39 | + (an example is provided in the Appendix below). | |
| 40 | + | |
| 41 | + "Derivative Works" shall mean any work, whether in Source or Object | |
| 42 | + form, that is based on (or derived from) the Work and for which the | |
| 43 | + editorial revisions, annotations, elaborations, or other modifications | |
| 44 | + represent, as a whole, an original work of authorship. For the purposes | |
| 45 | + of this License, Derivative Works shall not include works that remain | |
| 46 | + separable from, or merely link (or bind by name) to the interfaces of, | |
| 47 | + the Work and Derivative Works thereof. | |
| 48 | + | |
| 49 | + "Contribution" shall mean any work of authorship, including | |
| 50 | + the original version of the Work and any modifications or additions | |
| 51 | + to that Work or Derivative Works thereof, that is intentionally | |
| 52 | + submitted to Licensor for inclusion in the Work by the copyright owner | |
| 53 | + or by an individual or Legal Entity authorized to submit on behalf of | |
| 54 | + the copyright owner. For the purposes of this definition, "submitted" | |
| 55 | + means any form of electronic, verbal, or written communication sent | |
| 56 | + to the Licensor or its representatives, including but not limited to | |
| 57 | + communication on electronic mailing lists, source code control systems, | |
| 58 | + and issue tracking systems that are managed by, or on behalf of, the | |
| 59 | + Licensor for the purpose of discussing and improving the Work, but | |
| 60 | + excluding communication that is conspicuously marked or otherwise | |
| 61 | + designated in writing by the copyright owner as "Not a Contribution." | |
| 62 | + | |
| 63 | + "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 64 | + on behalf of whom a Contribution has been received by Licensor and | |
| 65 | + subsequently incorporated within the Work. | |
| 66 | + | |
| 67 | + 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 68 | + this License, each Contributor hereby grants to You a perpetual, | |
| 69 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 70 | + copyright license to reproduce, prepare Derivative Works of, | |
| 71 | + publicly display, publicly perform, sublicense, and distribute the | |
| 72 | + Work and such Derivative Works in Source or Object form. | |
| 73 | + | |
| 74 | + 3. Grant of Patent License. Subject to the terms and conditions of | |
| 75 | + this License, each Contributor hereby grants to You a perpetual, | |
| 76 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 77 | + (except as stated in this section) patent license to make, have made, | |
| 78 | + use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 79 | + where such license applies only to those patent claims licensable | |
| 80 | + by such Contributor that are necessarily infringed by their | |
| 81 | + Contribution(s) alone or by combination of their Contribution(s) | |
| 82 | + with the Work to which such Contribution(s) was submitted. If You | |
| 83 | + institute patent litigation against any entity (including a | |
| 84 | + cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 85 | + or a Contribution incorporated within the Work constitutes direct | |
| 86 | + or contributory patent infringement, then any patent licenses | |
| 87 | + granted to You under this License for that Work shall terminate | |
| 88 | + as of the date such litigation is filed. | |
| 89 | + | |
| 90 | + 4. Redistribution. You may reproduce and distribute copies of the | |
| 91 | + Work or Derivative Works thereof in any medium, with or without | |
| 92 | + modifications, and in Source or Object form, provided that You | |
| 93 | + meet the following conditions: | |
| 94 | + | |
| 95 | + (a) You must give any other recipients of the Work or | |
| 96 | + Derivative Works a copy of this License; and | |
| 97 | + | |
| 98 | + (b) You must cause any modified files to carry prominent notices | |
| 99 | + stating that You changed the files; and | |
| 100 | + | |
| 101 | + (c) You must retain, in the Source form of any Derivative Works | |
| 102 | + that You distribute, all copyright, patent, trademark, and | |
| 103 | + attribution notices from the Source form of the Work, | |
| 104 | + excluding those notices that do not pertain to any part of | |
| 105 | + the Derivative Works; and | |
| 106 | + | |
| 107 | + (d) If the Work includes a "NOTICE" text file as part of its | |
| 108 | + distribution, then any Derivative Works that You distribute must | |
| 109 | + include a readable copy of the attribution notices contained | |
| 110 | + within such NOTICE file, excluding those notices that do not | |
| 111 | + pertain to any part of the Derivative Works, in at least one | |
| 112 | + of the following places: within a NOTICE text file distributed | |
| 113 | + as part of the Derivative Works; within the Source form or | |
| 114 | + documentation, if provided along with the Derivative Works; or, | |
| 115 | + within a display generated by the Derivative Works, if and | |
| 116 | + wherever such third-party notices normally appear. The contents | |
| 117 | + of the NOTICE file are for informational purposes only and | |
| 118 | + do not modify the License. You may add Your own attribution | |
| 119 | + notices within Derivative Works that You distribute, alongside | |
| 120 | + or as an addendum to the NOTICE text from the Work, provided | |
| 121 | + that such additional attribution notices cannot be construed | |
| 122 | + as modifying the License. | |
| 123 | + | |
| 124 | + You may add Your own copyright statement to Your modifications and | |
| 125 | + may provide additional or different license terms and conditions | |
| 126 | + for use, reproduction, or distribution of Your modifications, or | |
| 127 | + for any such Derivative Works as a whole, provided Your use, | |
| 128 | + reproduction, and distribution of the Work otherwise complies with | |
| 129 | + the conditions stated in this License. | |
| 130 | + | |
| 131 | + 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 132 | + any Contribution intentionally submitted for inclusion in the Work | |
| 133 | + by You to the Licensor shall be under the terms and conditions of | |
| 134 | + this License, without any additional terms or conditions. | |
| 135 | + Notwithstanding the above, nothing herein shall supersede or modify | |
| 136 | + the terms of any separate license agreement you may have executed | |
| 137 | + with Licensor regarding such Contributions. | |
| 138 | + | |
| 139 | + 6. Trademarks. This License does not grant permission to use the trade | |
| 140 | + names, trademarks, service marks, or product names of the Licensor, | |
| 141 | + except as required for reasonable and customary use in describing the | |
| 142 | + origin of the Work and reproducing the content of the NOTICE file. | |
| 143 | + | |
| 144 | + 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 145 | + agreed to in writing, Licensor provides the Work (and each | |
| 146 | + Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 147 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 148 | + implied, including, without limitation, any warranties or conditions | |
| 149 | + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 150 | + PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 151 | + appropriateness of using or redistributing the Work and assume any | |
| 152 | + risks associated with Your exercise of permissions under this License. | |
| 153 | + | |
| 154 | + 8. Limitation of Liability. In no event and under no legal theory, | |
| 155 | + whether in tort (including negligence), contract, or otherwise, | |
| 156 | + unless required by applicable law (such as deliberate and grossly | |
| 157 | + negligent acts) or agreed to in writing, shall any Contributor be | |
| 158 | + liable to You for damages, including any direct, indirect, special, | |
| 159 | + incidental, or consequential damages of any character arising as a | |
| 160 | + result of this License or out of the use or inability to use the | |
| 161 | + Work (including but not limited to damages for loss of goodwill, | |
| 162 | + work stoppage, computer failure or malfunction, or any and all | |
| 163 | + other commercial damages or losses), even if such Contributor | |
| 164 | + has been advised of the possibility of such damages. | |
| 165 | + | |
| 166 | + 9. Accepting Warranty or Additional Liability. While redistributing | |
| 167 | + the Work or Derivative Works thereof, You may choose to offer, | |
| 168 | + and charge a fee for, acceptance of support, warranty, indemnity, | |
| 169 | + or other liability obligations and/or rights consistent with this | |
| 170 | + License. However, in accepting such obligations, You may act only | |
| 171 | + on Your own behalf and on Your sole responsibility, not on behalf | |
| 172 | + of any other Contributor, and only if You agree to indemnify, | |
| 173 | + defend, and hold each Contributor harmless for any liability | |
| 174 | + incurred by, or claims asserted against, such Contributor by reason | |
| 175 | + of your accepting any such warranty or additional liability. | |
| 176 | + | |
| 177 | + END OF TERMS AND CONDITIONS | |
| 178 | + | |
| 179 | + APPENDIX: How to apply the Apache License to your work. | |
| 180 | + | |
| 181 | + To apply the Apache License to your work, attach the following | |
| 182 | + boilerplate notice, with the fields enclosed by brackets "[]" | |
| 183 | + replaced with your own identifying information. (Don't include | |
| 184 | + the brackets!) The text should be enclosed in the appropriate | |
| 185 | + comment syntax for the file format. We also recommend that a | |
| 186 | + file or class name and description of purpose be included on the | |
| 187 | + same "printed page" as the copyright notice for easier | |
| 188 | + identification within third-party archives. | |
| 189 | + | |
| 190 | + Copyright [yyyy] [name of copyright owner] | |
| 191 | + | |
| 192 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
| 193 | + you may not use this file except in compliance with the License. | |
| 194 | + You may obtain a copy of the License at | |
| 195 | + | |
| 196 | + http://www.apache.org/licenses/LICENSE-2.0 | |
| 197 | + | |
| 198 | + Unless required by applicable law or agreed to in writing, software | |
| 199 | + distributed under the License is distributed on an "AS IS" BASIS, | |
| 200 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 201 | + See the License for the specific language governing permissions and | |
| 202 | + limitations under the License. | |
| 0 | 203 | \ No newline at end of file | ... | ... |
pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!-- | |
| 3 | + ~ Licensed to the Apache Software Foundation (ASF) under one or more | |
| 4 | + ~ contributor license agreements. See the NOTICE file distributed with | |
| 5 | + ~ this work for additional information regarding copyright ownership. | |
| 6 | + ~ The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 7 | + ~ (the "License"); you may not use this file except in compliance with | |
| 8 | + ~ the License. You may obtain a copy of the License at | |
| 9 | + ~ | |
| 10 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | |
| 11 | + ~ | |
| 12 | + ~ Unless required by applicable law or agreed to in writing, software | |
| 13 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | |
| 14 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 15 | + ~ See the License for the specific language governing permissions and | |
| 16 | + ~ limitations under the License. | |
| 17 | + --> | |
| 18 | + | |
| 19 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| 20 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| 21 | + <modelVersion>4.0.0</modelVersion> | |
| 22 | + | |
| 23 | + <groupId>org.apache.rocketmq</groupId> | |
| 24 | + <artifactId>spring-boot-starter-rocketmq</artifactId> | |
| 25 | + <version>1.0.0-SNAPSHOT</version> | |
| 26 | + | |
| 27 | + <name>Spring Boot Rocket Starter</name> | |
| 28 | + <description>Starter for messaging using Apache RocketMQ</description> | |
| 29 | + <url>https://github.com/apache/rocketmq-externals/tree/master/spring-boot-starter-rocketmq</url> | |
| 30 | + | |
| 31 | + <organization> | |
| 32 | + <name>Apache Software Foundation</name> | |
| 33 | + <url>http://www.apache.org</url> | |
| 34 | + </organization> | |
| 35 | + | |
| 36 | + <licenses> | |
| 37 | + <license> | |
| 38 | + <name>Apache License, Version 2.0</name> | |
| 39 | + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
| 40 | + <distribution>repo</distribution> | |
| 41 | + <comments>A business-friendly OSS license</comments> | |
| 42 | + </license> | |
| 43 | + </licenses> | |
| 44 | + | |
| 45 | + <properties> | |
| 46 | + <spring.boot.version>1.5.9.RELEASE</spring.boot.version> | |
| 47 | + <rocketmq-version>4.2.0</rocketmq-version> | |
| 48 | + <java.version>1.8</java.version> | |
| 49 | + | |
| 50 | + <resource.delimiter>@</resource.delimiter> <!-- delimiter that doesn't clash with Spring ${} placeholders --> | |
| 51 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| 52 | + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |
| 53 | + <maven.compiler.source>${java.version}</maven.compiler.source> | |
| 54 | + <maven.compiler.target>${java.version}</maven.compiler.target> | |
| 55 | + <additionalparam>-Xdoclint:none</additionalparam> | |
| 56 | + </properties> | |
| 57 | + <dependencies> | |
| 58 | + <dependency> | |
| 59 | + <groupId>org.springframework.boot</groupId> | |
| 60 | + <artifactId>spring-boot-starter</artifactId> | |
| 61 | + </dependency> | |
| 62 | + <dependency> | |
| 63 | + <groupId>org.apache.rocketmq</groupId> | |
| 64 | + <artifactId>rocketmq-client</artifactId> | |
| 65 | + <version>${rocketmq-version}</version> | |
| 66 | + <exclusions> | |
| 67 | + <exclusion> | |
| 68 | + <groupId>org.slf4j</groupId> | |
| 69 | + <artifactId>slf4j-api</artifactId> | |
| 70 | + </exclusion> | |
| 71 | + </exclusions> | |
| 72 | + </dependency> | |
| 73 | + <dependency> | |
| 74 | + <groupId>org.springframework</groupId> | |
| 75 | + <artifactId>spring-messaging</artifactId> | |
| 76 | + </dependency> | |
| 77 | + <dependency> | |
| 78 | + <groupId>com.fasterxml.jackson.core</groupId> | |
| 79 | + <artifactId>jackson-databind</artifactId> | |
| 80 | + </dependency> | |
| 81 | + | |
| 82 | + <dependency> | |
| 83 | + <groupId>org.springframework.boot</groupId> | |
| 84 | + <artifactId>spring-boot-configuration-processor</artifactId> | |
| 85 | + <optional>true</optional> | |
| 86 | + </dependency> | |
| 87 | + <dependency> | |
| 88 | + <groupId>org.projectlombok</groupId> | |
| 89 | + <artifactId>lombok</artifactId> | |
| 90 | + <scope>provided</scope> | |
| 91 | + </dependency> | |
| 92 | + | |
| 93 | + | |
| 94 | + <dependency> | |
| 95 | + <groupId>org.springframework.boot</groupId> | |
| 96 | + <artifactId>spring-boot-starter-test</artifactId> | |
| 97 | + <scope>test</scope> | |
| 98 | + </dependency> | |
| 99 | + </dependencies> | |
| 100 | + | |
| 101 | + <dependencyManagement> | |
| 102 | + <dependencies> | |
| 103 | + <dependency> | |
| 104 | + <groupId>org.springframework.boot</groupId> | |
| 105 | + <artifactId>spring-boot-starter-parent</artifactId> | |
| 106 | + <version>${spring.boot.version}</version> | |
| 107 | + <type>pom</type> | |
| 108 | + <scope>import</scope> | |
| 109 | + </dependency> | |
| 110 | + </dependencies> | |
| 111 | + </dependencyManagement> | |
| 112 | + | |
| 113 | + <build> | |
| 114 | + <plugins> | |
| 115 | + <plugin> | |
| 116 | + <groupId>org.apache.maven.plugins</groupId> | |
| 117 | + <artifactId>maven-release-plugin</artifactId> | |
| 118 | + <version>2.5.2</version> | |
| 119 | + <configuration> | |
| 120 | + <arguments>-Dgpg.passphrase=${gpg.passphrase}</arguments> | |
| 121 | + </configuration> | |
| 122 | + </plugin> | |
| 123 | + </plugins> | |
| 124 | + </build> | |
| 125 | + | |
| 126 | + <developers> | |
| 127 | + <developer> | |
| 128 | + <name>angus.aqlu</name> | |
| 129 | + <email>angus.aqlu@gmail.com</email> | |
| 130 | + <organization>Jiangsu QianMi Network Technology Co., Ltd.</organization> | |
| 131 | + <organizationUrl>http://www.qianmi.com</organizationUrl> | |
| 132 | + </developer> | |
| 133 | + </developers> | |
| 134 | + | |
| 135 | + <profiles> | |
| 136 | + <profile> | |
| 137 | + <id>release-sign-artifacts</id> | |
| 138 | + <activation> | |
| 139 | + <property> | |
| 140 | + <name>performRelease</name> | |
| 141 | + <value>true</value> | |
| 142 | + </property> | |
| 143 | + </activation> | |
| 144 | + <build> | |
| 145 | + <plugins> | |
| 146 | + <plugin> | |
| 147 | + <groupId>org.apache.maven.plugins</groupId> | |
| 148 | + <artifactId>maven-gpg-plugin</artifactId> | |
| 149 | + <version>1.4</version> | |
| 150 | + <configuration> | |
| 151 | + <passphrase>${gpg.passphrase}</passphrase> | |
| 152 | + </configuration> | |
| 153 | + <executions> | |
| 154 | + <execution> | |
| 155 | + <id>sign-artifacts</id> | |
| 156 | + <phase>verify</phase> | |
| 157 | + <goals> | |
| 158 | + <goal>sign</goal> | |
| 159 | + </goals> | |
| 160 | + </execution> | |
| 161 | + </executions> | |
| 162 | + </plugin> | |
| 163 | + </plugins> | |
| 164 | + </build> | |
| 165 | + </profile> | |
| 166 | + </profiles> | |
| 167 | +</project> | |
| 0 | 168 | \ No newline at end of file | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfiguration.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter; | |
| 19 | + | |
| 20 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
| 21 | +import org.apache.rocketmq.spring.starter.annotation.RocketMQMessageListener; | |
| 22 | +import org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainer; | |
| 23 | +import org.apache.rocketmq.spring.starter.core.RocketMQListener; | |
| 24 | +import org.apache.rocketmq.spring.starter.core.RocketMQTemplate; | |
| 25 | +import java.util.Map; | |
| 26 | +import java.util.Objects; | |
| 27 | +import java.util.concurrent.atomic.AtomicLong; | |
| 28 | +import javax.annotation.Resource; | |
| 29 | +import lombok.extern.slf4j.Slf4j; | |
| 30 | +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; | |
| 31 | +import org.apache.rocketmq.client.impl.MQClientAPIImpl; | |
| 32 | +import org.apache.rocketmq.client.producer.DefaultMQProducer; | |
| 33 | +import org.springframework.aop.support.AopUtils; | |
| 34 | +import org.springframework.beans.BeansException; | |
| 35 | +import org.springframework.beans.factory.InitializingBean; | |
| 36 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 37 | +import org.springframework.beans.factory.annotation.Qualifier; | |
| 38 | +import org.springframework.beans.factory.support.BeanDefinitionBuilder; | |
| 39 | +import org.springframework.beans.factory.support.DefaultListableBeanFactory; | |
| 40 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | |
| 41 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | |
| 42 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |
| 43 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 44 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 45 | +import org.springframework.context.ApplicationContext; | |
| 46 | +import org.springframework.context.ApplicationContextAware; | |
| 47 | +import org.springframework.context.ConfigurableApplicationContext; | |
| 48 | +import org.springframework.context.annotation.Bean; | |
| 49 | +import org.springframework.context.annotation.Configuration; | |
| 50 | +import org.springframework.core.annotation.Order; | |
| 51 | +import org.springframework.core.env.StandardEnvironment; | |
| 52 | +import org.springframework.util.Assert; | |
| 53 | + | |
| 54 | +import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.*; | |
| 55 | + | |
| 56 | +@Configuration | |
| 57 | +@EnableConfigurationProperties(RocketMQProperties.class) | |
| 58 | +@ConditionalOnClass(MQClientAPIImpl.class) | |
| 59 | +@Order | |
| 60 | +@Slf4j | |
| 61 | +public class RocketMQAutoConfiguration { | |
| 62 | + | |
| 63 | + @Bean | |
| 64 | + @ConditionalOnClass(DefaultMQProducer.class) | |
| 65 | + @ConditionalOnMissingBean(DefaultMQProducer.class) | |
| 66 | + @ConditionalOnProperty(prefix = "spring.rocketmq", value = {"nameServer", "producer.group"}) | |
| 67 | + public DefaultMQProducer mqProducer(RocketMQProperties rocketMQProperties) { | |
| 68 | + | |
| 69 | + RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer(); | |
| 70 | + String groupName = producerConfig.getGroup(); | |
| 71 | + Assert.hasText(groupName, "[spring.rocketmq.producer.group] must not be null"); | |
| 72 | + | |
| 73 | + DefaultMQProducer producer = new DefaultMQProducer(producerConfig.getGroup()); | |
| 74 | + producer.setNamesrvAddr(rocketMQProperties.getNameServer()); | |
| 75 | + producer.setSendMsgTimeout(producerConfig.getSendMsgTimeout()); | |
| 76 | + producer.setRetryTimesWhenSendFailed(producerConfig.getRetryTimesWhenSendFailed()); | |
| 77 | + producer.setRetryTimesWhenSendAsyncFailed(producerConfig.getRetryTimesWhenSendAsyncFailed()); | |
| 78 | + producer.setMaxMessageSize(producerConfig.getMaxMessageSize()); | |
| 79 | + producer.setCompressMsgBodyOverHowmuch(producerConfig.getCompressMsgBodyOverHowmuch()); | |
| 80 | + producer.setRetryAnotherBrokerWhenNotStoreOK(producerConfig.isRetryAnotherBrokerWhenNotStoreOk()); | |
| 81 | + | |
| 82 | + return producer; | |
| 83 | + } | |
| 84 | + | |
| 85 | + @Bean | |
| 86 | + @ConditionalOnClass(ObjectMapper.class) | |
| 87 | + @ConditionalOnMissingBean(name = "rocketMQMessageObjectMapper") | |
| 88 | + public ObjectMapper rocketMQMessageObjectMapper() { | |
| 89 | + return new ObjectMapper(); | |
| 90 | + } | |
| 91 | + | |
| 92 | + @Bean(destroyMethod = "destroy") | |
| 93 | + @ConditionalOnBean(DefaultMQProducer.class) | |
| 94 | + @ConditionalOnMissingBean(name = "rocketMQTemplate") | |
| 95 | + public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer, | |
| 96 | + @Autowired(required = false) | |
| 97 | + @Qualifier("rocketMQMessageObjectMapper") | |
| 98 | + ObjectMapper objectMapper) { | |
| 99 | + RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); | |
| 100 | + rocketMQTemplate.setProducer(mqProducer); | |
| 101 | + if (Objects.nonNull(objectMapper)) { | |
| 102 | + rocketMQTemplate.setObjectMapper(objectMapper); | |
| 103 | + } | |
| 104 | + | |
| 105 | + return rocketMQTemplate; | |
| 106 | + } | |
| 107 | + | |
| 108 | + @Configuration | |
| 109 | + @ConditionalOnClass(DefaultMQPushConsumer.class) | |
| 110 | + @EnableConfigurationProperties(RocketMQProperties.class) | |
| 111 | + @ConditionalOnProperty(prefix = "spring.rocketmq", value = "nameServer") | |
| 112 | + @Order | |
| 113 | + public static class ListenerContainerConfiguration implements ApplicationContextAware, InitializingBean { | |
| 114 | + private ConfigurableApplicationContext applicationContext; | |
| 115 | + | |
| 116 | + private AtomicLong counter = new AtomicLong(0); | |
| 117 | + | |
| 118 | + @Resource | |
| 119 | + private StandardEnvironment environment; | |
| 120 | + | |
| 121 | + @Resource | |
| 122 | + private RocketMQProperties rocketMQProperties; | |
| 123 | + | |
| 124 | + private ObjectMapper objectMapper; | |
| 125 | + | |
| 126 | + public ListenerContainerConfiguration() { | |
| 127 | + } | |
| 128 | + | |
| 129 | + @Autowired(required = false) | |
| 130 | + public ListenerContainerConfiguration( | |
| 131 | + @Qualifier("rocketMQMessageObjectMapper") ObjectMapper objectMapper) { | |
| 132 | + this.objectMapper = objectMapper; | |
| 133 | + } | |
| 134 | + | |
| 135 | + @Override | |
| 136 | + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { | |
| 137 | + this.applicationContext = (ConfigurableApplicationContext) applicationContext; | |
| 138 | + } | |
| 139 | + | |
| 140 | + @Override | |
| 141 | + public void afterPropertiesSet() { | |
| 142 | + Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(RocketMQMessageListener.class); | |
| 143 | + | |
| 144 | + if (Objects.nonNull(beans)) { | |
| 145 | + beans.forEach(this::registerContainer); | |
| 146 | + } | |
| 147 | + } | |
| 148 | + | |
| 149 | + private void registerContainer(String beanName, Object bean) { | |
| 150 | + Class<?> clazz = AopUtils.getTargetClass(bean); | |
| 151 | + | |
| 152 | + if (!RocketMQListener.class.isAssignableFrom(bean.getClass())) { | |
| 153 | + throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName()); | |
| 154 | + } | |
| 155 | + | |
| 156 | + RocketMQListener rocketMQListener = (RocketMQListener) bean; | |
| 157 | + RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class); | |
| 158 | + BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultRocketMQListenerContainer.class); | |
| 159 | + beanBuilder.addPropertyValue(PROP_NAMESERVER, rocketMQProperties.getNameServer()); | |
| 160 | + beanBuilder.addPropertyValue(PROP_TOPIC, environment.resolvePlaceholders(annotation.topic())); | |
| 161 | + | |
| 162 | + beanBuilder.addPropertyValue(PROP_CONSUMER_GROUP, environment.resolvePlaceholders(annotation.consumerGroup())); | |
| 163 | + beanBuilder.addPropertyValue(PROP_CONSUME_MODE, annotation.consumeMode()); | |
| 164 | + beanBuilder.addPropertyValue(PROP_CONSUME_THREAD_MAX, annotation.consumeThreadMax()); | |
| 165 | + beanBuilder.addPropertyValue(PROP_MESSAGE_MODEL, annotation.messageModel()); | |
| 166 | + beanBuilder.addPropertyValue(PROP_SELECTOR_EXPRESS, environment.resolvePlaceholders(annotation.selectorExpress())); | |
| 167 | + beanBuilder.addPropertyValue(PROP_SELECTOR_TYPE, annotation.selectorType()); | |
| 168 | + beanBuilder.addPropertyValue(PROP_ROCKETMQ_LISTENER, rocketMQListener); | |
| 169 | + if (Objects.nonNull(objectMapper)) { | |
| 170 | + beanBuilder.addPropertyValue(PROP_OBJECT_MAPPER, objectMapper); | |
| 171 | + } | |
| 172 | + beanBuilder.setDestroyMethodName(METHOD_DESTROY); | |
| 173 | + | |
| 174 | + String containerBeanName = String.format("%s_%s", DefaultRocketMQListenerContainer.class.getName(), counter.incrementAndGet()); | |
| 175 | + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory(); | |
| 176 | + beanFactory.registerBeanDefinition(containerBeanName, beanBuilder.getBeanDefinition()); | |
| 177 | + | |
| 178 | + DefaultRocketMQListenerContainer container = beanFactory.getBean(containerBeanName, DefaultRocketMQListenerContainer.class); | |
| 179 | + | |
| 180 | + if (!container.isStarted()) { | |
| 181 | + try { | |
| 182 | + container.start(); | |
| 183 | + } catch (Exception e) { | |
| 184 | + log.error("started container failed. {}", container, e); | |
| 185 | + throw new RuntimeException(e); | |
| 186 | + } | |
| 187 | + } | |
| 188 | + | |
| 189 | + log.info("register rocketMQ listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName); | |
| 190 | + } | |
| 191 | + } | |
| 192 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/RocketMQProperties.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter; | |
| 19 | + | |
| 20 | +import lombok.Data; | |
| 21 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 22 | + | |
| 23 | +@SuppressWarnings("WeakerAccess") | |
| 24 | +@ConfigurationProperties(prefix = "spring.rocketmq") | |
| 25 | +@Data | |
| 26 | +public class RocketMQProperties { | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * name server for rocketMQ, formats: `host:port;host:port` | |
| 30 | + */ | |
| 31 | + private String nameServer; | |
| 32 | + | |
| 33 | + private Producer producer; | |
| 34 | + | |
| 35 | + @Data | |
| 36 | + public static class Producer { | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * name of producer | |
| 40 | + */ | |
| 41 | + private String group; | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * millis of send message timeout | |
| 45 | + */ | |
| 46 | + private int sendMsgTimeout = 3000; | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * Compress message body threshold, namely, message body larger than 4k will be compressed on default. | |
| 50 | + */ | |
| 51 | + private int compressMsgBodyOverHowmuch = 1024 * 4; | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * <p> Maximum number of retry to perform internally before claiming sending failure in synchronous mode. </p> | |
| 55 | + * This may potentially cause message duplication which is up to application developers to resolve. | |
| 56 | + */ | |
| 57 | + private int retryTimesWhenSendFailed = 2; | |
| 58 | + | |
| 59 | + /** | |
| 60 | + * <p> Maximum number of retry to perform internally before claiming sending failure in asynchronous mode. </p> | |
| 61 | + * This may potentially cause message duplication which is up to application developers to resolve. | |
| 62 | + */ | |
| 63 | + private int retryTimesWhenSendAsyncFailed = 2; | |
| 64 | + | |
| 65 | + /** | |
| 66 | + * Indicate whether to retry another broker on sending failure internally. | |
| 67 | + */ | |
| 68 | + private boolean retryAnotherBrokerWhenNotStoreOk = false; | |
| 69 | + | |
| 70 | + /** | |
| 71 | + * Maximum allowed message size in bytes. | |
| 72 | + */ | |
| 73 | + private int maxMessageSize = 1024 * 1024 * 4; // 4M | |
| 74 | + | |
| 75 | + } | |
| 76 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.annotation; | |
| 19 | + | |
| 20 | +import org.apache.rocketmq.common.filter.ExpressionType; | |
| 21 | +import org.apache.rocketmq.spring.starter.enums.ConsumeMode; | |
| 22 | +import org.apache.rocketmq.spring.starter.enums.SelectorType; | |
| 23 | +import java.lang.annotation.Documented; | |
| 24 | +import java.lang.annotation.ElementType; | |
| 25 | +import java.lang.annotation.Retention; | |
| 26 | +import java.lang.annotation.RetentionPolicy; | |
| 27 | +import java.lang.annotation.Target; | |
| 28 | +import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; | |
| 29 | + | |
| 30 | +@Target(ElementType.TYPE) | |
| 31 | +@Retention(RetentionPolicy.RUNTIME) | |
| 32 | +@Documented | |
| 33 | +public @interface RocketMQMessageListener { | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve | |
| 37 | + * load balance. It's required and needs to be globally unique. | |
| 38 | + * </p> | |
| 39 | + * <p> | |
| 40 | + * See <a href="http://rocketmq.apache.org/docs/core-concept/">here</a> for further discussion. | |
| 41 | + */ | |
| 42 | + String consumerGroup(); | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * Topic name | |
| 46 | + */ | |
| 47 | + String topic(); | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * Control how to selector message | |
| 51 | + * | |
| 52 | + * @see ExpressionType | |
| 53 | + */ | |
| 54 | + SelectorType selectorType() default SelectorType.TAG; | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * Control which message can be select. Grammar please see {@link ExpressionType#TAG} and {@link ExpressionType#SQL92} | |
| 58 | + */ | |
| 59 | + String selectorExpress() default "*"; | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * Control consume mode, you can choice receive message concurrently or orderly | |
| 63 | + */ | |
| 64 | + ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice. | |
| 68 | + */ | |
| 69 | + MessageModel messageModel() default MessageModel.CLUSTERING; | |
| 70 | + | |
| 71 | + /** | |
| 72 | + * Max consumer thread number | |
| 73 | + */ | |
| 74 | + int consumeThreadMax() default 64; | |
| 75 | + | |
| 76 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
| 21 | +import org.apache.rocketmq.spring.starter.enums.ConsumeMode; | |
| 22 | +import org.apache.rocketmq.spring.starter.enums.SelectorType; | |
| 23 | +import java.lang.reflect.ParameterizedType; | |
| 24 | +import java.lang.reflect.Type; | |
| 25 | +import java.nio.charset.Charset; | |
| 26 | +import java.util.List; | |
| 27 | +import java.util.Objects; | |
| 28 | +import lombok.Getter; | |
| 29 | +import lombok.Setter; | |
| 30 | +import lombok.extern.slf4j.Slf4j; | |
| 31 | +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; | |
| 32 | +import org.apache.rocketmq.client.consumer.MessageSelector; | |
| 33 | +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; | |
| 34 | +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; | |
| 35 | +import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; | |
| 36 | +import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus; | |
| 37 | +import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; | |
| 38 | +import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; | |
| 39 | +import org.apache.rocketmq.client.exception.MQClientException; | |
| 40 | +import org.apache.rocketmq.common.message.MessageExt; | |
| 41 | +import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; | |
| 42 | +import org.springframework.beans.factory.InitializingBean; | |
| 43 | +import org.springframework.util.Assert; | |
| 44 | + | |
| 45 | +@SuppressWarnings("WeakerAccess") | |
| 46 | +@Slf4j | |
| 47 | +public class DefaultRocketMQListenerContainer implements InitializingBean, RocketMQListenerContainer { | |
| 48 | + | |
| 49 | + @Setter | |
| 50 | + @Getter | |
| 51 | + private long suspendCurrentQueueTimeMillis = 1000; | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * Message consume retry strategy<br> -1,no retry,put into DLQ directly<br> 0,broker control retry frequency<br> | |
| 55 | + * >0,client control retry frequency | |
| 56 | + */ | |
| 57 | + @Setter | |
| 58 | + @Getter | |
| 59 | + private int delayLevelWhenNextConsume = 0; | |
| 60 | + | |
| 61 | + @Setter | |
| 62 | + @Getter | |
| 63 | + private String consumerGroup; | |
| 64 | + | |
| 65 | + @Setter | |
| 66 | + @Getter | |
| 67 | + private String nameServer; | |
| 68 | + | |
| 69 | + @Setter | |
| 70 | + @Getter | |
| 71 | + private String topic; | |
| 72 | + | |
| 73 | + @Setter | |
| 74 | + @Getter | |
| 75 | + private ConsumeMode consumeMode = ConsumeMode.CONCURRENTLY; | |
| 76 | + | |
| 77 | + @Setter | |
| 78 | + @Getter | |
| 79 | + private SelectorType selectorType = SelectorType.TAG; | |
| 80 | + | |
| 81 | + @Setter | |
| 82 | + @Getter | |
| 83 | + private String selectorExpress = "*"; | |
| 84 | + | |
| 85 | + @Setter | |
| 86 | + @Getter | |
| 87 | + private MessageModel messageModel = MessageModel.CLUSTERING; | |
| 88 | + | |
| 89 | + @Setter | |
| 90 | + @Getter | |
| 91 | + private int consumeThreadMax = 64; | |
| 92 | + | |
| 93 | + @Getter | |
| 94 | + @Setter | |
| 95 | + private String charset = "UTF-8"; | |
| 96 | + | |
| 97 | + @Setter | |
| 98 | + @Getter | |
| 99 | + private ObjectMapper objectMapper = new ObjectMapper(); | |
| 100 | + | |
| 101 | + @Setter | |
| 102 | + @Getter | |
| 103 | + private boolean started; | |
| 104 | + | |
| 105 | + @Setter | |
| 106 | + private RocketMQListener rocketMQListener; | |
| 107 | + | |
| 108 | + private DefaultMQPushConsumer consumer; | |
| 109 | + | |
| 110 | + private Class messageType; | |
| 111 | + | |
| 112 | + public void setupMessageListener(RocketMQListener rocketMQListener) { | |
| 113 | + this.rocketMQListener = rocketMQListener; | |
| 114 | + } | |
| 115 | + | |
| 116 | + @Override | |
| 117 | + public void destroy() { | |
| 118 | + this.setStarted(false); | |
| 119 | + if (Objects.nonNull(consumer)) { | |
| 120 | + consumer.shutdown(); | |
| 121 | + } | |
| 122 | + log.info("container destroyed, {}", this.toString()); | |
| 123 | + } | |
| 124 | + | |
| 125 | + public synchronized void start() throws MQClientException { | |
| 126 | + | |
| 127 | + if (this.isStarted()) { | |
| 128 | + throw new IllegalStateException("container already started. " + this.toString()); | |
| 129 | + } | |
| 130 | + | |
| 131 | + initRocketMQPushConsumer(); | |
| 132 | + | |
| 133 | + // parse message type | |
| 134 | + this.messageType = getMessageType(); | |
| 135 | + log.debug("msgType: {}", messageType.getName()); | |
| 136 | + | |
| 137 | + consumer.start(); | |
| 138 | + this.setStarted(true); | |
| 139 | + | |
| 140 | + log.info("started container: {}", this.toString()); | |
| 141 | + } | |
| 142 | + | |
| 143 | + public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { | |
| 144 | + | |
| 145 | + @SuppressWarnings("unchecked") | |
| 146 | + public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { | |
| 147 | + for (MessageExt messageExt : msgs) { | |
| 148 | + log.debug("received msg: {}", messageExt); | |
| 149 | + try { | |
| 150 | + long now = System.currentTimeMillis(); | |
| 151 | + rocketMQListener.onMessage(doConvertMessage(messageExt)); | |
| 152 | + long costTime = System.currentTimeMillis() - now; | |
| 153 | + log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime); | |
| 154 | + } catch (Exception e) { | |
| 155 | + log.warn("consume message failed. messageExt:{}", messageExt, e); | |
| 156 | + context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume); | |
| 157 | + return ConsumeConcurrentlyStatus.RECONSUME_LATER; | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; | |
| 162 | + } | |
| 163 | + } | |
| 164 | + | |
| 165 | + public class DefaultMessageListenerOrderly implements MessageListenerOrderly { | |
| 166 | + | |
| 167 | + @SuppressWarnings("unchecked") | |
| 168 | + public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) { | |
| 169 | + for (MessageExt messageExt : msgs) { | |
| 170 | + log.debug("received msg: {}", messageExt); | |
| 171 | + try { | |
| 172 | + long now = System.currentTimeMillis(); | |
| 173 | + rocketMQListener.onMessage(doConvertMessage(messageExt)); | |
| 174 | + long costTime = System.currentTimeMillis() - now; | |
| 175 | + log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime); | |
| 176 | + } catch (Exception e) { | |
| 177 | + log.warn("consume message failed. messageExt:{}", messageExt, e); | |
| 178 | + context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis); | |
| 179 | + return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; | |
| 180 | + } | |
| 181 | + } | |
| 182 | + | |
| 183 | + return ConsumeOrderlyStatus.SUCCESS; | |
| 184 | + } | |
| 185 | + } | |
| 186 | + | |
| 187 | + @Override | |
| 188 | + public void afterPropertiesSet() throws Exception { | |
| 189 | + start(); | |
| 190 | + } | |
| 191 | + | |
| 192 | + @Override | |
| 193 | + public String toString() { | |
| 194 | + return "DefaultRocketMQListenerContainer{" + | |
| 195 | + "consumerGroup='" + consumerGroup + '\'' + | |
| 196 | + ", nameServer='" + nameServer + '\'' + | |
| 197 | + ", topic='" + topic + '\'' + | |
| 198 | + ", consumeMode=" + consumeMode + | |
| 199 | + ", selectorType=" + selectorType + | |
| 200 | + ", selectorExpress='" + selectorExpress + '\'' + | |
| 201 | + ", messageModel=" + messageModel + | |
| 202 | + '}'; | |
| 203 | + } | |
| 204 | + | |
| 205 | + @SuppressWarnings("unchecked") | |
| 206 | + private Object doConvertMessage(MessageExt messageExt) { | |
| 207 | + if (Objects.equals(messageType, MessageExt.class)) { | |
| 208 | + return messageExt; | |
| 209 | + } else { | |
| 210 | + String str = new String(messageExt.getBody(), Charset.forName(charset)); | |
| 211 | + if (Objects.equals(messageType, String.class)) { | |
| 212 | + return str; | |
| 213 | + } else { | |
| 214 | + // if msgType not string, use objectMapper change it. | |
| 215 | + try { | |
| 216 | + return objectMapper.readValue(str, messageType); | |
| 217 | + } catch (Exception e) { | |
| 218 | + log.info("convert failed. str:{}, msgType:{}", str, messageType); | |
| 219 | + throw new RuntimeException("cannot convert message to " + messageType, e); | |
| 220 | + } | |
| 221 | + } | |
| 222 | + } | |
| 223 | + } | |
| 224 | + | |
| 225 | + private Class getMessageType() { | |
| 226 | + Type[] interfaces = rocketMQListener.getClass().getGenericInterfaces(); | |
| 227 | + if (Objects.nonNull(interfaces)) { | |
| 228 | + for (Type type : interfaces) { | |
| 229 | + if (type instanceof ParameterizedType) { | |
| 230 | + ParameterizedType parameterizedType = (ParameterizedType) type; | |
| 231 | + if (Objects.equals(parameterizedType.getRawType(), RocketMQListener.class)) { | |
| 232 | + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); | |
| 233 | + if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { | |
| 234 | + return (Class) actualTypeArguments[0]; | |
| 235 | + } else { | |
| 236 | + return Object.class; | |
| 237 | + } | |
| 238 | + } | |
| 239 | + } | |
| 240 | + } | |
| 241 | + | |
| 242 | + return Object.class; | |
| 243 | + } else { | |
| 244 | + return Object.class; | |
| 245 | + } | |
| 246 | + } | |
| 247 | + | |
| 248 | + private void initRocketMQPushConsumer() throws MQClientException { | |
| 249 | + | |
| 250 | + Assert.notNull(rocketMQListener, "Property 'rocketMQListener' is required"); | |
| 251 | + Assert.notNull(consumerGroup, "Property 'consumerGroup' is required"); | |
| 252 | + Assert.notNull(nameServer, "Property 'nameServer' is required"); | |
| 253 | + Assert.notNull(topic, "Property 'topic' is required"); | |
| 254 | + | |
| 255 | + consumer = new DefaultMQPushConsumer(consumerGroup); | |
| 256 | + consumer.setNamesrvAddr(nameServer); | |
| 257 | + consumer.setConsumeThreadMax(consumeThreadMax); | |
| 258 | + if (consumeThreadMax < consumer.getConsumeThreadMin()) { | |
| 259 | + consumer.setConsumeThreadMin(consumeThreadMax); | |
| 260 | + } | |
| 261 | + | |
| 262 | + consumer.setMessageModel(messageModel); | |
| 263 | + | |
| 264 | + switch (selectorType) { | |
| 265 | + case TAG: | |
| 266 | + consumer.subscribe(topic, selectorExpress); | |
| 267 | + break; | |
| 268 | + case SQL92: | |
| 269 | + consumer.subscribe(topic, MessageSelector.bySql(selectorExpress)); | |
| 270 | + break; | |
| 271 | + default: | |
| 272 | + throw new IllegalArgumentException("Property 'selectorType' was wrong."); | |
| 273 | + } | |
| 274 | + | |
| 275 | + switch (consumeMode) { | |
| 276 | + case ORDERLY: | |
| 277 | + consumer.setMessageListener(new DefaultMessageListenerOrderly()); | |
| 278 | + break; | |
| 279 | + case CONCURRENTLY: | |
| 280 | + consumer.setMessageListener(new DefaultMessageListenerConcurrently()); | |
| 281 | + break; | |
| 282 | + default: | |
| 283 | + throw new IllegalArgumentException("Property 'consumeMode' was wrong."); | |
| 284 | + } | |
| 285 | + | |
| 286 | + // provide an entryway to custom setting RocketMQ consumer | |
| 287 | + if (rocketMQListener instanceof RocketMQPushConsumerLifecycleListener) { | |
| 288 | + ((RocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer); | |
| 289 | + } | |
| 290 | + | |
| 291 | + } | |
| 292 | + | |
| 293 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * Constants Created by aqlu on 2017/11/16. | |
| 22 | + */ | |
| 23 | +public final class DefaultRocketMQListenerContainerConstants { | |
| 24 | + public static final String PROP_NAMESERVER = "nameServer"; | |
| 25 | + public static final String PROP_TOPIC = "topic"; | |
| 26 | + public static final String PROP_CONSUMER_GROUP = "consumerGroup"; | |
| 27 | + public static final String PROP_CONSUME_MODE = "consumeMode"; | |
| 28 | + public static final String PROP_CONSUME_THREAD_MAX = "consumeThreadMax"; | |
| 29 | + public static final String PROP_MESSAGE_MODEL = "messageModel"; | |
| 30 | + public static final String PROP_SELECTOR_EXPRESS = "selectorExpress"; | |
| 31 | + public static final String PROP_SELECTOR_TYPE = "selectorType"; | |
| 32 | + public static final String PROP_ROCKETMQ_LISTENER = "rocketMQListener"; | |
| 33 | + public static final String PROP_OBJECT_MAPPER = "objectMapper"; | |
| 34 | + public static final String METHOD_DESTROY = "destroy"; | |
| 35 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +public interface RocketMQConsumerLifecycleListener<T> { | |
| 21 | + void prepareStart(final T consumer); | |
| 22 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListener.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +public interface RocketMQListener<T> { | |
| 21 | + void onMessage(T message); | |
| 22 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListenerContainer.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +import org.springframework.beans.factory.DisposableBean; | |
| 21 | + | |
| 22 | +public interface RocketMQListenerContainer extends DisposableBean { | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * Setup the message listener to use. Throws an {@link IllegalArgumentException} if that message listener type is | |
| 26 | + * not supported. | |
| 27 | + */ | |
| 28 | + void setupMessageListener(RocketMQListener<?> messageListener); | |
| 29 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQPushConsumerLifecycleListener.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; | |
| 21 | + | |
| 22 | +public interface RocketMQPushConsumerLifecycleListener extends RocketMQConsumerLifecycleListener<DefaultMQPushConsumer> { | |
| 23 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQTemplate.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.core; | |
| 19 | + | |
| 20 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
| 21 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
| 22 | +import java.nio.charset.Charset; | |
| 23 | +import java.util.Map; | |
| 24 | +import java.util.Objects; | |
| 25 | +import lombok.Getter; | |
| 26 | +import lombok.Setter; | |
| 27 | +import lombok.extern.slf4j.Slf4j; | |
| 28 | +import org.apache.rocketmq.client.producer.DefaultMQProducer; | |
| 29 | +import org.apache.rocketmq.client.producer.MessageQueueSelector; | |
| 30 | +import org.apache.rocketmq.client.producer.SendCallback; | |
| 31 | +import org.apache.rocketmq.client.producer.SendResult; | |
| 32 | +import org.apache.rocketmq.client.producer.selector.SelectMessageQueueByHash; | |
| 33 | +import org.apache.rocketmq.common.message.MessageConst; | |
| 34 | +import org.springframework.beans.factory.DisposableBean; | |
| 35 | +import org.springframework.beans.factory.InitializingBean; | |
| 36 | +import org.springframework.messaging.Message; | |
| 37 | +import org.springframework.messaging.MessageHeaders; | |
| 38 | +import org.springframework.messaging.MessagingException; | |
| 39 | +import org.springframework.messaging.core.AbstractMessageSendingTemplate; | |
| 40 | +import org.springframework.messaging.core.MessagePostProcessor; | |
| 41 | +import org.springframework.messaging.support.MessageBuilder; | |
| 42 | +import org.springframework.util.Assert; | |
| 43 | +import org.springframework.util.MimeTypeUtils; | |
| 44 | +import org.springframework.util.StringUtils; | |
| 45 | + | |
| 46 | +@SuppressWarnings({"WeakerAccess", "unused"}) | |
| 47 | +@Slf4j | |
| 48 | +public class RocketMQTemplate extends AbstractMessageSendingTemplate<String> implements InitializingBean, DisposableBean { | |
| 49 | + | |
| 50 | + @Getter | |
| 51 | + @Setter | |
| 52 | + private DefaultMQProducer producer; | |
| 53 | + | |
| 54 | + @Setter | |
| 55 | + @Getter | |
| 56 | + private ObjectMapper objectMapper = new ObjectMapper(); | |
| 57 | + | |
| 58 | + @Getter | |
| 59 | + @Setter | |
| 60 | + private String charset = "UTF-8"; | |
| 61 | + | |
| 62 | + @Getter | |
| 63 | + @Setter | |
| 64 | + private MessageQueueSelector messageQueueSelector = new SelectMessageQueueByHash(); | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * <p> Send message in synchronous mode. This method returns only when the sending procedure totally completes. | |
| 68 | + * Reliable synchronous transmission is used in extensive scenes, such as important notification messages, SMS | |
| 69 | + * notification, SMS marketing system, etc.. </p> | |
| 70 | + * | |
| 71 | + * <strong>Warn:</strong> this method has internal retry-mechanism, that is, internal implementation will retry | |
| 72 | + * {@link DefaultMQProducer#getRetryTimesWhenSendFailed} times before claiming failure. As a result, multiple | |
| 73 | + * messages may potentially delivered to broker(s). It's up to the application developers to resolve potential | |
| 74 | + * duplication issue. | |
| 75 | + * | |
| 76 | + * @param destination formats: `topicName:tags` | |
| 77 | + * @param message {@link org.springframework.messaging.Message} | |
| 78 | + * @return {@link SendResult} | |
| 79 | + */ | |
| 80 | + public SendResult syncSend(String destination, Message<?> message) { | |
| 81 | + return syncSend(destination, message, producer.getSendMsgTimeout()); | |
| 82 | + } | |
| 83 | + | |
| 84 | + /** | |
| 85 | + * Same to {@link #syncSend(String, Message)} with send timeout specified in addition. | |
| 86 | + * | |
| 87 | + * @param destination formats: `topicName:tags` | |
| 88 | + * @param message {@link org.springframework.messaging.Message} | |
| 89 | + * @param timeout send timeout with millis | |
| 90 | + * @return {@link SendResult} | |
| 91 | + */ | |
| 92 | + public SendResult syncSend(String destination, Message<?> message, long timeout) { | |
| 93 | + if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { | |
| 94 | + log.info("syncSend failed. destination:{}, message is null ", destination); | |
| 95 | + throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); | |
| 96 | + } | |
| 97 | + | |
| 98 | + try { | |
| 99 | + long now = System.currentTimeMillis(); | |
| 100 | + org.apache.rocketmq.common.message.Message rocketMsg = convertToRocketMsg(destination, message); | |
| 101 | + SendResult sendResult = producer.send(rocketMsg, timeout); | |
| 102 | + long costTime = System.currentTimeMillis() - now; | |
| 103 | + log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId()); | |
| 104 | + return sendResult; | |
| 105 | + } catch (Exception e) { | |
| 106 | + log.info("syncSend failed. destination:{}, message:{} ", destination, message); | |
| 107 | + throw new MessagingException(e.getMessage(), e); | |
| 108 | + } | |
| 109 | + } | |
| 110 | + | |
| 111 | + /** | |
| 112 | + * Same to {@link #syncSend(String, Message)}. | |
| 113 | + * | |
| 114 | + * @param destination formats: `topicName:tags` | |
| 115 | + * @param payload the Object to use as payload | |
| 116 | + * @return {@link SendResult} | |
| 117 | + */ | |
| 118 | + public SendResult syncSend(String destination, Object payload) { | |
| 119 | + return syncSend(destination, payload, producer.getSendMsgTimeout()); | |
| 120 | + } | |
| 121 | + | |
| 122 | + /** | |
| 123 | + * Same to {@link #syncSend(String, Object)} with send timeout specified in addition. | |
| 124 | + * | |
| 125 | + * @param destination formats: `topicName:tags` | |
| 126 | + * @param payload the Object to use as payload | |
| 127 | + * @param timeout send timeout with millis | |
| 128 | + * @return {@link SendResult} | |
| 129 | + */ | |
| 130 | + public SendResult syncSend(String destination, Object payload, long timeout) { | |
| 131 | + Message<?> message = this.doConvert(payload, null, null); | |
| 132 | + return syncSend(destination, message, timeout); | |
| 133 | + } | |
| 134 | + | |
| 135 | + /** | |
| 136 | + * Same to {@link #syncSend(String, Message)} with send orderly with hashKey by specified. | |
| 137 | + * | |
| 138 | + * @param destination formats: `topicName:tags` | |
| 139 | + * @param message {@link org.springframework.messaging.Message} | |
| 140 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 141 | + * @return {@link SendResult} | |
| 142 | + */ | |
| 143 | + public SendResult syncSendOrderly(String destination, Message<?> message, String hashKey) { | |
| 144 | + return syncSendOrderly(destination, message, hashKey, producer.getSendMsgTimeout()); | |
| 145 | + } | |
| 146 | + | |
| 147 | + /** | |
| 148 | + * Same to {@link #syncSendOrderly(String, Message, String)} with send timeout specified in addition. | |
| 149 | + * | |
| 150 | + * @param destination formats: `topicName:tags` | |
| 151 | + * @param message {@link org.springframework.messaging.Message} | |
| 152 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 153 | + * @param timeout send timeout with millis | |
| 154 | + * @return {@link SendResult} | |
| 155 | + */ | |
| 156 | + public SendResult syncSendOrderly(String destination, Message<?> message, String hashKey, long timeout) { | |
| 157 | + if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { | |
| 158 | + log.info("syncSendOrderly failed. destination:{}, message is null ", destination); | |
| 159 | + throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); | |
| 160 | + } | |
| 161 | + | |
| 162 | + try { | |
| 163 | + long now = System.currentTimeMillis(); | |
| 164 | + org.apache.rocketmq.common.message.Message rocketMsg = convertToRocketMsg(destination, message); | |
| 165 | + SendResult sendResult = producer.send(rocketMsg, messageQueueSelector, hashKey, timeout); | |
| 166 | + long costTime = System.currentTimeMillis() - now; | |
| 167 | + log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId()); | |
| 168 | + return sendResult; | |
| 169 | + } catch (Exception e) { | |
| 170 | + log.info("syncSendOrderly failed. destination:{}, message:{} ", destination, message); | |
| 171 | + throw new MessagingException(e.getMessage(), e); | |
| 172 | + } | |
| 173 | + } | |
| 174 | + | |
| 175 | + /** | |
| 176 | + * Same to {@link #syncSend(String, Object)} with send orderly with hashKey by specified. | |
| 177 | + * | |
| 178 | + * @param destination formats: `topicName:tags` | |
| 179 | + * @param payload the Object to use as payload | |
| 180 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 181 | + * @return {@link SendResult} | |
| 182 | + */ | |
| 183 | + public SendResult syncSendOrderly(String destination, Object payload, String hashKey) { | |
| 184 | + return syncSendOrderly(destination, payload, hashKey, producer.getSendMsgTimeout()); | |
| 185 | + } | |
| 186 | + | |
| 187 | + /** | |
| 188 | + * Same to {@link #syncSendOrderly(String, Object, String)} with send timeout specified in addition. | |
| 189 | + * | |
| 190 | + * @param destination formats: `topicName:tags` | |
| 191 | + * @param payload the Object to use as payload | |
| 192 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 193 | + * @param timeout send timeout with millis | |
| 194 | + * @return {@link SendResult} | |
| 195 | + */ | |
| 196 | + public SendResult syncSendOrderly(String destination, Object payload, String hashKey, long timeout) { | |
| 197 | + Message<?> message = this.doConvert(payload, null, null); | |
| 198 | + return syncSendOrderly(destination, message, hashKey, producer.getSendMsgTimeout()); | |
| 199 | + } | |
| 200 | + | |
| 201 | + /** | |
| 202 | + * Same to {@link #asyncSend(String, Message, SendCallback)} with send timeout specified in addition. | |
| 203 | + * | |
| 204 | + * @param destination formats: `topicName:tags` | |
| 205 | + * @param message {@link org.springframework.messaging.Message} | |
| 206 | + * @param sendCallback {@link SendCallback} | |
| 207 | + * @param timeout send timeout with millis | |
| 208 | + */ | |
| 209 | + public void asyncSend(String destination, Message<?> message, SendCallback sendCallback, long timeout) { | |
| 210 | + if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { | |
| 211 | + log.info("asyncSend failed. destination:{}, message is null ", destination); | |
| 212 | + throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); | |
| 213 | + } | |
| 214 | + | |
| 215 | + try { | |
| 216 | + org.apache.rocketmq.common.message.Message rocketMsg = convertToRocketMsg(destination, message); | |
| 217 | + producer.send(rocketMsg, sendCallback, timeout); | |
| 218 | + } catch (Exception e) { | |
| 219 | + log.info("asyncSend failed. destination:{}, message:{} ", destination, message); | |
| 220 | + throw new MessagingException(e.getMessage(), e); | |
| 221 | + } | |
| 222 | + } | |
| 223 | + | |
| 224 | + /** | |
| 225 | + * <p> Send message to broker asynchronously. asynchronous transmission is generally used in response time sensitive | |
| 226 | + * business scenarios. </p> | |
| 227 | + * | |
| 228 | + * This method returns immediately. On sending completion, <code>sendCallback</code> will be executed. | |
| 229 | + * | |
| 230 | + * Similar to {@link #syncSend(String, Object)}, internal implementation would potentially retry up to {@link | |
| 231 | + * DefaultMQProducer#getRetryTimesWhenSendAsyncFailed} times before claiming sending failure, which may yield | |
| 232 | + * message duplication and application developers are the one to resolve this potential issue. | |
| 233 | + * | |
| 234 | + * @param destination formats: `topicName:tags` | |
| 235 | + * @param message {@link org.springframework.messaging.Message} | |
| 236 | + * @param sendCallback {@link SendCallback} | |
| 237 | + */ | |
| 238 | + public void asyncSend(String destination, Message<?> message, SendCallback sendCallback) { | |
| 239 | + asyncSend(destination, message, sendCallback, producer.getSendMsgTimeout()); | |
| 240 | + } | |
| 241 | + | |
| 242 | + /** | |
| 243 | + * Same to {@link #asyncSend(String, Object, SendCallback)} with send timeout specified in addition. | |
| 244 | + * | |
| 245 | + * @param destination formats: `topicName:tags` | |
| 246 | + * @param payload the Object to use as payload | |
| 247 | + * @param sendCallback {@link SendCallback} | |
| 248 | + * @param timeout send timeout with millis | |
| 249 | + */ | |
| 250 | + public void asyncSend(String destination, Object payload, SendCallback sendCallback, long timeout) { | |
| 251 | + Message<?> message = this.doConvert(payload, null, null); | |
| 252 | + asyncSend(destination, message, sendCallback, timeout); | |
| 253 | + } | |
| 254 | + | |
| 255 | + /** | |
| 256 | + * Same to {@link #asyncSend(String, Message, SendCallback)}. | |
| 257 | + * | |
| 258 | + * @param destination formats: `topicName:tags` | |
| 259 | + * @param payload the Object to use as payload | |
| 260 | + * @param sendCallback {@link SendCallback} | |
| 261 | + */ | |
| 262 | + public void asyncSend(String destination, Object payload, SendCallback sendCallback) { | |
| 263 | + asyncSend(destination, payload, sendCallback, producer.getSendMsgTimeout()); | |
| 264 | + } | |
| 265 | + | |
| 266 | + /** | |
| 267 | + * Same to {@link #asyncSendOrderly(String, Message, String, SendCallback)} with send timeout specified in | |
| 268 | + * addition. | |
| 269 | + * | |
| 270 | + * @param destination formats: `topicName:tags` | |
| 271 | + * @param message {@link org.springframework.messaging.Message} | |
| 272 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 273 | + * @param sendCallback {@link SendCallback} | |
| 274 | + * @param timeout send timeout with millis | |
| 275 | + */ | |
| 276 | + public void asyncSendOrderly(String destination, Message<?> message, String hashKey, SendCallback sendCallback, | |
| 277 | + long timeout) { | |
| 278 | + if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { | |
| 279 | + log.info("asyncSendOrderly failed. destination:{}, message is null ", destination); | |
| 280 | + throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); | |
| 281 | + } | |
| 282 | + | |
| 283 | + try { | |
| 284 | + org.apache.rocketmq.common.message.Message rocketMsg = convertToRocketMsg(destination, message); | |
| 285 | + producer.send(rocketMsg, messageQueueSelector, hashKey, sendCallback, timeout); | |
| 286 | + } catch (Exception e) { | |
| 287 | + log.info("asyncSendOrderly failed. destination:{}, message:{} ", destination, message); | |
| 288 | + throw new MessagingException(e.getMessage(), e); | |
| 289 | + } | |
| 290 | + } | |
| 291 | + | |
| 292 | + /** | |
| 293 | + * Same to {@link #asyncSend(String, Message, SendCallback)} with send orderly with hashKey by specified. | |
| 294 | + * | |
| 295 | + * @param destination formats: `topicName:tags` | |
| 296 | + * @param message {@link org.springframework.messaging.Message} | |
| 297 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 298 | + * @param sendCallback {@link SendCallback} | |
| 299 | + */ | |
| 300 | + public void asyncSendOrderly(String destination, Message<?> message, String hashKey, SendCallback sendCallback) { | |
| 301 | + asyncSendOrderly(destination, message, hashKey, sendCallback, producer.getSendMsgTimeout()); | |
| 302 | + } | |
| 303 | + | |
| 304 | + /** | |
| 305 | + * Same to {@link #asyncSendOrderly(String, Message, String, SendCallback)}. | |
| 306 | + * | |
| 307 | + * @param destination formats: `topicName:tags` | |
| 308 | + * @param payload the Object to use as payload | |
| 309 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 310 | + * @param sendCallback {@link SendCallback} | |
| 311 | + */ | |
| 312 | + public void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback) { | |
| 313 | + asyncSendOrderly(destination, payload, hashKey, sendCallback, producer.getSendMsgTimeout()); | |
| 314 | + } | |
| 315 | + | |
| 316 | + /** | |
| 317 | + * Same to {@link #asyncSendOrderly(String, Object, String, SendCallback)} with send timeout specified in addition. | |
| 318 | + * | |
| 319 | + * @param destination formats: `topicName:tags` | |
| 320 | + * @param payload the Object to use as payload | |
| 321 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 322 | + * @param sendCallback {@link SendCallback} | |
| 323 | + * @param timeout send timeout with millis | |
| 324 | + */ | |
| 325 | + public void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback, | |
| 326 | + long timeout) { | |
| 327 | + Message<?> message = this.doConvert(payload, null, null); | |
| 328 | + asyncSendOrderly(destination, message, hashKey, sendCallback, timeout); | |
| 329 | + } | |
| 330 | + | |
| 331 | + /** | |
| 332 | + * Similar to <a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a>, this method won't wait for | |
| 333 | + * acknowledgement from broker before return. Obviously, it has maximums throughput yet potentials of message loss. | |
| 334 | + * | |
| 335 | + * One-way transmission is used for cases requiring moderate reliability, such as log collection. | |
| 336 | + * | |
| 337 | + * @param destination formats: `topicName:tags` | |
| 338 | + * @param message {@link org.springframework.messaging.Message} | |
| 339 | + */ | |
| 340 | + public void sendOneWay(String destination, Message<?> message) { | |
| 341 | + if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { | |
| 342 | + log.info("sendOneWay failed. destination:{}, message is null ", destination); | |
| 343 | + throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); | |
| 344 | + } | |
| 345 | + | |
| 346 | + try { | |
| 347 | + org.apache.rocketmq.common.message.Message rocketMsg = convertToRocketMsg(destination, message); | |
| 348 | + producer.sendOneway(rocketMsg); | |
| 349 | + } catch (Exception e) { | |
| 350 | + log.info("sendOneWay failed. destination:{}, message:{} ", destination, message); | |
| 351 | + throw new MessagingException(e.getMessage(), e); | |
| 352 | + } | |
| 353 | + } | |
| 354 | + | |
| 355 | + /** | |
| 356 | + * Same to {@link #sendOneWay(String, Message)} | |
| 357 | + * | |
| 358 | + * @param destination formats: `topicName:tags` | |
| 359 | + * @param payload the Object to use as payload | |
| 360 | + */ | |
| 361 | + public void sendOneWay(String destination, Object payload) { | |
| 362 | + Message<?> message = this.doConvert(payload, null, null); | |
| 363 | + sendOneWay(destination, message); | |
| 364 | + } | |
| 365 | + | |
| 366 | + /** | |
| 367 | + * Same to {@link #sendOneWay(String, Message)} with send orderly with hashKey by specified. | |
| 368 | + * | |
| 369 | + * @param destination formats: `topicName:tags` | |
| 370 | + * @param message {@link org.springframework.messaging.Message} | |
| 371 | + * @param hashKey use this key to select queue. for example: orderId, productId ... | |
| 372 | + */ | |
| 373 | + public void sendOneWayOrderly(String destination, Message<?> message, String hashKey) { | |
| 374 | + if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { | |
| 375 | + log.info("sendOneWayOrderly failed. destination:{}, message is null ", destination); | |
| 376 | + throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); | |
| 377 | + } | |
| 378 | + | |
| 379 | + try { | |
| 380 | + org.apache.rocketmq.common.message.Message rocketMsg = convertToRocketMsg(destination, message); | |
| 381 | + producer.sendOneway(rocketMsg, messageQueueSelector, hashKey); | |
| 382 | + } catch (Exception e) { | |
| 383 | + log.info("sendOneWayOrderly failed. destination:{}, message:{}", destination, message); | |
| 384 | + throw new MessagingException(e.getMessage(), e); | |
| 385 | + } | |
| 386 | + } | |
| 387 | + | |
| 388 | + /** | |
| 389 | + * Same to {@link #sendOneWayOrderly(String, Message, String)} | |
| 390 | + * | |
| 391 | + * @param destination formats: `topicName:tags` | |
| 392 | + * @param payload the Object to use as payload | |
| 393 | + */ | |
| 394 | + public void sendOneWayOrderly(String destination, Object payload, String hashKey) { | |
| 395 | + Message<?> message = this.doConvert(payload, null, null); | |
| 396 | + sendOneWayOrderly(destination, message, hashKey); | |
| 397 | + } | |
| 398 | + | |
| 399 | + public void afterPropertiesSet() throws Exception { | |
| 400 | + Assert.notNull(producer, "Property 'producer' is required"); | |
| 401 | + producer.start(); | |
| 402 | + } | |
| 403 | + | |
| 404 | + protected void doSend(String destination, Message<?> message) { | |
| 405 | + SendResult sendResult = syncSend(destination, message); | |
| 406 | + log.debug("send message to `{}` finished. result:{}", destination, sendResult); | |
| 407 | + } | |
| 408 | + | |
| 409 | + /** | |
| 410 | + * Convert spring message to rocketMQ message | |
| 411 | + * | |
| 412 | + * @param destination formats: `topicName:tags` | |
| 413 | + * @param message {@link org.springframework.messaging.Message} | |
| 414 | + * @return instance of {@link org.apache.rocketmq.common.message.Message} | |
| 415 | + */ | |
| 416 | + private org.apache.rocketmq.common.message.Message convertToRocketMsg(String destination, Message<?> message) { | |
| 417 | + Object payloadObj = message.getPayload(); | |
| 418 | + byte[] payloads; | |
| 419 | + | |
| 420 | + if (payloadObj instanceof String) { | |
| 421 | + payloads = ((String) payloadObj).getBytes(Charset.forName(charset)); | |
| 422 | + } else { | |
| 423 | + try { | |
| 424 | + String jsonObj = this.objectMapper.writeValueAsString(payloadObj); | |
| 425 | + payloads = jsonObj.getBytes(Charset.forName(charset)); | |
| 426 | + } catch (Exception e) { | |
| 427 | + throw new RuntimeException("convert to RocketMQ message failed.", e); | |
| 428 | + } | |
| 429 | + } | |
| 430 | + | |
| 431 | + String[] tempArr = destination.split(":", 2); | |
| 432 | + String topic = tempArr[0]; | |
| 433 | + String tags = ""; | |
| 434 | + if (tempArr.length > 1) { | |
| 435 | + tags = tempArr[1]; | |
| 436 | + } | |
| 437 | + | |
| 438 | + org.apache.rocketmq.common.message.Message rocketMsg = new org.apache.rocketmq.common.message.Message(topic, tags, payloads); | |
| 439 | + | |
| 440 | + MessageHeaders headers = message.getHeaders(); | |
| 441 | + if (Objects.nonNull(headers) && !headers.isEmpty()) { | |
| 442 | + Object keys = headers.get(MessageConst.PROPERTY_KEYS); | |
| 443 | + if (!StringUtils.isEmpty(keys)) { // if headers has 'KEYS', set rocketMQ message key | |
| 444 | + rocketMsg.setKeys(keys.toString()); | |
| 445 | + } | |
| 446 | + | |
| 447 | + // set rocketMQ message flag | |
| 448 | + Object flagObj = headers.getOrDefault("FLAG", "0"); | |
| 449 | + int flag = 0; | |
| 450 | + try { | |
| 451 | + flag = Integer.parseInt(flagObj.toString()); | |
| 452 | + } catch (NumberFormatException e) { | |
| 453 | + // ignore | |
| 454 | + log.info("flag must be integer, flagObj:{}", flagObj); | |
| 455 | + } | |
| 456 | + rocketMsg.setFlag(flag); | |
| 457 | + | |
| 458 | + // set rocketMQ message waitStoreMsgOkObj | |
| 459 | + Object waitStoreMsgOkObj = headers.getOrDefault("WAIT_STORE_MSG_OK", "true"); | |
| 460 | + boolean waitStoreMsgOK = Boolean.TRUE.equals(waitStoreMsgOkObj); | |
| 461 | + rocketMsg.setWaitStoreMsgOK(waitStoreMsgOK); | |
| 462 | + | |
| 463 | + headers.entrySet().stream() | |
| 464 | + .filter(entry -> !Objects.equals(entry.getKey(), MessageConst.PROPERTY_KEYS) | |
| 465 | + && !Objects.equals(entry.getKey(), "FLAG") | |
| 466 | + && !Objects.equals(entry.getKey(), "WAIT_STORE_MSG_OK")) // exclude "KEYS", "FLAG", "WAIT_STORE_MSG_OK" | |
| 467 | + .forEach(entry -> { | |
| 468 | + rocketMsg.putUserProperty("USERS_" + entry.getKey(), String.valueOf(entry.getValue())); // add other properties with prefix "USERS_" | |
| 469 | + }); | |
| 470 | + | |
| 471 | + } | |
| 472 | + | |
| 473 | + return rocketMsg; | |
| 474 | + } | |
| 475 | + | |
| 476 | + @Override | |
| 477 | + protected Message<?> doConvert(Object payload, Map<String, Object> headers, MessagePostProcessor postProcessor) { | |
| 478 | + String content; | |
| 479 | + if (payload instanceof String) { | |
| 480 | + content = (String) payload; | |
| 481 | + } else { | |
| 482 | + // if payload not as string, use objectMapper change it. | |
| 483 | + try { | |
| 484 | + content = objectMapper.writeValueAsString(payload); | |
| 485 | + } catch (JsonProcessingException e) { | |
| 486 | + log.info("convert payload to String failed. payload:{}", payload); | |
| 487 | + throw new RuntimeException("convert to payload to String failed.", e); | |
| 488 | + } | |
| 489 | + } | |
| 490 | + | |
| 491 | + MessageBuilder<?> builder = MessageBuilder.withPayload(content); | |
| 492 | + if (headers != null) { | |
| 493 | + builder.copyHeaders(headers); | |
| 494 | + } | |
| 495 | + builder.setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN); | |
| 496 | + | |
| 497 | + Message<?> message = builder.build(); | |
| 498 | + if (postProcessor != null) { | |
| 499 | + message = postProcessor.postProcessMessage(message); | |
| 500 | + } | |
| 501 | + return message; | |
| 502 | + } | |
| 503 | + | |
| 504 | + @Override | |
| 505 | + public void destroy() { | |
| 506 | + if (Objects.nonNull(producer)) { | |
| 507 | + producer.shutdown(); | |
| 508 | + } | |
| 509 | + } | |
| 510 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/enums/ConsumeMode.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.enums; | |
| 19 | + | |
| 20 | +public enum ConsumeMode { | |
| 21 | + /** | |
| 22 | + * receive asynchronously delivered messages concurrently | |
| 23 | + */ | |
| 24 | + CONCURRENTLY, | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * receive asynchronously delivered messages orderly. one queue, one thread | |
| 28 | + */ | |
| 29 | + ORDERLY | |
| 30 | +} | ... | ... |
src/main/java/org/apache/rocketmq/spring/starter/enums/SelectorType.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter.enums; | |
| 19 | + | |
| 20 | +import org.apache.rocketmq.common.filter.ExpressionType; | |
| 21 | + | |
| 22 | +public enum SelectorType { | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * @see ExpressionType#TAG | |
| 26 | + */ | |
| 27 | + TAG, | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * @see ExpressionType#SQL92 | |
| 31 | + */ | |
| 32 | + SQL92 | |
| 33 | +} | ... | ... |
src/main/resources/META-INF/spring.factories
0 → 100644
src/test/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java
0 → 100644
| 1 | +/* | |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 | + * contributor license agreements. See the NOTICE file distributed with | |
| 4 | + * this work for additional information regarding copyright ownership. | |
| 5 | + * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 | + * (the "License"); you may not use this file except in compliance with | |
| 7 | + * the License. You may obtain a copy of the License at | |
| 8 | + * | |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 | + * | |
| 11 | + * Unless required by applicable law or agreed to in writing, software | |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 | + * See the License for the specific language governing permissions and | |
| 15 | + * limitations under the License. | |
| 16 | + */ | |
| 17 | + | |
| 18 | +package org.apache.rocketmq.spring.starter; | |
| 19 | + | |
| 20 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
| 21 | +import org.apache.rocketmq.spring.starter.annotation.RocketMQMessageListener; | |
| 22 | +import org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainer; | |
| 23 | +import org.apache.rocketmq.spring.starter.core.RocketMQListener; | |
| 24 | +import org.apache.rocketmq.spring.starter.core.RocketMQTemplate; | |
| 25 | +import org.apache.rocketmq.spring.starter.enums.ConsumeMode; | |
| 26 | +import org.apache.rocketmq.spring.starter.enums.SelectorType; | |
| 27 | +import org.apache.rocketmq.client.producer.DefaultMQProducer; | |
| 28 | +import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; | |
| 29 | +import org.junit.After; | |
| 30 | +import org.junit.Test; | |
| 31 | +import org.springframework.beans.factory.support.BeanDefinitionBuilder; | |
| 32 | +import org.springframework.boot.test.util.EnvironmentTestUtils; | |
| 33 | +import org.springframework.context.annotation.AnnotationConfigApplicationContext; | |
| 34 | + | |
| 35 | +import static org.assertj.core.api.Assertions.assertThat; | |
| 36 | + | |
| 37 | +public class RocketMQAutoConfigurationTests { | |
| 38 | + | |
| 39 | + private static final String TEST_CONSUMER_GROUP = "my_consumer"; | |
| 40 | + | |
| 41 | + private static final String TEST_TOPIC = "test-topic"; | |
| 42 | + | |
| 43 | + private AnnotationConfigApplicationContext context; | |
| 44 | + | |
| 45 | + @Test | |
| 46 | + public void rocketMQTemplate() { | |
| 47 | + | |
| 48 | + load("spring.rocketmq.nameServer=127.0.0.1:9876", | |
| 49 | + "spring.rocketmq.producer.group=my_group", | |
| 50 | + "spring.rocketmq.producer.send-msg-timeout=30000", | |
| 51 | + "spring.rocketmq.producer.retry-times-when-send-async-failed=1", | |
| 52 | + "spring.rocketmq.producer.compress-msg-body-over-howmuch=1024", | |
| 53 | + "spring.rocketmq.producer.max-message-size=10240", | |
| 54 | + "spring.rocketmq.producer.retry-another-broker-when-not-store-ok=true", | |
| 55 | + "spring.rocketmq.producer.retry-times-when-send-failed=1"); | |
| 56 | + | |
| 57 | + assertThat(this.context.containsBean("rocketMQMessageObjectMapper")).isTrue(); | |
| 58 | + assertThat(this.context.containsBean("mqProducer")).isTrue(); | |
| 59 | + assertThat(this.context.containsBean("rocketMQTemplate")).isTrue(); | |
| 60 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); | |
| 61 | + | |
| 62 | + RocketMQTemplate rocketMQTemplate = this.context.getBean(RocketMQTemplate.class); | |
| 63 | + ObjectMapper objectMapper = this.context.getBean("rocketMQMessageObjectMapper", ObjectMapper.class); | |
| 64 | + assertThat(rocketMQTemplate.getObjectMapper()).isEqualTo(objectMapper); | |
| 65 | + | |
| 66 | + DefaultMQProducer defaultMQProducer = rocketMQTemplate.getProducer(); | |
| 67 | + | |
| 68 | + assertThat(defaultMQProducer.getNamesrvAddr()).isEqualTo("127.0.0.1:9876"); | |
| 69 | + assertThat(defaultMQProducer.getProducerGroup()).isEqualTo("my_group"); | |
| 70 | + assertThat(defaultMQProducer.getSendMsgTimeout()).isEqualTo(30000); | |
| 71 | + assertThat(defaultMQProducer.getRetryTimesWhenSendAsyncFailed()).isEqualTo(1); | |
| 72 | + assertThat(defaultMQProducer.getCompressMsgBodyOverHowmuch()).isEqualTo(1024); | |
| 73 | + assertThat(defaultMQProducer.getMaxMessageSize()).isEqualTo(10240); | |
| 74 | + assertThat(defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()).isTrue(); | |
| 75 | + assertThat(defaultMQProducer.getRetryTimesWhenSendFailed()).isEqualTo(1); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @Test | |
| 79 | + public void enableProducer() { | |
| 80 | + load(); | |
| 81 | + assertThat(this.context.containsBean("mqProducer")).isFalse(); | |
| 82 | + assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); | |
| 83 | + closeContext(); | |
| 84 | + | |
| 85 | + load("spring.rocketmq.nameServer=127.0.0.1:9876"); | |
| 86 | + assertThat(this.context.containsBean("mqProducer")).isFalse(); | |
| 87 | + assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); | |
| 88 | + closeContext(); | |
| 89 | + | |
| 90 | + load("spring.rocketmq.producer.group=my_group"); | |
| 91 | + assertThat(this.context.containsBean("mqProducer")).isFalse(); | |
| 92 | + assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); | |
| 93 | + closeContext(); | |
| 94 | + | |
| 95 | + load("spring.rocketmq.nameServer=127.0.0.1:9876", "spring.rocketmq.producer.group=my_group"); | |
| 96 | + assertThat(this.context.containsBean("mqProducer")).isTrue(); | |
| 97 | + assertThat(this.context.containsBean("rocketMQTemplate")).isEqualTo(true); | |
| 98 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); | |
| 99 | + } | |
| 100 | + | |
| 101 | + @Test | |
| 102 | + public void enableConsumer() { | |
| 103 | + load(); | |
| 104 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); | |
| 105 | + closeContext(); | |
| 106 | + | |
| 107 | + load("spring.rocketmq.nameServer=127.0.0.1:9876"); | |
| 108 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); | |
| 109 | + closeContext(); | |
| 110 | + | |
| 111 | + load(false); | |
| 112 | + this.context.registerBeanDefinition("myListener", | |
| 113 | + BeanDefinitionBuilder.rootBeanDefinition(MyListener.class).getBeanDefinition()); | |
| 114 | + this.context.refresh(); | |
| 115 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); | |
| 116 | + closeContext(); | |
| 117 | + | |
| 118 | + load(false, "spring.rocketmq.nameServer=127.0.0.1:9876"); | |
| 119 | + this.context.registerBeanDefinition("myListener", | |
| 120 | + BeanDefinitionBuilder.rootBeanDefinition(MyListener.class).getBeanDefinition()); | |
| 121 | + this.context.refresh(); | |
| 122 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isNotEmpty(); | |
| 123 | + assertThat(this.context.containsBean(DefaultRocketMQListenerContainer.class.getName() + "_1")).isTrue(); | |
| 124 | + assertThat(this.context.containsBean("mqProducer")).isFalse(); | |
| 125 | + assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); | |
| 126 | + | |
| 127 | + } | |
| 128 | + | |
| 129 | + @Test | |
| 130 | + public void listenerContainer() { | |
| 131 | + load(false, "spring.rocketmq.nameServer=127.0.0.1:9876"); | |
| 132 | + BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(MyListener.class); | |
| 133 | + this.context.registerBeanDefinition("myListener", beanBuilder.getBeanDefinition()); | |
| 134 | + this.context.refresh(); | |
| 135 | + | |
| 136 | + assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isNotEmpty(); | |
| 137 | + assertThat(this.context.containsBean(DefaultRocketMQListenerContainer.class.getName() + "_1")).isTrue(); | |
| 138 | + | |
| 139 | + DefaultRocketMQListenerContainer listenerContainer = | |
| 140 | + this.context.getBean(DefaultRocketMQListenerContainer.class.getName() + "_1", | |
| 141 | + DefaultRocketMQListenerContainer.class); | |
| 142 | + ObjectMapper objectMapper = this.context.getBean("rocketMQMessageObjectMapper", ObjectMapper.class); | |
| 143 | + assertThat(listenerContainer.getObjectMapper()).isEqualTo(objectMapper); | |
| 144 | + assertThat(listenerContainer.getConsumeMode()).isEqualTo(ConsumeMode.CONCURRENTLY); | |
| 145 | + assertThat(listenerContainer.getSelectorType()).isEqualTo(SelectorType.TAG); | |
| 146 | + assertThat(listenerContainer.getSelectorExpress()).isEqualTo("*"); | |
| 147 | + assertThat(listenerContainer.getConsumerGroup()).isEqualTo(TEST_CONSUMER_GROUP); | |
| 148 | + assertThat(listenerContainer.getTopic()).isEqualTo(TEST_TOPIC); | |
| 149 | + assertThat(listenerContainer.getNameServer()).isEqualTo("127.0.0.1:9876"); | |
| 150 | + assertThat(listenerContainer.getMessageModel()).isEqualTo(MessageModel.CLUSTERING); | |
| 151 | + assertThat(listenerContainer.getConsumeThreadMax()).isEqualTo(1); | |
| 152 | + } | |
| 153 | + | |
| 154 | + @After | |
| 155 | + public void closeContext() { | |
| 156 | + if (this.context != null) { | |
| 157 | + this.context.close(); | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + @RocketMQMessageListener(consumerGroup = TEST_CONSUMER_GROUP, topic = TEST_TOPIC, consumeThreadMax = 1) | |
| 162 | + private static class MyListener implements RocketMQListener<String> { | |
| 163 | + | |
| 164 | + @Override | |
| 165 | + public void onMessage(String message) { | |
| 166 | + System.out.println(message); | |
| 167 | + } | |
| 168 | + } | |
| 169 | + | |
| 170 | + private void load(boolean refresh, String... environment) { | |
| 171 | + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); | |
| 172 | + ctx.register(RocketMQAutoConfiguration.class); | |
| 173 | + EnvironmentTestUtils.addEnvironment(ctx, environment); | |
| 174 | + if (refresh) { | |
| 175 | + ctx.refresh(); | |
| 176 | + } | |
| 177 | + this.context = ctx; | |
| 178 | + } | |
| 179 | + | |
| 180 | + private void load(String... environment) { | |
| 181 | + load(true, environment); | |
| 182 | + } | |
| 183 | +} | |
| 184 | + | ... | ... |