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