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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | + |