├── .gitignore ├── LICENSE ├── META-INF ├── LICENSE.txt └── MANIFEST.MF ├── README.md ├── pom.xml └── src └── main ├── assembly └── assembly.xml ├── java └── com │ └── aliyun │ └── mq │ └── http │ ├── MQClient.java │ ├── MQConsumer.java │ ├── MQProducer.java │ ├── MQTransProducer.java │ ├── common │ ├── AckMessageException.java │ ├── ClientErrorCode.java │ ├── ClientException.java │ ├── Constants.java │ ├── HttpMethod.java │ ├── ServiceException.java │ ├── auth │ │ ├── HmacSHA1Signature.java │ │ ├── RequestSigner.java │ │ ├── ServiceCredentials.java │ │ └── ServiceSignature.java │ ├── comm │ │ ├── ExecutionContext.java │ │ ├── NoRetryStrategy.java │ │ ├── RepeatableInputStreamEntity.java │ │ ├── RequestHandler.java │ │ ├── ResponseHandler.java │ │ └── RetryStrategy.java │ ├── http │ │ ├── ClientConfiguration.java │ │ ├── DefaultServiceClient.java │ │ ├── ExceptionResultParser.java │ │ ├── HttpCallback.java │ │ ├── HttpClientConfig.java │ │ ├── HttpDeleteWrapper.java │ │ ├── HttpFactory.java │ │ ├── HttpMesssage.java │ │ ├── RequestMessage.java │ │ ├── ResponseMessage.java │ │ ├── ServiceClient.java │ │ └── ServiceClientFactory.java │ ├── parser │ │ ├── JAXBResultParser.java │ │ ├── ResultParseException.java │ │ └── ResultParser.java │ └── utils │ │ ├── BinaryUtil.java │ │ ├── BooleanSerializer.java │ │ ├── CaseInsensitiveMap.java │ │ ├── CodingUtils.java │ │ ├── DateUtil.java │ │ ├── HttpHeaders.java │ │ ├── HttpUtil.java │ │ ├── IOUtils.java │ │ ├── ResourceManager.java │ │ ├── ServiceConstants.java │ │ ├── Utils.java │ │ └── VersionInfoUtils.java │ └── model │ ├── AbstractRequest.java │ ├── AbstractResponse.java │ ├── AsyncCallback.java │ ├── AsyncResult.java │ ├── BaseMessage.java │ ├── ErrorMessage.java │ ├── ErrorMessageResult.java │ ├── Message.java │ ├── TopicMessage.java │ ├── action │ ├── AbstractAction.java │ ├── AckMessageAction.java │ ├── Action.java │ ├── ConsumeMessageAction.java │ └── PublishMessageAction.java │ ├── jaxb.index │ ├── request │ ├── AckMessageRequest.java │ ├── ConsumeMessageRequest.java │ └── PublishMessageRequest.java │ └── serialize │ ├── BaseXMLSerializer.java │ ├── Deserializer.java │ ├── ErrorReceiptHandleListDeserializer.java │ ├── MessageListDeserializer.java │ ├── ReceiptHandleListSerializer.java │ ├── Serializer.java │ ├── TopicMessageDeserializer.java │ ├── TopicMessageSerializer.java │ ├── XMLDeserializer.java │ ├── XMLSerializer.java │ └── XmlUtil.java └── resources ├── common.properties └── common_zh_CN.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .idea/ 26 | *.iml 27 | target/ 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 aliyun.mq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /META-INF/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 aliyun.mq 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including wicd ..thout limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Sealed: true 3 | Created-By: AliyunMQ 4 | Implementation-Title: Aliyun Message Queue(MQ) Http Java SDK 5 | Implementation-Version: 1.0.0-SNAPSHOT 6 | url: https://github.com/aliyunmq/mq-http-java-sdk 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MQ JAVA HTTP SDK 2 | 3 | Alyun MQ Documents: http://www.aliyun.com/product/ons 4 | 5 | Aliyun MQ Console: https://ons.console.aliyun.com 6 | 7 | ## Use 8 | 9 | ### Add Maven Dependency 10 | ``` 11 | 12 | com.aliyun.mq 13 | mq-http-sdk 14 | 1.0.3.2 15 | 16 | ``` 17 | 18 | Or With-dependencies 19 | ``` 20 | 21 | com.aliyun.mq 22 | mq-http-sdk 23 | 1.0.3.2 24 | jar-with-dependencies 25 | 26 | ``` 27 | 28 | ## Note 29 | 1. Http consumer only support timer msg (less than 3 days), no matter the msg is produced from http or tcp protocol. 30 | 2. Order is only supported at special server cluster. 31 | 32 | ### Sample (github) 33 | 34 | [Publish Message](https://github.com/aliyunmq/mq-http-samples/blob/master/java/src/main/java/Producer.java) 35 | 36 | [Consume Message](https://github.com/aliyunmq/mq-http-samples/blob/master/java/src/main/java/Consumer.java) 37 | 38 | [Transaction Message](https://github.com/aliyunmq/mq-http-samples/blob/master/java/src/main/java/TransProducer.java) 39 | 40 | [Publish Order Message](https://github.com/aliyunmq/mq-http-samples/blob/master/java/src/main/java/OrderProducer.java) 41 | 42 | [Consume Order Message](https://github.com/aliyunmq/mq-http-samples/blob/master/java/src/main/java/OrderConsumer.java) 43 | 44 | ### Sample (code.aliyun.com) 45 | 46 | [Publish Message](https://code.aliyun.com/aliware_rocketmq/mq-http-samples/blob/master/java/src/main/java/Producer.java) 47 | 48 | [Consume Message](https://code.aliyun.com/aliware_rocketmq/mq-http-samples/blob/master/java/src/main/java/Consumer.java) 49 | 50 | [Transaction Message](https://code.aliyun.com/aliware_rocketmq/mq-http-samples/blob/master/java/src/main/java/TransProducer.java) 51 | 52 | [Publish Order Message](https://code.aliyun.com/aliware_rocketmq/mq-http-samples/blob/master/java/src/main/java/OrderProducer.java) 53 | 54 | [Consume Order Message](https://code.aliyun.com/aliware_rocketmq/mq-http-samples/blob/master/java/src/main/java/OrderConsumer.java) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.sonatype.oss 5 | oss-parent 6 | 9 7 | 8 | 9 | 10 | 11 | sonatype-nexus-snapshots 12 | https://s01.oss.sonatype.org/content/repositories/snapshots 13 | 14 | 15 | sonatype-nexus-staging 16 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 17 | 18 | 19 | 20 | 4.0.0 21 | com.aliyun.mq 22 | mq-http-sdk 23 | 1.0.3.3-SNAPSHOT 24 | jar 25 | Aliyun MQ Java HTTP SDK 26 | http://www.aliyun.com/product/ons 27 | 28 | 29 | junit 30 | junit 31 | 4.10 32 | test 33 | 34 | 35 | org.apache.httpcomponents 36 | httpasyncclient 37 | 4.1 38 | 39 | 40 | org.apache.commons 41 | commons-lang3 42 | 3.4 43 | 44 | 45 | org.slf4j 46 | slf4j-api 47 | 1.7.32 48 | 49 | 50 | com.google.code.gson 51 | gson 52 | 2.8.2 53 | 54 | 55 | 56 | 57 | 58 | maven-compiler-plugin 59 | 2.3.2 60 | 61 | 1.6 62 | 1.6 63 | UTF-8 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-jar-plugin 69 | 2.3.2 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-surefire-plugin 76 | 2.10 77 | 78 | -Dfile.encoding=UTF-8 79 | 80 | 81 | 82 | run-test 83 | test 84 | 85 | test 86 | 87 | 88 | 89 | **/integration/*.java 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-source-plugin 99 | 2.2.1 100 | 101 | 102 | attach-sources 103 | 104 | jar-no-fork 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-eclipse-plugin 112 | 113 | true 114 | true 115 | 116 | 117 | 118 | maven-failsafe-plugin 119 | 2.6 120 | 121 | -Dfile.encoding=UTF-8 122 | 123 | 124 | 125 | 126 | integration-test 127 | verify 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-javadoc-plugin 135 | 2.8 136 | 137 | UTF-8 138 | com.aliyun.mq.http.common.*;com.aliyun.mq.http.model.action.*;com.aliyun.mq.http.model.request.*;com.aliyun.mq.http.model.serialize.*; 139 | 140 | 141 | author 142 | X 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-assembly-plugin 150 | 2.2.1 151 | 152 | 153 | src/main/assembly/assembly.xml 154 | 155 | 156 | 157 | 158 | make-assembly 159 | package 160 | 161 | single 162 | 163 | 164 | 165 | 166 | 167 | org.codehaus.mojo 168 | exec-maven-plugin 169 | 1.2.1 170 | 171 | 172 | 173 | java 174 | 175 | 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-shade-plugin 181 | 3.2.1 182 | 183 | 184 | package 185 | 186 | shade 187 | 188 | 189 | 190 | 191 | true 192 | jar-with-dependencies 193 | 194 | 195 | org.apache.http 196 | shaded.org.apache.http 197 | 198 | 199 | org.apache.commons 200 | shaded.org.apache.commons 201 | 202 | 203 | com.google.gson 204 | shaded.com.google.gson 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | MIT 214 | https://opensource.org/licenses/MIT 215 | repo 216 | 217 | 218 | 219 | master 220 | https://github.com/aliyunmq/mq-http-java-sdk 221 | scm:git:ssh://git@github.com/aliyunmq/mq-http-java-sdk.git 222 | scm:git:ssh://git@github.com/aliyunmq/mq-http-java-sdk.git 223 | 224 | 225 | 226 | aliyunmq 227 | aliyunmq 228 | 229 | http://www.aliyun.com/product/ons 230 | Aliyun MQ 231 | http://www.aliyun.com/product/ons 232 | 233 | architect 234 | developer 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /src/main/assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | allinone 3 | 4 | zip 5 | 6 | 7 | 8 | true 9 | lib 10 | 11 | 12 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/MQClient.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.http.ClientConfiguration; 5 | import com.aliyun.mq.http.common.utils.Utils; 6 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 7 | import com.aliyun.mq.http.common.http.ServiceClient; 8 | import com.aliyun.mq.http.common.http.ServiceClientFactory; 9 | 10 | import java.net.URI; 11 | import javax.print.DocFlavor; 12 | 13 | public class MQClient { 14 | 15 | /** 16 | * http url 17 | */ 18 | private URI endpoint; 19 | 20 | /** 21 | * http client 22 | */ 23 | private ServiceClient serviceClient; 24 | 25 | /** 26 | * user info 27 | */ 28 | private ServiceCredentials credentials; 29 | private ClientConfiguration config; 30 | 31 | private static volatile MQProducer PRODUCER; 32 | private static volatile MQTransProducer TRANSACTIONAL_PRODUCER; 33 | private static volatile MQConsumer CONSUMER; 34 | 35 | private static MQConsumer buildConsumer(String instanceId, String topicName, String consumer, String messageTag, ServiceClient client, 36 | ServiceCredentials credentials, URI endpoint) { 37 | return new MQConsumer(instanceId, topicName, consumer, messageTag, client, credentials, endpoint); 38 | } 39 | 40 | private static MQProducer buildProducer(String instanceId, String topicName, ServiceClient client, 41 | ServiceCredentials credentials, URI endpoint) { 42 | return new MQProducer(instanceId, topicName, client, credentials, endpoint); 43 | } 44 | 45 | private static MQTransProducer buildTransactionalProducer(String instanceId, String topicName, String groupId, ServiceClient client, 46 | ServiceCredentials credentials, URI endpoint) { 47 | return new MQTransProducer(instanceId, topicName, groupId, client, credentials, endpoint); 48 | } 49 | 50 | /** 51 | * init a MQ client with default client config 52 | * 53 | * @param accountEndpoint mq http endpoint, like: http://xxx.mqreset.cn-hangzhou.aliyuncs.com 54 | * @param accessId aliyun access id 55 | * @param accessKey aliyun access secret key 56 | */ 57 | public MQClient(String accountEndpoint, String accessId, String accessKey) { 58 | this(accountEndpoint, accessId, accessKey, null, null); 59 | } 60 | 61 | /** 62 | * init a MQ client with defined client config 63 | * 64 | * @param accountEndpoint mq http endpoint, like: http://xxx.mqreset.cn-hangzhou.aliyuncs.com 65 | * @param accessId aliyun access id 66 | * @param accessKey aliyun access secret key 67 | * @param config defined client config 68 | */ 69 | public MQClient(String accountEndpoint, String accessId, String accessKey, ClientConfiguration config) { 70 | this(accountEndpoint, accessId, accessKey, null, config); 71 | } 72 | 73 | /** 74 | * init a MQ client with default client config, use sts token to access mq 75 | * 76 | * @param accountEndpoint mq http endpoint, like: http://xxx.mqreset.cn-hangzhou.aliyuncs.com 77 | * @param accessId aliyun access id 78 | * @param accessKey aliyun access secret key 79 | * @param securityToken aliyun sts token 80 | */ 81 | public MQClient(String accountEndpoint, String accessId, String accessKey, String securityToken) { 82 | this(accountEndpoint, accessId, accessKey, securityToken, null); 83 | } 84 | 85 | /** 86 | * init a MQ client with defined client config, use sts token to access mq 87 | * 88 | * @param accountEndpoint mq http endpoint, like: http://xxx.mqreset.cn-hangzhou.aliyuncs.com 89 | * @param accessId aliyun access id 90 | * @param accessKey aliyun access secret key 91 | * @param securityToken aliyun sts token 92 | * @param config defined client config 93 | */ 94 | public MQClient(String accountEndpoint, String accessId, String accessKey, String securityToken, ClientConfiguration config) { 95 | this.credentials = new ServiceCredentials(accessId, accessKey, securityToken); 96 | this.endpoint = Utils.getHttpURI(accountEndpoint); 97 | if (config == null) { 98 | this.config = new ClientConfiguration(); 99 | } else { 100 | this.config = config; 101 | } 102 | try { 103 | this.serviceClient = ServiceClientFactory.createServiceClient(this.config); 104 | } catch (Exception e) { 105 | if (this.serviceClient != null) { 106 | ServiceClientFactory.closeServiceClient(serviceClient); 107 | } 108 | throw new ClientException(e); 109 | } 110 | } 111 | 112 | public void close() { 113 | synchronized (this) { 114 | if (isOpen()) { 115 | ServiceClientFactory.closeServiceClient(this.serviceClient); 116 | } 117 | } 118 | } 119 | 120 | public boolean isOpen() { 121 | return serviceClient != null && this.serviceClient.isOpen(); 122 | } 123 | 124 | /** 125 | * default instance 126 | * 127 | * @param topicName topic name 128 | * @return MQProducer 129 | */ 130 | public MQProducer getProducer(String topicName) { 131 | if (null == PRODUCER) { 132 | synchronized (MQClient.class) { 133 | if (null == PRODUCER) { 134 | PRODUCER = buildProducer(null, topicName, this.serviceClient, this.credentials, this.endpoint); 135 | } 136 | } 137 | } 138 | return PRODUCER; 139 | } 140 | 141 | /** 142 | * with instance 143 | * 144 | * @param instanceId instance id 145 | * @param topicName topic name 146 | * @return MQProducer 147 | */ 148 | public MQProducer getProducer(String instanceId, String topicName) { 149 | if (null == PRODUCER) { 150 | synchronized (MQClient.class) { 151 | if (null == PRODUCER) { 152 | PRODUCER = buildProducer(instanceId, topicName, this.serviceClient, this.credentials, this.endpoint); 153 | } 154 | } 155 | } 156 | return PRODUCER; 157 | } 158 | 159 | /** 160 | * default instance, mq transaction producer 161 | * 162 | * @param topicName topic name 163 | * @param groupId consumer id or group id that is for consume transaction half msg. 164 | * @return MQTransProducer 165 | */ 166 | public MQTransProducer getTransProducer(String topicName, String groupId) { 167 | if (null == TRANSACTIONAL_PRODUCER) { 168 | synchronized (MQClient.class) { 169 | if (null == TRANSACTIONAL_PRODUCER) { 170 | TRANSACTIONAL_PRODUCER = buildTransactionalProducer(null, topicName, groupId, this.serviceClient, this.credentials, this.endpoint); 171 | } 172 | } 173 | } 174 | return TRANSACTIONAL_PRODUCER; 175 | } 176 | 177 | /** 178 | * with instance, mq transaction producer 179 | * 180 | * @param instanceId instance id 181 | * @param topicName topic name 182 | * @param groupId consumer id or group id that is for consume transaction half msg. 183 | * @return MQTransProducer 184 | */ 185 | public MQTransProducer getTransProducer(String instanceId, String topicName, String groupId) { 186 | if (null == TRANSACTIONAL_PRODUCER) { 187 | synchronized (MQClient.class) { 188 | if (null == TRANSACTIONAL_PRODUCER) { 189 | TRANSACTIONAL_PRODUCER = buildTransactionalProducer(instanceId, topicName, groupId, this.serviceClient, this.credentials, this.endpoint); 190 | } 191 | } 192 | } 193 | return TRANSACTIONAL_PRODUCER; 194 | } 195 | 196 | /** 197 | * default instance with filter message tag. 198 | * 199 | * @param topicName topic name 200 | * @param consumer client id 201 | * @param messageTag message tag 202 | * @return MQConsumer 203 | */ 204 | public MQConsumer getConsumer(String topicName, String consumer, String messageTag) { 205 | if (null == CONSUMER) { 206 | synchronized (MQClient.class) { 207 | if (null == CONSUMER) { 208 | CONSUMER = buildConsumer(null, topicName, consumer, messageTag, this.serviceClient, this.credentials, this.endpoint); 209 | } 210 | } 211 | } 212 | return CONSUMER; 213 | } 214 | 215 | /** 216 | * default instance 217 | * 218 | * @param topicName topic name 219 | * @param consumer client id 220 | * @return MQConsumer 221 | */ 222 | public MQConsumer getConsumer(String topicName, String consumer) { 223 | if (null == CONSUMER) { 224 | synchronized (MQClient.class) { 225 | if (null == CONSUMER) { 226 | CONSUMER = buildConsumer(null, topicName, consumer, null, this.serviceClient, this.credentials, this.endpoint); 227 | } 228 | } 229 | } 230 | return CONSUMER; 231 | } 232 | 233 | /** 234 | * instance with filter message tag. 235 | * 236 | * @param instanceId instance id 237 | * @param topicName topic name 238 | * @param consumer client id 239 | * @param messageTag message tag 240 | * @return MQConsumer 241 | */ 242 | public MQConsumer getConsumer(String instanceId, String topicName, String consumer, String messageTag) { 243 | if (null == CONSUMER) { 244 | synchronized (MQClient.class) { 245 | if (null == CONSUMER) { 246 | CONSUMER = buildConsumer(instanceId, topicName, consumer, messageTag, this.serviceClient, this.credentials, this.endpoint); 247 | } 248 | } 249 | } 250 | return CONSUMER; 251 | } 252 | 253 | @Override 254 | public String toString() { 255 | final StringBuilder sb = new StringBuilder("MQClient{"); 256 | sb.append("endpoint=").append(endpoint); 257 | sb.append(", credentials=").append(credentials); 258 | sb.append('}'); 259 | return sb.toString(); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/MQConsumer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http; 2 | 3 | import com.aliyun.mq.http.model.AsyncCallback; 4 | import com.aliyun.mq.http.model.AsyncResult; 5 | import com.aliyun.mq.http.common.ClientException; 6 | import com.aliyun.mq.http.common.Constants; 7 | import com.aliyun.mq.http.common.ServiceException; 8 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 9 | import com.aliyun.mq.http.common.http.ServiceClient; 10 | import com.aliyun.mq.http.model.Message; 11 | import com.aliyun.mq.http.model.TopicMessage; 12 | import com.aliyun.mq.http.model.action.AckMessageAction; 13 | import com.aliyun.mq.http.model.action.ConsumeMessageAction; 14 | import com.aliyun.mq.http.model.request.AckMessageRequest; 15 | import com.aliyun.mq.http.model.request.ConsumeMessageRequest; 16 | 17 | import java.io.UnsupportedEncodingException; 18 | import java.net.URI; 19 | import java.net.URLEncoder; 20 | import java.util.List; 21 | 22 | public class MQConsumer { 23 | 24 | private final ServiceClient serviceClient; 25 | /** 26 | * topic url, ie: http://uid.mqrest.region.aliyuncs.com/topics/topicName 27 | */ 28 | private final String topicURL; 29 | private final String topicName; 30 | private final String consumer; 31 | /** 32 | * filter messageTag for consumer. 33 | * If not empty, only consume the message which's @see {@link TopicMessage#messageTag} is equal to it. 34 | */ 35 | private final String messageTag; 36 | /** 37 | * object content user auth info 38 | */ 39 | private final ServiceCredentials credentials; 40 | /** 41 | * user mq http endpoint, ie: http://uid.mqrest.region.aliyuncs.com/ 42 | */ 43 | private final URI endpoint; 44 | /** 45 | * instance id 46 | */ 47 | private String instanceId; 48 | 49 | /** 50 | * @param instanceId, instance id 51 | * @param topicName, topic name 52 | * @param consumer mq cid 53 | * @param messageTag message tag for filter 54 | * @param client, ServiceClient object 55 | * @param credentials, ServiceCredentials object 56 | * @param endpoint, user mq http endpoint, ie: http://uid.mqrest.region.aliyuncs.com/ 57 | */ 58 | protected MQConsumer(String instanceId, String topicName, String consumer, String messageTag, ServiceClient client, 59 | ServiceCredentials credentials, URI endpoint) { 60 | this.instanceId = instanceId; 61 | this.serviceClient = client; 62 | this.credentials = credentials; 63 | this.endpoint = endpoint; 64 | 65 | String uri = endpoint.toString(); 66 | if (!uri.endsWith(Constants.SLASH)) { 67 | uri += Constants.SLASH; 68 | } 69 | uri += Constants.TPOIC_PREFIX + topicName; 70 | this.topicURL = uri; 71 | this.topicName = topicName; 72 | this.consumer = consumer; 73 | if (this.consumer == null || this.consumer.isEmpty()) { 74 | throw new RuntimeException("Consumer can't be empty"); 75 | } 76 | if (messageTag != null) { 77 | try { 78 | this.messageTag = URLEncoder.encode(messageTag, "utf-8"); 79 | } catch (UnsupportedEncodingException e) { 80 | throw new RuntimeException("messageTag maybe not utf-8 character.", e); 81 | } 82 | } else { 83 | this.messageTag = null; 84 | } 85 | } 86 | 87 | /** 88 | * the topic name 89 | * 90 | * @return topic name 91 | */ 92 | public String getTopicName() { 93 | return topicName; 94 | } 95 | 96 | /** 97 | * the consumer group name of mq 98 | * 99 | * @return consumer(client id) 100 | */ 101 | public String getConsumer() { 102 | return consumer; 103 | } 104 | 105 | /** 106 | * filter message tag, could be null. 107 | * @return message tag(for filter) 108 | */ 109 | public String getMessageTag() { 110 | return messageTag; 111 | } 112 | 113 | /** 114 | * instance id 115 | * @return instance id 116 | */ 117 | public String getInstanceId() { 118 | return instanceId; 119 | } 120 | 121 | /** 122 | * sync consume message from topic. 123 | * If the messages gotten by {@link #consumeMessage(int, int)} are not acked by {@link #ackMessage(List)}, they will be consumable again after 300 seconds; 124 | * 125 | * @param num the count of messages consumed once 126 | * value: 1~16 127 | * @param pollingSecond if greater than 0, means the time(second) the request holden at server if there is no message to consume. 128 | * If less or equal 0, means the server will response back if there is no message to consume. 129 | * value: 1~30 130 | * @return null or List 131 | */ 132 | public List consumeMessage(int num, int pollingSecond) 133 | throws ServiceException, ClientException { 134 | ConsumeMessageRequest request = new ConsumeMessageRequest(); 135 | request.setConsumer(this.consumer); 136 | request.setBatchSize(num); 137 | request.setTag(messageTag); 138 | request.setWaitSeconds(pollingSecond); 139 | request.setInstanceId(this.instanceId); 140 | 141 | try { 142 | ConsumeMessageAction action = new ConsumeMessageAction(serviceClient, credentials, endpoint); 143 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 144 | return action.executeWithCustomHeaders(request, null); 145 | } catch (ServiceException e) { 146 | if (Constants.CODE_MESSAGE_NOT_EXIST.equals(e.getErrorCode())) { 147 | return null; 148 | } else { 149 | throw e; 150 | } 151 | } 152 | } 153 | 154 | /** 155 | * sync consume message from topic orderly. 156 | * 157 | * Next messages will be consumed if all of same shard are acked. Otherwise, same messages will be consumed again after NextConsumeTime. 158 | * 159 | * Attention: the topic should be order topic created at console, if not, mq could not keep the order feature. 160 | * 161 | * This interface is suitable for globally order and partitionally order messages, and could be used in multi-thread scenes. 162 | * 163 | * @param num the count of messages consumed once 164 | * value: 1~16 165 | * @param pollingSecond if greater than 0, means the time(second) the request holden at server if there is no message to consume. 166 | * If less or equal 0, means the server will response back if there is no message to consume. 167 | * value: 1~30 168 | * @return null or List may contains several shard's messages, the messages of one shard are ordered. 169 | * Get the sharding key through {@link Message#getShardingKey()} 170 | */ 171 | public List consumeMessageOrderly(int num, int pollingSecond) 172 | throws ServiceException, ClientException { 173 | ConsumeMessageRequest request = new ConsumeMessageRequest(); 174 | request.setConsumer(this.consumer); 175 | request.setBatchSize(num); 176 | request.setTag(messageTag); 177 | request.setWaitSeconds(pollingSecond); 178 | request.setInstanceId(this.instanceId); 179 | request.setTrans("order"); 180 | 181 | try { 182 | ConsumeMessageAction action = new ConsumeMessageAction(serviceClient, credentials, endpoint); 183 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 184 | return action.executeWithCustomHeaders(request, null); 185 | } catch (ServiceException e) { 186 | if (Constants.CODE_MESSAGE_NOT_EXIST.equals(e.getErrorCode())) { 187 | return null; 188 | } else { 189 | throw e; 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * async consume message from topic. 196 | * If the messages gotten by {@link #consumeMessage(int, int)} are not acked by {@link #ackMessage(List)}, they will be consumable again after 300 seconds; 197 | * 198 | * @param num the count of messages consumed once 199 | * value: 1~16 200 | * @param pollingSecond if greater than 0, means the time(second) the request holden at server if there is no message to consume. 201 | * If less or equal 0, means the server will response back if there is no message to consume. 202 | * value: 1~30 203 | * @param callback, user callback object 204 | * @return AsyncResult, you can get the result blocked. 205 | */ 206 | public AsyncResult> asyncConsumeMessage(int num, int pollingSecond, AsyncCallback> callback) { 207 | ConsumeMessageRequest request = new ConsumeMessageRequest(); 208 | request.setConsumer(consumer); 209 | request.setBatchSize(num); 210 | request.setTag(messageTag); 211 | request.setWaitSeconds(pollingSecond); 212 | request.setInstanceId(this.instanceId); 213 | 214 | ConsumeMessageAction action = new ConsumeMessageAction(serviceClient, credentials, endpoint); 215 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 216 | return action.executeWithCustomHeaders(request, callback, null); 217 | } 218 | 219 | /** 220 | * Tell server the messages are consumed success. 221 | * If the messages gotten by {@link #consumeMessage(int, int)} are not acked by {@link #ackMessage(List)}, they will be consumable again after 300 seconds; 222 | * 223 | * @param receiptHandles size should be 1~16, @see {@link Message#receiptHandle} 224 | * @throws ServiceException Exception from server 225 | * @throws ClientException Exception from client 226 | */ 227 | public void ackMessage(List receiptHandles) throws ServiceException, ClientException { 228 | AckMessageAction action = new AckMessageAction(serviceClient, credentials, endpoint); 229 | 230 | AckMessageRequest request = new AckMessageRequest(); 231 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 232 | request.setConsumer(consumer); 233 | request.setReceiptHandles(receiptHandles); 234 | request.setInstanceId(this.instanceId); 235 | action.executeWithCustomHeaders(request, null); 236 | } 237 | 238 | /** 239 | * Async tell server the messages are consumed success. 240 | * If the messages gotten by {@link #consumeMessage(int, int)} are not acked by {@link #ackMessage(List)}, they will be consumable again after 300 seconds; 241 | * 242 | * @param receiptHandles size should be 1~16, @see {@link Message#receiptHandle}, 243 | * @param callback, user callback object 244 | * @throws ServiceException Exception from server 245 | * @throws ClientException Exception from client 246 | * 247 | * @return AsyncResult, you can get the result blocked. 248 | */ 249 | public AsyncResult asyncAckMessage(List receiptHandles, AsyncCallback callback) 250 | throws ServiceException, ClientException { 251 | AckMessageAction action = new AckMessageAction(serviceClient, credentials, endpoint); 252 | 253 | AckMessageRequest request = new AckMessageRequest(); 254 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 255 | request.setConsumer(consumer); 256 | request.setReceiptHandles(receiptHandles); 257 | request.setInstanceId(this.instanceId); 258 | return action.executeWithCustomHeaders(request, callback, null); 259 | } 260 | 261 | @Override 262 | public String toString() { 263 | final StringBuilder sb = new StringBuilder("MQConsumer{"); 264 | sb.append("topicName='").append(topicName).append('\''); 265 | sb.append(", consumer='").append(consumer).append('\''); 266 | sb.append(", messageTag='").append(messageTag).append('\''); 267 | sb.append(", endpoint=").append(endpoint); 268 | sb.append(", instanceId='").append(instanceId).append('\''); 269 | sb.append('}'); 270 | return sb.toString(); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/MQProducer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http; 2 | 3 | import com.aliyun.mq.http.model.AsyncCallback; 4 | import com.aliyun.mq.http.model.AsyncResult; 5 | import com.aliyun.mq.http.model.action.PublishMessageAction; 6 | import com.aliyun.mq.http.common.ClientException; 7 | import com.aliyun.mq.http.common.Constants; 8 | import com.aliyun.mq.http.common.ServiceException; 9 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 10 | import com.aliyun.mq.http.common.http.ServiceClient; 11 | import com.aliyun.mq.http.model.TopicMessage; 12 | import com.aliyun.mq.http.model.request.PublishMessageRequest; 13 | 14 | import java.net.URI; 15 | 16 | public class MQProducer { 17 | protected ServiceClient serviceClient; 18 | /** 19 | * topic url, ie: http://uid.mqrest.region.aliyuncs.com/topics/topicName 20 | */ 21 | protected String topicURL; 22 | protected String topicName; 23 | /** 24 | * object content user auth info 25 | */ 26 | protected ServiceCredentials credentials; 27 | /** 28 | * user mq http endpoint, ie: http://uid.mqrest.region.aliyuncs.com/ 29 | */ 30 | protected URI endpoint; 31 | /** 32 | * instance id 33 | */ 34 | protected String instanceId; 35 | 36 | /** 37 | * @param instanceId, instance id 38 | * @param topicName, topic name 39 | * @param client, ServiceClient object 40 | * @param credentials, ServiceCredentials object 41 | * @param endpoint, user mq http endpoint, ie: http://uid.mqrest.region.aliyuncs.com/ 42 | */ 43 | protected MQProducer(String instanceId, String topicName, ServiceClient client, 44 | ServiceCredentials credentials, URI endpoint) { 45 | this.instanceId = instanceId; 46 | this.serviceClient = client; 47 | this.credentials = credentials; 48 | this.endpoint = endpoint; 49 | 50 | String uri = endpoint.toString(); 51 | if (!uri.endsWith(Constants.SLASH)) { 52 | uri += Constants.SLASH; 53 | } 54 | uri += Constants.TPOIC_PREFIX + topicName; 55 | this.topicURL = uri; 56 | this.topicName = topicName; 57 | } 58 | 59 | public String getTopicName() { 60 | return topicName; 61 | } 62 | 63 | public String getInstanceId() { 64 | return instanceId; 65 | } 66 | 67 | protected void checkMessage(TopicMessage msg) throws ClientException { 68 | String prop = msg.getProperties().get(Constants.MESSAGE_PROPERTIES_TRANS_CHECK_KEY); 69 | if (prop == null || prop.length() <= 0) { 70 | return; 71 | } 72 | try { 73 | Integer.valueOf(prop); 74 | } catch (Throwable e) { 75 | throw new ClientException("Should setTransCheckImmunityTime Integer!", "LocalClientError"); 76 | } 77 | 78 | prop = msg.getProperties().get(Constants.MESSAGE_PROPERTIES_TIMER_KEY); 79 | if (prop == null || prop.length() <= 0) { 80 | return; 81 | } 82 | try { 83 | Long.valueOf(prop); 84 | } catch (Throwable e) { 85 | throw new ClientException("Should setStartDeliverTime Long!", "LocalClientError"); 86 | } 87 | } 88 | 89 | /** 90 | * publish message to topic. 91 | * 92 | *
 93 |      *     - Timing message : {@link TopicMessage#setStartDeliverTime(long)}
 94 |      *     - Ordered message : {@link TopicMessage#setShardingKey(String)}
 95 |      * 
96 | * 97 | * @param msg message 98 | * @return message from server 99 | * @throws ServiceException Exception from server 100 | * @throws ClientException Exception from client 101 | */ 102 | public TopicMessage publishMessage(TopicMessage msg) throws ServiceException, ClientException { 103 | checkMessage(msg); 104 | 105 | PublishMessageRequest request = new PublishMessageRequest(); 106 | request.setMessage(msg); 107 | request.setInstanceId(instanceId); 108 | PublishMessageAction action = new PublishMessageAction(serviceClient, credentials, endpoint); 109 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 110 | return action.executeWithCustomHeaders(request, null); 111 | } 112 | 113 | /** 114 | * async publish message to topic. 115 | * 116 | *
117 |      *     - Timing message : {@link TopicMessage#setStartDeliverTime(long)}
118 |      *     - Ordered message : {@link TopicMessage#setShardingKey(String)}, it's recommended to sync publish order message.
119 |      * 
120 | * 121 | * @param msg message 122 | * @param callback, user callback object 123 | * @return AsyncResult, you can get the result blocked. 124 | */ 125 | public AsyncResult asyncPublishMessage(TopicMessage msg, AsyncCallback callback) { 126 | checkMessage(msg); 127 | 128 | PublishMessageRequest request = new PublishMessageRequest(); 129 | request.setMessage(msg); 130 | request.setInstanceId(instanceId); 131 | PublishMessageAction action = new PublishMessageAction(serviceClient, credentials, endpoint); 132 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 133 | return action.executeWithCustomHeaders(request, callback, null); 134 | } 135 | 136 | @Override 137 | public String toString() { 138 | final StringBuilder sb = new StringBuilder("MQProducer{"); 139 | sb.append("topicName='").append(topicName).append('\''); 140 | sb.append(", endpoint=").append(endpoint); 141 | sb.append(", instanceId='").append(instanceId).append('\''); 142 | sb.append('}'); 143 | return sb.toString(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/MQTransProducer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.Constants; 5 | import com.aliyun.mq.http.common.ServiceException; 6 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 7 | import com.aliyun.mq.http.common.http.ServiceClient; 8 | import com.aliyun.mq.http.model.AsyncCallback; 9 | import com.aliyun.mq.http.model.AsyncResult; 10 | import com.aliyun.mq.http.model.Message; 11 | import com.aliyun.mq.http.model.action.AckMessageAction; 12 | import com.aliyun.mq.http.model.action.ConsumeMessageAction; 13 | import com.aliyun.mq.http.model.request.AckMessageRequest; 14 | import com.aliyun.mq.http.model.request.ConsumeMessageRequest; 15 | 16 | import java.net.URI; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | /** 21 | * transaction producer 22 | */ 23 | public class MQTransProducer extends MQProducer { 24 | protected final String groupId; 25 | 26 | /** 27 | * @param instanceId, instance id 28 | * @param topicName, topic name 29 | * @param client, ServiceClient object 30 | * @param credentials, ServiceCredentials object 31 | * @param endpoint, user mq http endpoint, ie: http://uid.mqrest.region.aliyuncs.com/ 32 | */ 33 | protected MQTransProducer(String instanceId, String topicName, String groupId, ServiceClient client, 34 | ServiceCredentials credentials, URI endpoint) { 35 | super(instanceId, topicName, client, credentials, endpoint); 36 | this.groupId = groupId; 37 | } 38 | 39 | /** 40 | * consume half message to check transaction status, three choice: {@link #commit(String)} , {@link #rollback(String)} 41 | * or do nothing (after 10s will get the message again). 42 | * 43 | * @param num the count of messages consumed once 44 | * value: 1~16 45 | * @param pollingSecond if greater than 0, means the time(second) the request holden at server if there is no message to consume. 46 | * If less or equal 0, means the server will response back if there is no message to consume. 47 | * value: 1~30 48 | * 49 | * @throws ServiceException Exception from server 50 | * @throws ClientException Exception from client 51 | */ 52 | public List consumeHalfMessage(int num, int pollingSecond) throws ServiceException, ClientException { 53 | ConsumeMessageRequest request = new ConsumeMessageRequest(); 54 | request.setBatchSize(num); 55 | request.setWaitSeconds(pollingSecond); 56 | request.setInstanceId(this.instanceId); 57 | request.setConsumer(groupId); 58 | request.setTrans(Constants.PARAM_TRANSACTION_V_POP); 59 | 60 | try { 61 | ConsumeMessageAction action = new ConsumeMessageAction(serviceClient, credentials, endpoint); 62 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 63 | return action.executeWithCustomHeaders(request, null); 64 | } catch (ServiceException e) { 65 | if (Constants.CODE_MESSAGE_NOT_EXIST.equals(e.getErrorCode())) { 66 | return null; 67 | } else { 68 | throw e; 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * consume half message to check transaction status, three choice: {@link #commit(String)} , {@link #rollback(String)} 75 | * or do nothing (will get the message again after nextConsumeTime). 76 | * 77 | * @param num the count of messages consumed once 78 | * value: 1~16 79 | * @param pollingSecond if greater than 0, means the time(second) the request holden at server if there is no message to consume. 80 | * If less or equal 0, means the server will response back if there is no message to consume. 81 | * value: 1~30 82 | * @param callback, user callback object 83 | * @return AsyncResult, you can get the result blocked. 84 | */ 85 | public AsyncResult> asyncConsumeHalfMessage(int num, int pollingSecond, AsyncCallback> callback) { 86 | ConsumeMessageRequest request = new ConsumeMessageRequest(); 87 | request.setBatchSize(num); 88 | request.setWaitSeconds(pollingSecond); 89 | request.setInstanceId(this.instanceId); 90 | request.setConsumer(groupId); 91 | request.setTrans(Constants.PARAM_TRANSACTION_V_POP); 92 | 93 | ConsumeMessageAction action = new ConsumeMessageAction(serviceClient, credentials, endpoint); 94 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 95 | return action.executeWithCustomHeaders(request, callback, null); 96 | } 97 | 98 | /** 99 | * commit transaction msg, the consumer will receive the msg. 100 | * 101 | * @param handle msg handle 102 | * @throws ServiceException Exception from server 103 | * @throws ClientException Exception from client 104 | */ 105 | public void commit(String handle) throws ServiceException, ClientException { 106 | AckMessageAction action = new AckMessageAction(serviceClient, credentials, endpoint); 107 | AckMessageRequest request = new AckMessageRequest(); 108 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 109 | request.setReceiptHandles(Arrays.asList(handle)); 110 | request.setInstanceId(this.instanceId); 111 | request.setConsumer(groupId); 112 | request.setTrans("commit"); 113 | 114 | action.executeWithCustomHeaders(request, null); 115 | } 116 | 117 | /** 118 | * rollback transaction msg, the consumer will not receive the msg. 119 | * 120 | * @param handle msg handle 121 | * @throws ServiceException Exception from server 122 | * @throws ClientException Exception from client 123 | */ 124 | public void rollback(String handle) throws ServiceException, ClientException { 125 | AckMessageAction action = new AckMessageAction(serviceClient, credentials, endpoint); 126 | AckMessageRequest request = new AckMessageRequest(); 127 | request.setRequestPath(topicURL + "/" + Constants.LOCATION_MESSAGES); 128 | request.setReceiptHandles(Arrays.asList(handle)); 129 | request.setInstanceId(this.instanceId); 130 | request.setConsumer(groupId); 131 | request.setTrans("rollback"); 132 | 133 | action.executeWithCustomHeaders(request, null); 134 | } 135 | 136 | @Override 137 | public String toString() { 138 | final StringBuilder sb = new StringBuilder("MQTransProducer{"); 139 | sb.append("topicName='").append(topicName).append('\''); 140 | sb.append("groupId='").append(groupId).append('\''); 141 | sb.append(", endpoint=").append(endpoint); 142 | sb.append(", instanceId='").append(instanceId).append('\''); 143 | sb.append('}'); 144 | return sb.toString(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/AckMessageException.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common; 2 | 3 | import com.aliyun.mq.http.model.ErrorMessageResult; 4 | 5 | import java.util.Map; 6 | 7 | public class AckMessageException extends ServiceException { 8 | /** 9 | * 10 | */ 11 | private static final long serialVersionUID = -7705861423905005565L; 12 | private Map errorMessages; 13 | 14 | public AckMessageException(Map errorMsgs) { 15 | this.errorMessages = errorMsgs; 16 | } 17 | 18 | public Map getErrorMessages() { 19 | return errorMessages; 20 | } 21 | 22 | public void setErrorMessages(Map errorMessages) { 23 | this.errorMessages = errorMessages; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/ClientErrorCode.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common; 2 | 3 | public interface ClientErrorCode { 4 | 5 | /** 6 | * 未知错误 7 | */ 8 | static final String UNKNOWN = "Unknown"; 9 | 10 | /** 11 | * 远程服务连接超时 12 | */ 13 | static final String CONNECTION_TIMEOUT = "ConnectionTimeout"; 14 | 15 | /** 16 | * 远程服务socket读写超时 17 | */ 18 | static final String SOCKET_TIMEOUT = "SocketTimeout"; 19 | 20 | /** 21 | * 返回结果无法解析 22 | */ 23 | static final String INVALID_RESPONSE = "InvalidResponse"; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/ClientException.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common; 2 | 3 | public class ClientException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1870835486798448798L; 6 | 7 | private String errorCode = ClientErrorCode.UNKNOWN; 8 | private String requestId; 9 | 10 | public ClientException() { 11 | super(); 12 | } 13 | 14 | public ClientException(String message, String requestId) { 15 | super(message); 16 | this.requestId = requestId; 17 | } 18 | 19 | public ClientException(Throwable cause) { 20 | super(cause); 21 | } 22 | 23 | public ClientException(String message, String requestId, Throwable cause) { 24 | super(message, cause); 25 | this.requestId = requestId; 26 | } 27 | 28 | public ClientException(String errorCode, String message, String requestId, Throwable cause) { 29 | super(message, cause); 30 | this.errorCode = errorCode; 31 | this.requestId = requestId; 32 | } 33 | 34 | public String getErrorCode() { 35 | return errorCode; 36 | } 37 | 38 | public String getRequestId() { 39 | return requestId; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "[Error Code]:" + errorCode + ", " 45 | + "[Message]:" + getMessage() + ", " 46 | + "[RequestId]: " + getRequestId(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/Constants.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common; 2 | 3 | import com.aliyun.mq.http.common.utils.HttpHeaders; 4 | 5 | public interface Constants extends HttpHeaders { 6 | String LOCATION_MESSAGES = "messages"; 7 | 8 | String X_HEADER_API_VERSION = "x-mq-version"; 9 | String X_HEADER_API_VERSION_VALUE = "2015-06-06"; 10 | 11 | String X_HEADER_PREFIX = "x-mq-"; 12 | String X_HEADER_REQUEST_ID = "x-mq-request-id"; 13 | 14 | 15 | String DEFAULT_CHARSET = "UTF-8"; 16 | String DEFAULT_CONTENT_TYPE = "text/xml;charset=UTF-8"; 17 | 18 | String DEFAULT_XML_NAMESPACE = "http://mq.aliyuncs.com/doc/v1"; 19 | 20 | String TPOIC_PREFIX = "topics/"; 21 | 22 | String MESSAGE_TAG = "Message"; 23 | String MESSAGE_ID_TAG = "MessageId"; 24 | 25 | String RECEIPT_HANDLE_LIST_TAG = "ReceiptHandles"; 26 | String RECEIPT_HANDLE_TAG = "ReceiptHandle"; 27 | String MESSAGE_BODY_TAG = "MessageBody"; 28 | String MESSAGE_BODY_MD5_TAG = "MessageBodyMD5"; 29 | String PUBLISH_TIME_TAG = "PublishTime"; 30 | String NEXT_CONSUME_TIME_TAG = "NextConsumeTime"; 31 | String FIRST_CONSUME_TIME_TAG = "FirstConsumeTime"; 32 | String CONSUMED_TIMES_TAG = "ConsumedTimes"; 33 | String MESSAGE_TAG_TAG = "MessageTag"; 34 | String MESSAGE_PROPERTIES = "Properties"; 35 | String MESSAGE_PROPERTIES_TIMER_KEY = "__STARTDELIVERTIME"; 36 | String MESSAGE_PROPERTIES_TRANS_CHECK_KEY = "__TransCheckT"; 37 | String MESSAGE_PROPERTIES_MSG_KEY = "KEYS"; 38 | String MESSAGE_PROPERTIES_SHARDING = "__SHARDINGKEY"; 39 | 40 | String ERROR_LIST_TAG = "Errors"; 41 | String ERROR_TAG = "Error"; 42 | String ERROR_CODE_TAG = "Code"; 43 | String ERROR_MESSAGE_TAG = "Message"; 44 | String ERROR_REQUEST_ID_TAG = "RequestId"; 45 | String ERROR_HOST_ID_TAG = "HostId"; 46 | String MESSAGE_ERRORCODE_TAG = "ErrorCode"; 47 | String MESSAGE_ERRORMESSAGE_TAG = "ErrorMessage"; 48 | 49 | String PARAM_WAIT_SECONDS = "waitseconds"; 50 | String PARAM_CONSUMER_TAG = "tag"; 51 | String PARAM_CONSUMER = "consumer"; 52 | String PARAM_CONSUME_NUM = "numOfMessages"; 53 | String PARAM_NS = "ns"; 54 | String PARAM_TRANSACTION = "trans"; 55 | String PARAM_TRANSACTION_V_POP = "pop"; 56 | String PARAM_ORDER = "order"; 57 | 58 | String SLASH = "/"; 59 | String HTTP_PREFIX = "http://"; 60 | String HTTPS_PREFIX = "https://"; 61 | 62 | String CODE_MESSAGE_NOT_EXIST = "MessageNotExist"; 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common; 2 | 3 | /** 4 | * 表示HTTP的请求方法。 5 | */ 6 | public enum HttpMethod { 7 | /** 8 | * DELETE方法。 9 | */ 10 | DELETE, 11 | 12 | /** 13 | * GET方法。 14 | */ 15 | GET, 16 | 17 | /** 18 | * HEAD方法。 19 | */ 20 | HEAD, 21 | 22 | /** 23 | * POST方法。 24 | */ 25 | POST, 26 | 27 | /** 28 | * PUT方法。 29 | */ 30 | PUT, 31 | 32 | /** 33 | * OPTION方法。 34 | */ 35 | OPTIONS 36 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/ServiceException.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common; 2 | 3 | /** 4 | * Service Exception 5 | */ 6 | public class ServiceException extends RuntimeException { 7 | 8 | private static final long serialVersionUID = 430933593095358673L; 9 | private String errorCode; 10 | private String requestId; 11 | private String hostId; 12 | 13 | public ServiceException() { 14 | super(); 15 | this.errorCode = ""; 16 | } 17 | 18 | public ServiceException(String message, String requestId) { 19 | super(message); 20 | this.requestId = requestId; 21 | this.errorCode = ""; 22 | } 23 | 24 | public ServiceException(Throwable cause) { 25 | super(cause); 26 | this.errorCode = ""; 27 | } 28 | 29 | public ServiceException(String message, String requestId, Throwable cause) { 30 | super(message, cause); 31 | this.requestId = requestId; 32 | this.errorCode = ""; 33 | } 34 | 35 | /** 36 | * 用异常消息和表示异常原因及其他信息的对象构造新实例。 37 | * 38 | * @param message 异常信息。 39 | * @param cause 异常原因。 40 | * @param errorCode 错误代码。 41 | * @param requestId Request ID。 42 | * @param hostId Host ID。 43 | */ 44 | public ServiceException(String message, Throwable cause, 45 | String errorCode, String requestId, String hostId) { 46 | this(message, requestId, cause); 47 | 48 | if (errorCode != null) { 49 | this.errorCode = errorCode; 50 | } 51 | this.hostId = hostId; 52 | } 53 | 54 | /** 55 | * 返回错误代码的字符串表示。 56 | * 57 | * @return 错误代码的字符串表示。 58 | */ 59 | public String getErrorCode() { 60 | return errorCode; 61 | } 62 | 63 | /** 64 | * 返回Request标识。 65 | * 66 | * @return Request标识。 67 | */ 68 | public String getRequestId() { 69 | return requestId; 70 | } 71 | 72 | /** 73 | * 返回Host标识。 74 | * 75 | * @return Host标识。 76 | */ 77 | public String getHostId() { 78 | return hostId; 79 | } 80 | 81 | public void setRequestId(String requestId) { 82 | this.requestId = requestId; 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return "[Error Code]:" + errorCode + ", " 88 | + "[Message]:" + getMessage() + ", " 89 | + "[RequestId]: " + getRequestId(); 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/auth/HmacSHA1Signature.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.auth; 2 | 3 | import com.aliyun.mq.http.common.utils.BinaryUtil; 4 | 5 | import javax.crypto.Mac; 6 | import javax.crypto.spec.SecretKeySpec; 7 | import java.io.UnsupportedEncodingException; 8 | import java.security.InvalidKeyException; 9 | import java.security.NoSuchAlgorithmException; 10 | 11 | /** 12 | * Hmac-SHA1签名。 13 | */ 14 | public class HmacSHA1Signature extends ServiceSignature { 15 | private static final String DEFAULT_ENCODING = "UTF-8"; // Default encoding 16 | private static final String ALGORITHM = "HmacSHA1"; // Signature method. 17 | private static final String VERSION = "1"; // Signature version. 18 | private static final Object LOCK = new Object(); 19 | private static Mac macInstance; // Prototype of the Mac instance. 20 | 21 | public HmacSHA1Signature() { 22 | } 23 | 24 | public String getAlgorithm() { 25 | return ALGORITHM; 26 | } 27 | 28 | public String getVersion() { 29 | return VERSION; 30 | } 31 | 32 | public String computeSignature(String key, String data) { 33 | try { 34 | byte[] signData = sign( 35 | key.getBytes(DEFAULT_ENCODING), 36 | data.getBytes(DEFAULT_ENCODING)); 37 | 38 | return BinaryUtil.toBase64String(signData); 39 | } catch (UnsupportedEncodingException ex) { 40 | throw new RuntimeException("Unsupported algorithm: " + DEFAULT_ENCODING); 41 | } 42 | } 43 | 44 | 45 | private byte[] sign(byte[] key, byte[] data) { 46 | try { 47 | // Because Mac.getInstance(String) calls a synchronized method, 48 | // it could block on invoked concurrently. 49 | // SO use prototype pattern to improve perf. 50 | if (macInstance == null) { 51 | synchronized (LOCK) { 52 | if (macInstance == null) { 53 | macInstance = Mac.getInstance(ALGORITHM); 54 | } 55 | } 56 | } 57 | 58 | Mac mac; 59 | try { 60 | mac = (Mac) macInstance.clone(); 61 | } catch (CloneNotSupportedException e) { 62 | // If it is not clonable, create a new one. 63 | mac = Mac.getInstance(ALGORITHM); 64 | } 65 | mac.init(new SecretKeySpec(key, ALGORITHM)); 66 | return mac.doFinal(data); 67 | } catch (NoSuchAlgorithmException ex) { 68 | throw new RuntimeException("Unsupported algorithm: " + ALGORITHM); 69 | } catch (InvalidKeyException ex) { 70 | throw new RuntimeException(); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/auth/RequestSigner.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.auth; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.http.RequestMessage; 5 | 6 | public interface RequestSigner { 7 | 8 | public void sign(RequestMessage request) 9 | throws ClientException; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/auth/ServiceCredentials.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.auth; 2 | 3 | import com.aliyun.mq.http.common.utils.CodingUtils; 4 | 5 | public class ServiceCredentials { 6 | private String accessKeyId; 7 | private String accessKeySecret; 8 | private String securityToken; 9 | 10 | public ServiceCredentials() { 11 | } 12 | 13 | public ServiceCredentials(String accessKeyId, String accessKeySecret, String securityToken) { 14 | setAccessKeyId(accessKeyId); 15 | setAccessKeySecret(accessKeySecret); 16 | setSecurityToken(securityToken); 17 | } 18 | 19 | public ServiceCredentials(String accessKeyId, String accessKeySecret) { 20 | this(accessKeyId, accessKeySecret, ""); 21 | } 22 | 23 | public String getAccessKeyId() { 24 | return accessKeyId; 25 | } 26 | 27 | public void setAccessKeyId(String accessKeyId) { 28 | CodingUtils.assertParameterNotNull(accessKeyId, "accessKeyId"); 29 | this.accessKeyId = accessKeyId; 30 | } 31 | 32 | public String getAccessKeySecret() { 33 | return accessKeySecret; 34 | } 35 | 36 | public void setAccessKeySecret(String accessKeySecret) { 37 | CodingUtils.assertParameterNotNull(accessKeySecret, "accessKeySecret"); 38 | 39 | this.accessKeySecret = accessKeySecret; 40 | } 41 | 42 | public String getSecurityToken() { 43 | return securityToken; 44 | } 45 | 46 | public void setSecurityToken(String securityToken) { 47 | this.securityToken = securityToken; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | final StringBuilder sb = new StringBuilder("ServiceCredentials{"); 53 | sb.append("accessKeyId='").append(accessKeyId).append('\''); 54 | sb.append(", accessKeySecret='").append(accessKeySecret).append('\''); 55 | sb.append(", securityToken='").append(securityToken).append('\''); 56 | sb.append('}'); 57 | return sb.toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/auth/ServiceSignature.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.auth; 2 | 3 | public abstract class ServiceSignature { 4 | public ServiceSignature() { 5 | } 6 | 7 | public static ServiceSignature create() { 8 | return new HmacSHA1Signature(); 9 | } 10 | 11 | public abstract String getAlgorithm(); 12 | 13 | public abstract String getVersion(); 14 | 15 | public abstract String computeSignature(String key, String data); 16 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/comm/ExecutionContext.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.comm; 2 | 3 | import com.aliyun.mq.http.common.auth.RequestSigner; 4 | import com.aliyun.mq.http.common.utils.ServiceConstants; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | public class ExecutionContext { 10 | private String charset = ServiceConstants.DEFAULT_ENCODING; 11 | private RequestSigner signer; 12 | // The request handlers that handle request content in as a pipeline 13 | private List requestHandlers = new LinkedList(); 14 | // The response handlers that handle response message in as a pipeline. 15 | private List responseHandlers = new LinkedList(); 16 | private RetryStrategy retryStrategy; 17 | 18 | /** 19 | * Constructor. 20 | */ 21 | public ExecutionContext() { 22 | } 23 | 24 | public RetryStrategy getRetryStrategy() { 25 | return retryStrategy; 26 | } 27 | 28 | public void setRetryStrategy(RetryStrategy retryStrategy) { 29 | this.retryStrategy = retryStrategy; 30 | } 31 | 32 | /** 33 | * Returns the default encoding (charset). Default: "UTF-8" 34 | * 35 | * @return charset 36 | */ 37 | public String getCharset() { 38 | return charset; 39 | } 40 | 41 | /** 42 | * Sets the default encoding (charset). Default: "UTF-8" 43 | * 44 | * @param defaultEncoding charset 45 | */ 46 | public void setCharset(String defaultEncoding) { 47 | this.charset = defaultEncoding; 48 | } 49 | 50 | /** 51 | * @return the signer 52 | */ 53 | public RequestSigner getSigner() { 54 | return signer; 55 | } 56 | 57 | /** 58 | * @param signer the signer to set 59 | */ 60 | public void setSigner(RequestSigner signer) { 61 | this.signer = signer; 62 | } 63 | 64 | /** 65 | * @return the responseHandlers 66 | */ 67 | public List getResponseHandlers() { 68 | return responseHandlers; 69 | } 70 | 71 | public void addResponseHandler(ResponseHandler handler) { 72 | responseHandlers.add(handler); 73 | } 74 | 75 | public void insertResponseHandler(int position, ResponseHandler handler) { 76 | responseHandlers.add(position, handler); 77 | } 78 | 79 | public void removeResponseHandler(ResponseHandler handler) { 80 | responseHandlers.remove(handler); 81 | } 82 | 83 | /** 84 | * @return the requestHandlers 85 | */ 86 | public List getResquestHandlers() { 87 | return requestHandlers; 88 | } 89 | 90 | public void addRequestHandler(RequestHandler handler) { 91 | requestHandlers.add(handler); 92 | } 93 | 94 | public void insertRequestHandler(int position, RequestHandler handler) { 95 | requestHandlers.add(position, handler); 96 | } 97 | 98 | public void removeRequestHandler(RequestHandler handler) { 99 | requestHandlers.remove(handler); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/comm/NoRetryStrategy.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.comm; 2 | 3 | import com.aliyun.mq.http.common.http.RequestMessage; 4 | import com.aliyun.mq.http.common.http.ResponseMessage; 5 | 6 | public class NoRetryStrategy extends RetryStrategy { 7 | 8 | @Override 9 | public boolean shouldRetry(Exception ex, RequestMessage request, 10 | ResponseMessage response, int retries) { 11 | return false; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/comm/RepeatableInputStreamEntity.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.comm; 2 | 3 | import com.aliyun.mq.http.common.http.ServiceClient; 4 | import com.aliyun.mq.http.common.utils.HttpHeaders; 5 | import org.apache.http.entity.AbstractHttpEntity; 6 | import org.apache.http.entity.BasicHttpEntity; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | 12 | public class RepeatableInputStreamEntity extends BasicHttpEntity { 13 | 14 | private boolean firstAttempt = true; 15 | 16 | private NoAutoClosedInputStreamEntity innerEntity; 17 | 18 | private InputStream content; 19 | 20 | public RepeatableInputStreamEntity(ServiceClient.Request request) { 21 | setChunked(false); 22 | 23 | String contentType = request.getHeaders().get(HttpHeaders.CONTENT_TYPE); 24 | content = request.getContent(); 25 | long contentLength = request.getContentLength(); 26 | 27 | innerEntity = new NoAutoClosedInputStreamEntity(content, contentLength); 28 | innerEntity.setContentType(contentType); 29 | 30 | setContent(content); 31 | setContentType(contentType); 32 | setContentLength(request.getContentLength()); 33 | } 34 | 35 | @Override 36 | public boolean isChunked() { 37 | return false; 38 | } 39 | 40 | @Override 41 | public boolean isRepeatable() { 42 | return content.markSupported() || innerEntity.isRepeatable(); 43 | } 44 | 45 | @Override 46 | public void writeTo(OutputStream output) throws IOException { 47 | if (!firstAttempt && isRepeatable()) { 48 | content.reset(); 49 | } 50 | 51 | firstAttempt = false; 52 | innerEntity.writeTo(output); 53 | } 54 | 55 | /** 56 | * The default entity org.apache.http.entity.InputStreamEntity will close input stream after wirteTo was called. 57 | * To avoid this, we custom a entity that will not close stream automatically. 58 | */ 59 | public static class NoAutoClosedInputStreamEntity extends AbstractHttpEntity { 60 | private final static int BUFFER_SIZE = 2048; 61 | 62 | private final InputStream content; 63 | private final long length; 64 | 65 | public NoAutoClosedInputStreamEntity(final InputStream instream, long length) { 66 | super(); 67 | if (instream == null) { 68 | throw new IllegalArgumentException("Source input stream may not be null"); 69 | } 70 | this.content = instream; 71 | this.length = length; 72 | } 73 | 74 | public boolean isRepeatable() { 75 | return false; 76 | } 77 | 78 | public long getContentLength() { 79 | return this.length; 80 | } 81 | 82 | public InputStream getContent() throws IOException { 83 | return this.content; 84 | } 85 | 86 | public void writeTo(final OutputStream outstream) throws IOException { 87 | if (outstream == null) { 88 | throw new IllegalArgumentException("Output stream may not be null"); 89 | } 90 | InputStream instream = this.content; 91 | 92 | byte[] buffer = new byte[BUFFER_SIZE]; 93 | int l; 94 | if (this.length < 0) { 95 | // consume until EOF 96 | while ((l = instream.read(buffer)) != -1) { 97 | outstream.write(buffer, 0, l); 98 | } 99 | } else { 100 | // consume no more than length 101 | long remaining = this.length; 102 | while (remaining > 0) { 103 | l = instream.read(buffer, 0, (int) Math.min(BUFFER_SIZE, remaining)); 104 | if (l == -1) { 105 | break; 106 | } 107 | outstream.write(buffer, 0, l); 108 | remaining -= l; 109 | } 110 | } 111 | 112 | } 113 | 114 | public boolean isStreaming() { 115 | return true; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/comm/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.comm; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.ServiceException; 5 | import com.aliyun.mq.http.common.http.ServiceClient; 6 | 7 | public interface RequestHandler { 8 | 9 | /** 10 | * pre handle the request 11 | * 12 | * @param message request 13 | * @throws ServiceException Exception from server 14 | * @throws ClientException Exception from client 15 | */ 16 | void handle(ServiceClient.Request message) throws ServiceException, ClientException; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/comm/ResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.comm; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.ServiceException; 5 | import com.aliyun.mq.http.common.http.ResponseMessage; 6 | 7 | public interface ResponseHandler { 8 | 9 | /** 10 | * handle response 11 | * 12 | * @param responseData data of response 13 | * @throws ServiceException Exception from server 14 | * @throws ClientException Exception from client 15 | */ 16 | void handle(ResponseMessage responseData) 17 | throws ServiceException, ClientException; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/comm/RetryStrategy.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.comm; 2 | 3 | import com.aliyun.mq.http.common.http.RequestMessage; 4 | import com.aliyun.mq.http.common.http.ResponseMessage; 5 | 6 | public abstract class RetryStrategy { 7 | 8 | private static final int DEFAULT_RETRY_PAUSE_SCALE = 300; // milliseconds. 9 | 10 | public abstract boolean shouldRetry(Exception ex, RequestMessage request, ResponseMessage response, int retries); 11 | 12 | public long getPauseDelay(int retries) { 13 | // make the pause time increase exponentially 14 | // based on an assumption that the more times it retries, 15 | // the less probability it succeeds. 16 | int scale = DEFAULT_RETRY_PAUSE_SCALE; 17 | long delay = (long) Math.pow(2, retries) * scale; 18 | 19 | return delay; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/ClientConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import com.aliyun.mq.http.common.utils.VersionInfoUtils; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | public class ClientConfiguration implements Cloneable { 7 | 8 | private static final String DEFAULT_USER_AGENT = VersionInfoUtils.getDefaultUserAgent(); 9 | private String userAgent = DEFAULT_USER_AGENT; 10 | private static final int DEFAULT_MAX_RETRIES = 3; 11 | private int maxErrorRetry = DEFAULT_MAX_RETRIES; 12 | private String proxyHost; 13 | private int proxyPort; 14 | private String proxyUsername; 15 | private String proxyPassword; 16 | private String proxyDomain; 17 | private String proxyWorkstation; 18 | private int maxConnections = 4000; 19 | private int maxConnectionsPerRoute = 4000; 20 | private int socketTimeout = 33 * 1000; 21 | private int connectionTimeout = 30 * 1000; 22 | private boolean soKeepAlive = true; 23 | private int ioReactorThreadCount = Runtime.getRuntime().availableProcessors(); 24 | 25 | private boolean exceptContinue = true; 26 | 27 | @Override 28 | protected Object clone() throws CloneNotSupportedException { 29 | return super.clone(); 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | HashCodeBuilder hcb = new HashCodeBuilder(); 35 | hcb.append(maxConnections); 36 | hcb.append(maxConnectionsPerRoute); 37 | hcb.append(socketTimeout); 38 | hcb.append(connectionTimeout); 39 | hcb.append(soKeepAlive); 40 | hcb.append(exceptContinue); 41 | hcb.append(proxyPort); 42 | hcb.append(proxyHost); 43 | hcb.append(proxyUsername); 44 | hcb.append(proxyPassword); 45 | hcb.append(proxyDomain); 46 | hcb.append(proxyWorkstation); 47 | return hcb.build(); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object obj) { 52 | if (obj instanceof ClientConfiguration) { 53 | ClientConfiguration conf = (ClientConfiguration) obj; 54 | return maxConnections == conf.maxConnections 55 | && maxConnectionsPerRoute == conf.maxConnectionsPerRoute 56 | && socketTimeout == conf.socketTimeout 57 | && connectionTimeout == conf.connectionTimeout 58 | && proxyPort == conf.proxyPort 59 | && proxyHost == null ? conf.proxyHost == null : proxyHost.equals(conf.proxyHost) 60 | && proxyUsername == null ? conf.proxyUsername == null : proxyUsername.equals(conf.proxyUsername) 61 | && proxyPassword == null ? conf.proxyPassword == null : proxyPassword.equals(conf.proxyPassword) 62 | && proxyDomain == null ? conf.proxyDomain == null : proxyDomain.equals(conf.proxyDomain) 63 | && proxyWorkstation == null ? conf.proxyWorkstation == null : proxyWorkstation.equals(conf.proxyWorkstation) 64 | && soKeepAlive == conf.soKeepAlive 65 | && exceptContinue == conf.exceptContinue; 66 | } 67 | return super.equals(obj); 68 | } 69 | 70 | /** 71 | * 构造新实例。 72 | */ 73 | public ClientConfiguration() { 74 | } 75 | 76 | /** 77 | * 构造用户代理。 78 | * 79 | * @return 用户代理。 80 | */ 81 | public String getUserAgent() { 82 | return userAgent; 83 | } 84 | 85 | /** 86 | * 设置用户代理。 87 | * 88 | * @param userAgent 用户代理。 89 | */ 90 | public void setUserAgent(String userAgent) { 91 | this.userAgent = userAgent; 92 | } 93 | 94 | /** 95 | * 返回代理服务器主机地址。 96 | * 97 | * @return 代理服务器主机地址。 98 | */ 99 | public String getProxyHost() { 100 | return proxyHost; 101 | } 102 | 103 | /** 104 | * 设置代理服务器主机地址。 105 | * 106 | * @param proxyHost 代理服务器主机地址。 107 | */ 108 | public void setProxyHost(String proxyHost) { 109 | this.proxyHost = proxyHost; 110 | } 111 | 112 | /** 113 | * 返回代理服务器端口。 114 | * 115 | * @return 代理服务器端口。 116 | */ 117 | public int getProxyPort() { 118 | return proxyPort; 119 | } 120 | 121 | /** 122 | * 设置代理服务器端口。 123 | * 124 | * @param proxyPort 代理服务器端口。 125 | */ 126 | public void setProxyPort(int proxyPort) { 127 | this.proxyPort = proxyPort; 128 | } 129 | 130 | /** 131 | * 返回代理服务器验证的用户名。 132 | * 133 | * @return 用户名。 134 | */ 135 | public String getProxyUsername() { 136 | return proxyUsername; 137 | } 138 | 139 | /** 140 | * 设置代理服务器验证的用户名。 141 | * 142 | * @param proxyUsername 用户名。 143 | */ 144 | public void setProxyUsername(String proxyUsername) { 145 | this.proxyUsername = proxyUsername; 146 | } 147 | 148 | /** 149 | * 返回代理服务器验证的密码。 150 | * 151 | * @return 密码。 152 | */ 153 | public String getProxyPassword() { 154 | return proxyPassword; 155 | } 156 | 157 | /** 158 | * 设置代理服务器验证的密码。 159 | * 160 | * @param proxyPassword 密码。 161 | */ 162 | public void setProxyPassword(String proxyPassword) { 163 | this.proxyPassword = proxyPassword; 164 | } 165 | 166 | /** 167 | * 返回访问NTLM验证的代理服务器的Windows域名(可选)。 168 | * 169 | * @return 域名。 170 | */ 171 | public String getProxyDomain() { 172 | return proxyDomain; 173 | } 174 | 175 | /** 176 | * 设置访问NTLM验证的代理服务器的Windows域名(可选)。 177 | * 178 | * @param proxyDomain 域名。 179 | */ 180 | public void setProxyDomain(String proxyDomain) { 181 | this.proxyDomain = proxyDomain; 182 | } 183 | 184 | /** 185 | * 返回NTLM代理服务器的Windows工作站名称。 186 | * 187 | * @return NTLM代理服务器的Windows工作站名称。 188 | */ 189 | public String getProxyWorkstation() { 190 | return proxyWorkstation; 191 | } 192 | 193 | /** 194 | * 设置NTLM代理服务器的Windows工作站名称。 195 | * (可选,如果代理服务器非NTLM,不需要设置该参数)。 196 | * 197 | * @param proxyWorkstation NTLM代理服务器的Windows工作站名称。 198 | */ 199 | public void setProxyWorkstation(String proxyWorkstation) { 200 | this.proxyWorkstation = proxyWorkstation; 201 | } 202 | 203 | /** 204 | * 返回允许打开的最大HTTP连接数。 205 | * 206 | * @return 最大HTTP连接数。 207 | */ 208 | public int getMaxConnections() { 209 | return maxConnections; 210 | } 211 | 212 | /** 213 | * 设置允许打开的最大HTTP连接数。 214 | * 215 | * @param maxConnections 最大HTTP连接数。 216 | */ 217 | public void setMaxConnections(int maxConnections) { 218 | this.maxConnections = maxConnections; 219 | } 220 | 221 | /** 222 | * 返回通过打开的连接传输数据的超时时间(单位:毫秒)。 223 | * 0表示无限等待(但不推荐使用)。 224 | * 225 | * @return 通过打开的连接传输数据的超时时间(单位:毫秒)。 226 | */ 227 | public int getSocketTimeout() { 228 | return socketTimeout; 229 | } 230 | 231 | /** 232 | * 设置通过打开的连接传输数据的超时时间(单位:毫秒)。 233 | * 0表示无限等待(但不推荐使用)。 234 | * 235 | * @param socketTimeout 通过打开的连接传输数据的超时时间(单位:毫秒)。 236 | */ 237 | public void setSocketTimeout(int socketTimeout) { 238 | this.socketTimeout = socketTimeout; 239 | } 240 | 241 | /** 242 | * 返回建立连接的超时时间(单位:毫秒)。 243 | * 244 | * @return 建立连接的超时时间(单位:毫秒)。 245 | */ 246 | public int getConnectionTimeout() { 247 | return connectionTimeout; 248 | } 249 | 250 | /** 251 | * 设置建立连接的超时时间(单位:毫秒)。 252 | * 253 | * @param connectionTimeout 建立连接的超时时间(单位:毫秒)。 254 | */ 255 | public void setConnectionTimeout(int connectionTimeout) { 256 | this.connectionTimeout = connectionTimeout; 257 | } 258 | 259 | /** 260 | * 返回一个值表示当可重试的请求失败后最大的重试次数。(默认值为3) 261 | * 262 | * @return 当可重试的请求失败后最大的重试次数。 263 | */ 264 | public int getMaxErrorRetry() { 265 | return maxErrorRetry; 266 | } 267 | 268 | /** 269 | * 设置一个值表示当可重试的请求失败后最大的重试次数。(默认值为3) 270 | * 271 | * @param maxErrorRetry 当可重试的请求失败后最大的重试次数。 272 | */ 273 | public void setMaxErrorRetry(int maxErrorRetry) { 274 | this.maxErrorRetry = maxErrorRetry; 275 | } 276 | 277 | public boolean isSoKeepAlive() { 278 | return soKeepAlive; 279 | } 280 | 281 | public void setSoKeepAlive(boolean soKeepAlive) { 282 | this.soKeepAlive = soKeepAlive; 283 | } 284 | 285 | public boolean isExceptContinue() { 286 | return exceptContinue; 287 | } 288 | 289 | public void setExceptContinue(boolean exceptContinue) { 290 | this.exceptContinue = exceptContinue; 291 | } 292 | 293 | public int getMaxConnectionsPerRoute() { 294 | return maxConnectionsPerRoute; 295 | } 296 | 297 | public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) { 298 | this.maxConnectionsPerRoute = maxConnectionsPerRoute; 299 | } 300 | 301 | public int getIoReactorThreadCount() { 302 | return ioReactorThreadCount; 303 | } 304 | 305 | public void setIoReactorThreadCount(int ioReactorThreadCount) { 306 | this.ioReactorThreadCount = ioReactorThreadCount; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/DefaultServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import com.aliyun.mq.http.common.ClientErrorCode; 4 | import com.aliyun.mq.http.common.ClientException; 5 | import com.aliyun.mq.http.common.comm.RetryStrategy; 6 | import com.aliyun.mq.http.common.comm.ExecutionContext; 7 | import org.apache.http.HttpResponse; 8 | import org.apache.http.HttpStatus; 9 | import org.apache.http.client.methods.HttpRequestBase; 10 | import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; 11 | import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; 12 | import org.apache.http.nio.client.HttpAsyncClient; 13 | 14 | import java.io.IOException; 15 | import java.util.TimerTask; 16 | import java.util.concurrent.*; 17 | import java.util.concurrent.atomic.AtomicBoolean; 18 | import java.util.concurrent.atomic.AtomicInteger; 19 | 20 | /** 21 | * The default implementation of ServiceClient. 22 | */ 23 | public class DefaultServiceClient extends ServiceClient { 24 | 25 | AtomicBoolean clientIsOpen = new AtomicBoolean(false); 26 | private HttpAsyncClient httpClient; 27 | private PoolingNHttpClientConnectionManager connManager; 28 | 29 | private AtomicInteger refCount = new AtomicInteger(0); 30 | 31 | private ScheduledExecutorService timer = new ScheduledThreadPoolExecutor(1, new ThreadFactory() { 32 | @Override 33 | public Thread newThread(Runnable r) { 34 | return new Thread(r, "CheckApacheClientTimer"); 35 | } 36 | }); 37 | 38 | // this constructor in package visible 39 | DefaultServiceClient(ClientConfiguration config) { 40 | super(config); 41 | connManager = HttpFactory.createConnectionManager(config); 42 | httpClient = HttpFactory.createHttpAsyncClient(connManager, config); 43 | this.ref(); 44 | } 45 | 46 | @Override 47 | int ref() { 48 | this.open(); 49 | return refCount.incrementAndGet(); 50 | } 51 | 52 | @Override 53 | int unRef() { 54 | if (refCount.decrementAndGet() <= 0) { 55 | this.close(); 56 | } 57 | return refCount.get(); 58 | } 59 | 60 | @Override 61 | public Future sendRequestCore( 62 | ServiceClient.Request request, ExecutionContext context, 63 | HttpCallback callback) throws IOException { 64 | assert request != null && context != null; 65 | 66 | if (!isOpen()) { 67 | throw new IOException("Http selector is not running, try it again after 3s."); 68 | } 69 | 70 | HttpRequestBase httpRequest = HttpFactory.createHttpRequest(request, context); 71 | 72 | return httpClient.execute(httpRequest, callback); 73 | } 74 | 75 | private void open() { 76 | if (this.httpClient != null 77 | && this.httpClient instanceof CloseableHttpAsyncClient 78 | && clientIsOpen.compareAndSet(false, true)) { 79 | ((CloseableHttpAsyncClient) httpClient).start(); 80 | // start a thread to clean idle and expired connection 81 | HttpFactory.IdleConnectionMonitor.getInstance().addConnMgr(connManager); 82 | } 83 | 84 | this.timer.scheduleAtFixedRate(new TimerTask() { 85 | @Override 86 | public void run() { 87 | try { 88 | if (refCount.get() > 0 && clientIsOpen.get()) { 89 | // selector thread may be stopped because of unknown cases. 90 | // this is to re create it & start it. 91 | if (!isSelectorOk()) { 92 | try { 93 | connManager.shutdown(); 94 | if (httpClient instanceof CloseableHttpAsyncClient) { 95 | ((CloseableHttpAsyncClient) httpClient).close(); 96 | } 97 | HttpFactory.IdleConnectionMonitor.getInstance().removeConnMgr(connManager); 98 | } catch (Throwable e) { 99 | } 100 | 101 | connManager = HttpFactory.createConnectionManager(getClientConfigurationNoClone()); 102 | httpClient = HttpFactory.createHttpAsyncClient(connManager, getClientConfigurationNoClone()); 103 | 104 | ((CloseableHttpAsyncClient) httpClient).start(); 105 | // start a thread to clean idle and expired connection 106 | HttpFactory.IdleConnectionMonitor.getInstance().addConnMgr(connManager); 107 | } 108 | 109 | } 110 | } catch (Throwable e) { 111 | } 112 | } 113 | }, 10 * 1000, 3 * 1000, TimeUnit.MILLISECONDS); 114 | } 115 | 116 | @Override 117 | public boolean isOpen() { 118 | return clientIsOpen.get() && isSelectorOk(); 119 | } 120 | 121 | @Override 122 | protected boolean isSelectorOk() { 123 | if (this.httpClient instanceof CloseableHttpAsyncClient) { 124 | return ((CloseableHttpAsyncClient) httpClient).isRunning(); 125 | } 126 | return true; 127 | } 128 | 129 | @Override 130 | protected void close() { 131 | HttpFactory.IdleConnectionMonitor.getInstance().removeConnMgr(connManager); 132 | if (this.httpClient != null 133 | && this.httpClient instanceof CloseableHttpAsyncClient 134 | && clientIsOpen.compareAndSet(true, false)) { 135 | try { 136 | ((CloseableHttpAsyncClient) httpClient).close(); 137 | this.timer.shutdownNow(); 138 | } catch (IOException e) { // quietly 139 | } 140 | } 141 | } 142 | 143 | protected RetryStrategy getDefaultRetryStrategy() { 144 | return new DefaultRetryStrategy(); 145 | } 146 | 147 | private static class DefaultRetryStrategy extends RetryStrategy { 148 | 149 | @Override 150 | public boolean shouldRetry(Exception ex, RequestMessage request, 151 | ResponseMessage response, int retries) { 152 | if (ex instanceof ClientException) { 153 | String errorCode = ((ClientException) ex).getErrorCode(); 154 | if (errorCode.equals(ClientErrorCode.CONNECTION_TIMEOUT) 155 | || errorCode.equals(ClientErrorCode.SOCKET_TIMEOUT)) { 156 | return true; 157 | } 158 | } 159 | 160 | if (response != null) { 161 | int statusCode = response.getStatusCode(); 162 | if (statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR 163 | || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE) { 164 | return true; 165 | } 166 | } 167 | 168 | return false; 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/ExceptionResultParser.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import java.io.IOException; 4 | 5 | import com.aliyun.mq.http.common.ServiceException; 6 | import com.aliyun.mq.http.common.parser.JAXBResultParser; 7 | import com.aliyun.mq.http.common.parser.ResultParseException; 8 | import com.aliyun.mq.http.common.parser.ResultParser; 9 | import com.aliyun.mq.http.common.utils.IOUtils; 10 | import com.aliyun.mq.http.model.ErrorMessage; 11 | 12 | public class ExceptionResultParser implements ResultParser { 13 | private String userRequestId; 14 | 15 | public ExceptionResultParser(String userRequestId) { 16 | super(); 17 | this.userRequestId = userRequestId; 18 | } 19 | 20 | @Override 21 | public Exception parse(ResponseMessage response) throws ResultParseException { 22 | assert response != null; 23 | 24 | if (response.isSuccessful()) { 25 | return null; 26 | } 27 | 28 | ServiceException result = null; 29 | String content = null; 30 | try { 31 | content = IOUtils.readStreamAsString(response.getContent(), "UTF-8"); 32 | } catch (IOException e) { 33 | return new ServiceException(e.getMessage(), userRequestId, e); 34 | } 35 | 36 | try { 37 | // 使用jaxb common parser 38 | JAXBResultParser d = new JAXBResultParser(ErrorMessage.class); 39 | Object obj = d.parse(content); 40 | if (obj instanceof ErrorMessage) { 41 | ErrorMessage err = (ErrorMessage) obj; 42 | result = new ServiceException(err.Message, null, err.Code, err.RequestId, err.HostId); 43 | } 44 | } catch (Exception e) { 45 | // now treat it as unknown formats 46 | result = new ServiceException(content, null, "InternalServerError", null, null); 47 | } 48 | 49 | return result; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/HttpCallback.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import com.aliyun.mq.http.model.AsyncCallback; 4 | import com.aliyun.mq.http.model.AsyncResult; 5 | import com.aliyun.mq.http.common.ClientException; 6 | import com.aliyun.mq.http.common.parser.ResultParser; 7 | import com.aliyun.mq.http.common.utils.HttpUtil; 8 | import com.aliyun.mq.http.common.utils.IOUtils; 9 | import org.apache.http.Header; 10 | import org.apache.http.HttpResponse; 11 | import org.apache.http.concurrent.FutureCallback; 12 | 13 | import java.io.IOException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.concurrent.*; 17 | import java.util.concurrent.locks.Condition; 18 | import java.util.concurrent.locks.ReentrantLock; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | public class HttpCallback implements FutureCallback { 23 | private static Logger log = LoggerFactory.getLogger(HttpCallback.class); 24 | private boolean success = false; 25 | private Exception exception = null; 26 | private ResponseMessage responseMessage = null; 27 | private boolean cancalled = false; 28 | private ResultParser resultParser; 29 | private AsyncCallback callback; 30 | private DefaultAsyncResult result; 31 | private ResultParser exceptionParser; 32 | private static ExecutorService executor; 33 | 34 | static { 35 | executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 36 | ((ThreadPoolExecutor) executor).setKeepAliveTime(30, TimeUnit.SECONDS); 37 | ((ThreadPoolExecutor) executor).allowCoreThreadTimeOut(true); 38 | } 39 | 40 | public static void setCallbackExecutor(ExecutorService executor) { 41 | HttpCallback.executor = executor; 42 | } 43 | 44 | public HttpCallback(ResultParser resultParser, 45 | ResultParser exceptionParser, 46 | AsyncCallback callback) { 47 | this.resultParser = resultParser; 48 | this.callback = callback; 49 | this.exceptionParser = exceptionParser; 50 | this.result = new DefaultAsyncResult(this); 51 | } 52 | 53 | private void executeCallback(final AsyncCallback callback, final T result) { 54 | executor.submit(new Runnable() { 55 | @Override 56 | public void run() { 57 | callback.onSuccess(result); 58 | } 59 | }); 60 | } 61 | 62 | private void executeCallback(final AsyncCallback callback, final Exception ex) { 63 | executor.submit(new Runnable() { 64 | @Override 65 | public void run() { 66 | callback.onFail(ex); 67 | } 68 | }); 69 | } 70 | 71 | @Override 72 | public void completed(HttpResponse response) { 73 | try { 74 | buildResponseMessage(response); 75 | } catch (Exception ex) { 76 | log.error(ex.getMessage()); 77 | if (callback != null) { 78 | executeCallback(callback, ex); 79 | } 80 | result.onFail(ex); 81 | log.error("onFail finish when exception in completed"); 82 | } 83 | } 84 | 85 | protected void buildResponseMessage(HttpResponse response) { 86 | // Build result 87 | responseMessage = new ResponseMessage(); 88 | // message.setUrl(request.getUri()); 89 | if (response.getStatusLine() != null) { 90 | responseMessage.setStatusCode(response.getStatusLine() 91 | .getStatusCode()); 92 | } 93 | 94 | if (response.getEntity() != null) { 95 | try { 96 | responseMessage.setContent(response.getEntity().getContent()); 97 | } catch (IllegalStateException e) { 98 | log.error(e.getMessage(), e); 99 | } catch (IOException e) { 100 | log.error(e.getMessage(), e); 101 | } 102 | } 103 | // fill in headers 104 | Header[] headers = response.getAllHeaders(); 105 | Map resultHeaders = new HashMap(); 106 | for (int i = 0; i < headers.length; i++) { 107 | Header h = headers[i]; 108 | resultHeaders.put(h.getName(), h.getValue()); 109 | } 110 | HttpUtil.convertHeaderCharsetFromIso88591(resultHeaders); 111 | responseMessage.setHeaders(resultHeaders); 112 | 113 | handleResult(); 114 | } 115 | 116 | private void close() { 117 | if (responseMessage != null) { 118 | try { 119 | this.responseMessage.close(); 120 | } catch (IOException e) { 121 | } 122 | } 123 | 124 | } 125 | 126 | private void handleResult() { 127 | try { 128 | if (responseMessage.isSuccessful()) { 129 | T obj = null; 130 | if (resultParser != null) { 131 | obj = this.resultParser.parse(responseMessage); 132 | } 133 | if (callback != null) { 134 | executeCallback(callback, obj); 135 | } 136 | 137 | result.onSuccess(obj); 138 | this.success = true; 139 | } else { 140 | Exception obj = exceptionParser.parse(responseMessage); 141 | if (callback != null) { 142 | executeCallback(callback, obj); 143 | } 144 | 145 | result.onFail(obj); 146 | 147 | } 148 | } catch (Exception ex) { 149 | try { 150 | System.out.println(IOUtils.readStreamAsString(responseMessage.getContent(), "UTF-8")); 151 | } catch (Exception e) { 152 | } 153 | if (callback != null) { 154 | executeCallback(callback, ex); 155 | } 156 | result.onFail(ex); 157 | } 158 | 159 | } 160 | 161 | @Override 162 | public void failed(Exception ex) { 163 | this.exception = ex; 164 | try { 165 | if (callback != null) { 166 | executeCallback(callback, ex); 167 | } 168 | result.onFail(ex); 169 | } catch (Exception e) { 170 | if (callback != null) { 171 | executeCallback(callback, ex); 172 | } 173 | result.onFail(e); 174 | } 175 | } 176 | 177 | @Override 178 | public void cancelled() { 179 | this.cancalled = true; 180 | exception = new ClientException("call is cancelled.", null); 181 | try { 182 | if (callback != null) { 183 | executeCallback(callback, exception); 184 | } 185 | result.onFail(exception); 186 | } catch (Exception e) { 187 | if (callback != null) { 188 | executeCallback(callback, e); 189 | } 190 | result.onFail(e); 191 | } 192 | } 193 | 194 | public boolean isCancelled() { 195 | return cancalled; 196 | } 197 | 198 | public boolean isSuccess() { 199 | return success; 200 | } 201 | 202 | public Exception getException() { 203 | return this.exception; 204 | } 205 | 206 | public ResponseMessage getResponseMessage() { 207 | return responseMessage; 208 | } 209 | 210 | public AsyncResult getAsyncResult() { 211 | return this.result; 212 | } 213 | 214 | static class DefaultAsyncResult implements AsyncResult { 215 | private ReentrantLock rlock = new ReentrantLock(); 216 | private Condition lock = rlock.newCondition(); 217 | 218 | private long defaultTimewait; 219 | private long startTimeMillis; 220 | 221 | private boolean completed = false; 222 | private T result = null; 223 | private boolean success; 224 | private Exception exception; 225 | 226 | private HttpCallback callback; 227 | private Future future; 228 | 229 | protected DefaultAsyncResult(HttpCallback callback) { 230 | this.callback = callback; 231 | this.startTimeMillis = System.currentTimeMillis(); 232 | } 233 | 234 | public void setTimewait(long timewait) { 235 | defaultTimewait = timewait; 236 | } 237 | 238 | public void setFuture(Future future) { 239 | this.future = future; 240 | } 241 | 242 | /* 243 | * (non-Javadoc) 244 | * 245 | */ 246 | @Override 247 | public T getResult() { 248 | T result = getResult(defaultTimewait); 249 | while (result == null && (this.future != null && !this.future.isDone())) { 250 | result = getResult(defaultTimewait); 251 | } 252 | return result; 253 | } 254 | 255 | /* 256 | * (non-Javadoc) 257 | * 258 | */ 259 | @Override 260 | public T getResult(long timewait) { 261 | if (!completed) { 262 | try { 263 | rlock.lock(); 264 | if (!completed) { 265 | boolean signaled = false; 266 | if (timewait <= 0) { 267 | signaled = lock.await(defaultTimewait, TimeUnit.MILLISECONDS); 268 | } else { 269 | signaled = lock.await(timewait, TimeUnit.MILLISECONDS); 270 | } 271 | if (!signaled 272 | && (this.future != null && this.future.isDone()) 273 | && System.currentTimeMillis() >= (startTimeMillis + defaultTimewait)) { 274 | this.exception = new ClientException("Client wait result timeout!", null); 275 | this.success = false; 276 | this.completed = true; 277 | } 278 | } 279 | } catch (InterruptedException e) { 280 | throw new RuntimeException(e); 281 | } finally { 282 | rlock.unlock(); 283 | } 284 | } 285 | return result; 286 | } 287 | 288 | public void onSuccess(T result) { 289 | try { 290 | rlock.lock(); 291 | if (completed) { 292 | return; 293 | } 294 | this.result = result; 295 | this.success = true; 296 | this.completed = true; 297 | lock.signal(); 298 | } finally { 299 | rlock.unlock(); 300 | } 301 | } 302 | 303 | public void onFail(Exception ex) { 304 | try { 305 | rlock.lock(); 306 | if (completed) { 307 | return; 308 | } 309 | this.exception = ex; 310 | this.success = false; 311 | this.completed = true; 312 | lock.signal(); 313 | } finally { 314 | rlock.unlock(); 315 | } 316 | } 317 | 318 | @Override 319 | public boolean isSuccess() { 320 | return success; 321 | } 322 | 323 | @Override 324 | public Exception getException() { 325 | return exception; 326 | } 327 | 328 | //TODO: erase it ? 329 | public void close() { 330 | callback.close(); 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/HttpClientConfig.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import org.apache.http.*; 4 | import org.apache.http.client.CookieStore; 5 | import org.apache.http.client.CredentialsProvider; 6 | import org.apache.http.client.config.AuthSchemes; 7 | import org.apache.http.client.config.CookieSpecs; 8 | import org.apache.http.client.config.RequestConfig; 9 | import org.apache.http.config.ConnectionConfig; 10 | import org.apache.http.config.MessageConstraints; 11 | import org.apache.http.config.Registry; 12 | import org.apache.http.config.RegistryBuilder; 13 | import org.apache.http.conn.DnsResolver; 14 | import org.apache.http.conn.routing.HttpRoute; 15 | import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; 16 | import org.apache.http.conn.ssl.SSLContexts; 17 | import org.apache.http.conn.ssl.X509HostnameVerifier; 18 | import org.apache.http.impl.DefaultHttpResponseFactory; 19 | import org.apache.http.impl.client.BasicCookieStore; 20 | import org.apache.http.impl.client.BasicCredentialsProvider; 21 | import org.apache.http.impl.conn.SystemDefaultDnsResolver; 22 | import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; 23 | import org.apache.http.impl.nio.client.HttpAsyncClients; 24 | import org.apache.http.impl.nio.codecs.DefaultHttpRequestWriterFactory; 25 | import org.apache.http.impl.nio.codecs.DefaultHttpResponseParser; 26 | import org.apache.http.impl.nio.codecs.DefaultHttpResponseParserFactory; 27 | import org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionFactory; 28 | import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; 29 | import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; 30 | import org.apache.http.impl.nio.reactor.IOReactorConfig; 31 | import org.apache.http.message.BasicHeader; 32 | import org.apache.http.message.BasicLineParser; 33 | import org.apache.http.message.LineParser; 34 | import org.apache.http.nio.NHttpMessageParser; 35 | import org.apache.http.nio.NHttpMessageParserFactory; 36 | import org.apache.http.nio.NHttpMessageWriterFactory; 37 | import org.apache.http.nio.conn.ManagedNHttpClientConnection; 38 | import org.apache.http.nio.conn.NHttpConnectionFactory; 39 | import org.apache.http.nio.conn.NoopIOSessionStrategy; 40 | import org.apache.http.nio.conn.SchemeIOSessionStrategy; 41 | import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; 42 | import org.apache.http.nio.reactor.ConnectingIOReactor; 43 | import org.apache.http.nio.reactor.IOReactorException; 44 | import org.apache.http.nio.reactor.SessionInputBuffer; 45 | import org.apache.http.nio.util.HeapByteBufferAllocator; 46 | import org.apache.http.util.CharArrayBuffer; 47 | 48 | import javax.net.ssl.SSLContext; 49 | import java.net.InetAddress; 50 | import java.net.UnknownHostException; 51 | import java.nio.charset.CodingErrorAction; 52 | import java.util.Arrays; 53 | 54 | public class HttpClientConfig { 55 | 56 | public HttpClientConfig() throws IOReactorException { 57 | 58 | // Use custom message parser / writer to customize the way HTTP 59 | // messages are parsed from and written out to the data stream. 60 | NHttpMessageParserFactory responseParserFactory = new DefaultHttpResponseParserFactory() { 61 | 62 | @Override 63 | public NHttpMessageParser create( 64 | final SessionInputBuffer buffer, 65 | final MessageConstraints constraints) { 66 | LineParser lineParser = new BasicLineParser() { 67 | 68 | @Override 69 | public Header parseHeader(final CharArrayBuffer buffer) { 70 | try { 71 | return super.parseHeader(buffer); 72 | } catch (ParseException ex) { 73 | return new BasicHeader(buffer.toString(), null); 74 | } 75 | } 76 | 77 | }; 78 | return new DefaultHttpResponseParser(buffer, lineParser, 79 | DefaultHttpResponseFactory.INSTANCE, constraints); 80 | } 81 | 82 | }; 83 | NHttpMessageWriterFactory requestWriterFactory = new DefaultHttpRequestWriterFactory(); 84 | 85 | // Use a custom connection factory to customize the process of 86 | // initialization of outgoing HTTP connections. Beside standard 87 | // connection 88 | // configuration parameters HTTP connection factory can define message 89 | // parser / writer routines to be employed by individual connections. 90 | NHttpConnectionFactory connFactory = new ManagedNHttpClientConnectionFactory( 91 | requestWriterFactory, responseParserFactory, 92 | HeapByteBufferAllocator.INSTANCE); 93 | 94 | // Client HTTP connection objects when fully initialized can be bound to 95 | // an arbitrary network socket. The process of network socket 96 | // initialization, 97 | // its connection to a remote address and binding to a local one is 98 | // controlled 99 | // by a connection socket factory. 100 | 101 | // SSL context for secure connections can be created either based on 102 | // system or application specific properties. 103 | SSLContext sslcontext = SSLContexts.createSystemDefault(); 104 | // Use custom hostname verifier to customize SSL hostname verification. 105 | X509HostnameVerifier hostnameVerifier = new BrowserCompatHostnameVerifier(); 106 | 107 | // Create a registry of custom connection session strategies for 108 | // supported 109 | // protocol schemes. 110 | Registry sessionStrategyRegistry = RegistryBuilder 111 | .create() 112 | .register("http", NoopIOSessionStrategy.INSTANCE) 113 | .register("https", 114 | new SSLIOSessionStrategy(sslcontext, hostnameVerifier)) 115 | .build(); 116 | 117 | // Use custom DNS resolver to override the system DNS resolution. 118 | DnsResolver dnsResolver = new SystemDefaultDnsResolver() { 119 | 120 | @Override 121 | public InetAddress[] resolve(final String host) 122 | throws UnknownHostException { 123 | if (host.equalsIgnoreCase("myhost")) { 124 | return new InetAddress[]{InetAddress 125 | .getByAddress(new byte[]{127, 0, 0, 1})}; 126 | } else { 127 | return super.resolve(host); 128 | } 129 | } 130 | 131 | }; 132 | 133 | // Create I/O reactor configuration 134 | IOReactorConfig ioReactorConfig = IOReactorConfig.custom() 135 | .setIoThreadCount(Runtime.getRuntime().availableProcessors()) 136 | .setConnectTimeout(30000).setSoTimeout(30000).build(); 137 | 138 | // Create a custom I/O reactort 139 | ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor( 140 | ioReactorConfig); 141 | 142 | // Create a connection manager with custom configuration. 143 | PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager( 144 | ioReactor, connFactory, sessionStrategyRegistry, dnsResolver); 145 | 146 | // Create message constraints 147 | MessageConstraints messageConstraints = MessageConstraints.custom() 148 | .setMaxHeaderCount(200).setMaxLineLength(2000).build(); 149 | // Create connection configuration 150 | ConnectionConfig connectionConfig = ConnectionConfig.custom() 151 | .setMalformedInputAction(CodingErrorAction.IGNORE) 152 | .setUnmappableInputAction(CodingErrorAction.IGNORE) 153 | .setCharset(Consts.UTF_8) 154 | .setMessageConstraints(messageConstraints).build(); 155 | // Configure the connection manager to use connection configuration 156 | // either 157 | // by default or for a specific host. 158 | connManager.setDefaultConnectionConfig(connectionConfig); 159 | connManager.setConnectionConfig(new HttpHost("somehost", 80), 160 | ConnectionConfig.DEFAULT); 161 | 162 | // Configure total max or per route limits for persistent connections 163 | // that can be kept in the pool or leased by the connection manager. 164 | connManager.setMaxTotal(100); 165 | connManager.setDefaultMaxPerRoute(10); 166 | connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 167 | 20); 168 | 169 | // Use custom cookie store if necessary. 170 | CookieStore cookieStore = new BasicCookieStore(); 171 | // Use custom credentials provider if necessary. 172 | CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 173 | // Create global request configuration 174 | RequestConfig defaultRequestConfig = RequestConfig 175 | .custom() 176 | .setCookieSpec(CookieSpecs.BEST_MATCH) 177 | .setExpectContinueEnabled(true) 178 | .setStaleConnectionCheckEnabled(true) 179 | .setTargetPreferredAuthSchemes( 180 | Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST)) 181 | .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)) 182 | .build(); 183 | 184 | // Create an HttpClient with the given custom dependencies and 185 | // configuration. 186 | CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() 187 | .setConnectionManager(connManager) 188 | .setDefaultCookieStore(cookieStore) 189 | .setDefaultCredentialsProvider(credentialsProvider) 190 | .setProxy(new HttpHost("myproxy", 8080)) 191 | .setDefaultRequestConfig(defaultRequestConfig).build(); 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/HttpDeleteWrapper.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import org.apache.http.annotation.NotThreadSafe; 4 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 5 | 6 | import java.net.URI; 7 | 8 | @NotThreadSafe 9 | public class HttpDeleteWrapper extends HttpEntityEnclosingRequestBase { 10 | 11 | public final static String METHOD_NAME = "DELETE"; 12 | 13 | 14 | public HttpDeleteWrapper() { 15 | super(); 16 | } 17 | 18 | public HttpDeleteWrapper(final URI uri) { 19 | super(); 20 | setURI(uri); 21 | } 22 | 23 | public HttpDeleteWrapper(final String uri) { 24 | super(); 25 | setURI(URI.create(uri)); 26 | } 27 | 28 | @Override 29 | public String getMethod() { 30 | return METHOD_NAME; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/HttpMesssage.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import com.aliyun.mq.http.common.utils.CaseInsensitiveMap; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.Map; 8 | 9 | /** 10 | * The base class for message of HTTP request and response. 11 | */ 12 | public abstract class HttpMesssage { 13 | 14 | private Map headers = new CaseInsensitiveMap(); 15 | private InputStream content; 16 | private long contentLength; 17 | 18 | protected HttpMesssage() { 19 | super(); 20 | } 21 | 22 | public Map getHeaders() { 23 | return headers; 24 | } 25 | 26 | public void setHeaders(Map headers) { 27 | assert (headers != null); 28 | this.headers = headers; 29 | } 30 | 31 | public void addHeader(String key, String value) { 32 | this.headers.put(key, value); 33 | } 34 | 35 | public String getHeader(String key) { 36 | if (this.headers.containsKey(key)) { 37 | return this.headers.get(key); 38 | } else { 39 | return ""; 40 | } 41 | } 42 | 43 | public InputStream getContent() { 44 | return content; 45 | } 46 | 47 | public void setContent(InputStream content) { 48 | this.content = content; 49 | } 50 | 51 | public long getContentLength() { 52 | return contentLength; 53 | } 54 | 55 | public void setContentLength(long contentLength) { 56 | this.contentLength = contentLength; 57 | } 58 | 59 | public void close() throws IOException { 60 | if (content != null) { 61 | content.close(); 62 | content = null; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/RequestMessage.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import com.aliyun.mq.http.common.HttpMethod; 4 | import com.aliyun.mq.http.common.utils.CodingUtils; 5 | 6 | import java.net.URI; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class RequestMessage extends HttpMesssage { 11 | private HttpMethod method = HttpMethod.GET; // HTTP Method. default GET. 12 | private URI endpoint; 13 | private String resourcePath; 14 | private Map parameters = new HashMap(); 15 | 16 | public RequestMessage() { 17 | } 18 | 19 | /** 20 | * 获取HTTP的请求方法。 21 | * 22 | * @return HTTP的请求方法。 23 | */ 24 | public HttpMethod getMethod() { 25 | return method; 26 | } 27 | 28 | /** 29 | * 设置HTTP的请求方法。 30 | * 31 | * @param method HTTP的请求方法。 32 | */ 33 | public void setMethod(HttpMethod method) { 34 | this.method = method; 35 | } 36 | 37 | /** 38 | * @return the endpoint 39 | */ 40 | public URI getEndpoint() { 41 | return endpoint; 42 | } 43 | 44 | /** 45 | * @param endpoint the endpoint to set 46 | */ 47 | public void setEndpoint(URI endpoint) { 48 | this.endpoint = endpoint; 49 | } 50 | 51 | /** 52 | * @return the resourcePath 53 | */ 54 | public String getResourcePath() { 55 | return resourcePath; 56 | } 57 | 58 | /** 59 | * @param resourcePath the resourcePath to set 60 | */ 61 | public void setResourcePath(String resourcePath) { 62 | this.resourcePath = resourcePath; 63 | } 64 | 65 | /** 66 | * @return the parameters 67 | */ 68 | public Map getParameters() { 69 | return parameters; 70 | } 71 | 72 | /** 73 | * @param parameters the parameters to set 74 | */ 75 | public void setParameters(Map parameters) { 76 | CodingUtils.assertParameterNotNull(parameters, "parameters"); 77 | 78 | this.parameters = parameters; 79 | } 80 | 81 | public void addParameter(String key, String value) { 82 | CodingUtils.assertStringNotNullOrEmpty(key, "key"); 83 | 84 | this.parameters.put(key, value); 85 | } 86 | 87 | public void removeParameter(String key) { 88 | CodingUtils.assertStringNotNullOrEmpty(key, "key"); 89 | 90 | this.parameters.remove(key); 91 | } 92 | 93 | /** 94 | * Whether or not the request can be repeatedly sent. 95 | * 96 | * @return isRepeatable 97 | */ 98 | public boolean isRepeatable() { 99 | return this.getContent() == null || this.getContent().markSupported(); 100 | } 101 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/ResponseMessage.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | /** 4 | * 表示返回结果的信息。 5 | */ 6 | public class ResponseMessage extends HttpMesssage { 7 | private static final int HTTP_SUCCESS_STATUS_CODE = 200; 8 | private String uri; 9 | private int statusCode; 10 | 11 | /** 12 | * 构造函数。 13 | */ 14 | public ResponseMessage() { 15 | } 16 | 17 | public String getUri() { 18 | return uri; 19 | } 20 | 21 | public void setUrl(String uri) { 22 | this.uri = uri; 23 | } 24 | 25 | public int getStatusCode() { 26 | return statusCode; 27 | } 28 | 29 | public void setStatusCode(int statusCode) { 30 | this.statusCode = statusCode; 31 | } 32 | 33 | public boolean isSuccessful() { 34 | return statusCode / 100 == HTTP_SUCCESS_STATUS_CODE / 100; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/ServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.HttpMethod; 5 | import com.aliyun.mq.http.common.ServiceException; 6 | import com.aliyun.mq.http.common.comm.RequestHandler; 7 | import com.aliyun.mq.http.common.comm.RetryStrategy; 8 | import com.aliyun.mq.http.common.utils.HttpUtil; 9 | import com.aliyun.mq.http.common.utils.ResourceManager; 10 | import com.aliyun.mq.http.common.utils.ServiceConstants; 11 | import com.aliyun.mq.http.common.comm.ExecutionContext; 12 | import com.aliyun.mq.http.common.comm.ResponseHandler; 13 | import org.apache.http.HttpResponse; 14 | 15 | import java.io.ByteArrayInputStream; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.UnsupportedEncodingException; 19 | import java.util.List; 20 | import java.util.concurrent.Future; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | public abstract class ServiceClient { 25 | 26 | private static final int DEFAULT_MARK_LIMIT = 1024 * 4; 27 | private static final Logger log = LoggerFactory.getLogger(ServiceClient.class); 28 | private static ResourceManager rm = ResourceManager 29 | .getInstance(ServiceConstants.RESOURCE_NAME_COMMON); 30 | private ClientConfiguration config; 31 | 32 | protected ServiceClient(ClientConfiguration config) { 33 | this.config = config; 34 | } 35 | 36 | public ClientConfiguration getClientConfiguration() { 37 | try { 38 | return (ClientConfiguration) this.config.clone(); 39 | } catch (CloneNotSupportedException ex) { 40 | // this should not happen 41 | return null; 42 | } 43 | } 44 | 45 | ClientConfiguration getClientConfigurationNoClone() { 46 | return this.config; 47 | } 48 | 49 | public Future sendRequest(RequestMessage request, 50 | ExecutionContext context, 51 | HttpCallback callback) { 52 | if (!isOpen()) { 53 | throw new ClientException("Client is already closed!", null); 54 | } 55 | return sendRequestImpl(request, context, callback); 56 | } 57 | 58 | private Future sendRequestImpl(RequestMessage request, 59 | ExecutionContext context, 60 | HttpCallback callback) 61 | throws ClientException, ServiceException { 62 | 63 | RetryStrategy retryStrategy = context.getRetryStrategy() != null ? context 64 | .getRetryStrategy() : this.getDefaultRetryStrategy(); 65 | 66 | // Sign the request if a signer is provided. 67 | if (context.getSigner() != null) { 68 | context.getSigner().sign(request); 69 | } 70 | 71 | int retries = 0; 72 | ResponseMessage response = null; 73 | 74 | InputStream content = request.getContent(); 75 | 76 | if (content != null && content.markSupported()) { 77 | content.mark(DEFAULT_MARK_LIMIT); 78 | } 79 | 80 | while (true) { 81 | try { 82 | if (retries > 0) { 83 | pause(retries, retryStrategy); 84 | if (content != null && content.markSupported()) { 85 | content.reset(); 86 | } 87 | } 88 | Request httpRequest = buildRequest(request, context); 89 | // post process request 90 | handleRequest(httpRequest, context.getResquestHandlers()); 91 | return sendRequestCore(httpRequest, context, callback); 92 | 93 | } catch (ServiceException ex) { 94 | // Notice that the response should not be closed in the 95 | // finally block because if the request is successful, 96 | // the response should be returned to the callers. 97 | closeResponseSilently(response); 98 | if (!shouldRetry(ex, request, response, retries, retryStrategy)) { 99 | throw ex; 100 | } 101 | } catch (ClientException ex) { 102 | // Notice that the response should not be closed in the 103 | // finally block because if the request is successful, 104 | // the response should be returned to the callers. 105 | closeResponseSilently(response); 106 | if (!shouldRetry(ex, request, response, retries, retryStrategy)) { 107 | throw ex; 108 | } 109 | } catch (Exception ex) { 110 | // Notice that the response should not be closed in the 111 | // finally block because if the request is successful, 112 | // the response should be returned to the callers. 113 | closeResponseSilently(response); 114 | throw new ClientException(rm.getFormattedString( 115 | "ConnectionError", ex.getMessage()), null, ex); 116 | } finally { 117 | retries++; 118 | } 119 | } 120 | } 121 | 122 | protected abstract Future sendRequestCore( 123 | Request request, ExecutionContext context, HttpCallback callback) 124 | throws Exception; 125 | 126 | private Request buildRequest(RequestMessage requestMessage, 127 | ExecutionContext context) throws ClientException { 128 | Request request = new Request(); 129 | request.setMethod(requestMessage.getMethod()); 130 | request.setHeaders(requestMessage.getHeaders()); 131 | 132 | // The header must be converted after the request is signed, 133 | // otherwise the signature will be incorrect. 134 | if (request.getHeaders() != null) { 135 | HttpUtil.convertHeaderCharsetToIso88591(request.getHeaders()); 136 | } 137 | 138 | final String delimiter = "/"; 139 | String uri = requestMessage.getEndpoint().toString(); 140 | if (!uri.endsWith(delimiter) 141 | && (requestMessage.getResourcePath() == null || !requestMessage 142 | .getResourcePath().startsWith(delimiter))) { 143 | uri += delimiter; 144 | } 145 | 146 | if (requestMessage.getResourcePath() != null) { 147 | uri += requestMessage.getResourcePath(); 148 | } 149 | 150 | String paramString; 151 | try { 152 | paramString = HttpUtil.paramToQueryString( 153 | requestMessage.getParameters(), context.getCharset()); 154 | } catch (UnsupportedEncodingException e) { 155 | // Assertion error because the caller should guarantee the charset. 156 | throw new AssertionError(rm.getFormattedString("EncodingFailed", 157 | e.getMessage())); 158 | } 159 | /* 160 | * For all non-POST requests, and any POST requests that already have a 161 | * payload, we put the encoded params directly in the URI, otherwise, 162 | * we'll put them in the POST request's payload. 163 | */ 164 | boolean requestHasNoPayload = requestMessage.getContent() != null; 165 | boolean requestIsPost = requestMessage.getMethod() == HttpMethod.POST; 166 | boolean putParamsInUri = !requestIsPost || requestHasNoPayload; 167 | if (paramString != null && putParamsInUri) { 168 | uri += "?" + paramString; 169 | } 170 | 171 | request.setUrl(uri); 172 | 173 | if (requestIsPost && requestMessage.getContent() == null 174 | && paramString != null) { 175 | // Put the param string to the request body if POSTing and 176 | // no content. 177 | try { 178 | byte[] buf = paramString.getBytes(context.getCharset()); 179 | ByteArrayInputStream content = new ByteArrayInputStream(buf); 180 | request.setContent(content); 181 | request.setContentLength(buf.length); 182 | } catch (UnsupportedEncodingException e) { 183 | throw new AssertionError(rm.getFormattedString( 184 | "EncodingFailed", e.getMessage())); 185 | } 186 | } else { 187 | request.setContent(requestMessage.getContent()); 188 | request.setContentLength(requestMessage.getContentLength()); 189 | } 190 | 191 | return request; 192 | } 193 | 194 | private void handleResponse(ResponseMessage response, 195 | List responseHandlers) throws ServiceException, 196 | ClientException { 197 | for (ResponseHandler h : responseHandlers) { 198 | h.handle(response); 199 | } 200 | } 201 | 202 | private void handleRequest(Request message, 203 | List resquestHandlers) throws ServiceException, 204 | ClientException { 205 | for (RequestHandler h : resquestHandlers) { 206 | h.handle(message); 207 | } 208 | 209 | } 210 | 211 | private void pause(int retries, RetryStrategy retryStrategy) 212 | throws ClientException { 213 | 214 | long delay = retryStrategy.getPauseDelay(retries); 215 | 216 | log.debug("Retriable error detected, will retry in " + delay 217 | + "ms, attempt number: " + retries); 218 | 219 | try { 220 | Thread.sleep(delay); 221 | } catch (InterruptedException e) { 222 | throw new ClientException(e.getMessage(), null, e); 223 | } 224 | } 225 | 226 | private boolean shouldRetry(Exception exception, RequestMessage request, 227 | ResponseMessage response, int retries, RetryStrategy retryStrategy) { 228 | 229 | if (retries >= config.getMaxErrorRetry()) { 230 | return false; 231 | } 232 | 233 | if (!request.isRepeatable()) { 234 | return false; 235 | } 236 | 237 | if (retryStrategy.shouldRetry(exception, request, response, retries)) { 238 | log.debug("Retrying on " + exception.getClass().getName() + ": " 239 | + exception.getMessage()); 240 | return true; 241 | } 242 | return false; 243 | } 244 | 245 | private void closeResponseSilently(ResponseMessage response) { 246 | 247 | if (response != null) { 248 | try { 249 | response.close(); 250 | } catch (IOException ioe) { /* silently close the response. */ 251 | } 252 | } 253 | } 254 | 255 | /** 256 | * ref count + 1 257 | * 258 | * @return current value 259 | */ 260 | abstract int ref(); 261 | 262 | /** 263 | * ref count - 1 264 | * 265 | * @return current value 266 | */ 267 | abstract int unRef(); 268 | 269 | protected abstract void close(); 270 | 271 | /** 272 | * check client is opened and running. 273 | * 274 | * @return true of false 275 | */ 276 | public abstract boolean isOpen(); 277 | 278 | /** 279 | * check apache http selector is running. 280 | * 281 | * @return true of false 282 | */ 283 | protected abstract boolean isSelectorOk(); 284 | 285 | /** 286 | * get default retry stg. 287 | * 288 | * @return default retry stg. 289 | */ 290 | protected abstract RetryStrategy getDefaultRetryStrategy(); 291 | 292 | /** 293 | * A wrapper class to HttpMessage. It contains the data to create 294 | * HttpRequestBase, and it is easy for testing to verify the built data such 295 | * as URL, content. 296 | */ 297 | public static class Request extends HttpMesssage { 298 | private String uri; 299 | private HttpMethod method; 300 | 301 | public Request() { 302 | 303 | } 304 | 305 | public String getUri() { 306 | return this.uri; 307 | } 308 | 309 | public void setUrl(String uri) { 310 | this.uri = uri; 311 | } 312 | 313 | /** 314 | * @return the method 315 | */ 316 | public HttpMethod getMethod() { 317 | return method; 318 | } 319 | 320 | /** 321 | * @param method the method to set 322 | */ 323 | public void setMethod(HttpMethod method) { 324 | this.method = method; 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/http/ServiceClientFactory.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.http; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ServiceClientFactory { 7 | 8 | private static final Map serviceClientMap = new HashMap(); 9 | 10 | public static ServiceClient createServiceClient(ClientConfiguration config) { 11 | synchronized (serviceClientMap) { 12 | ServiceClient serviceClient = serviceClientMap.get(config); 13 | if (serviceClient == null) { 14 | serviceClient = new DefaultServiceClient(config); 15 | serviceClientMap.put(config, serviceClient); 16 | } else { 17 | serviceClient.ref(); 18 | } 19 | return serviceClient; 20 | } 21 | } 22 | 23 | public static void closeServiceClient(ServiceClient serviceClient) { 24 | synchronized (serviceClientMap) { 25 | int count = serviceClient.unRef(); 26 | if (count <= 0) { 27 | serviceClientMap.remove(serviceClient.getClientConfigurationNoClone()); 28 | } 29 | } 30 | } 31 | 32 | public static int getServiceClientCount() { 33 | synchronized (serviceClientMap) { 34 | return serviceClientMap.size(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/parser/JAXBResultParser.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.parser; 2 | 3 | import com.aliyun.mq.http.common.http.ResponseMessage; 4 | import com.aliyun.mq.http.common.utils.ResourceManager; 5 | import com.aliyun.mq.http.common.utils.ServiceConstants; 6 | import org.xml.sax.InputSource; 7 | import org.xml.sax.SAXException; 8 | 9 | import javax.xml.bind.JAXBContext; 10 | import javax.xml.bind.JAXBException; 11 | import javax.xml.bind.Unmarshaller; 12 | import javax.xml.parsers.ParserConfigurationException; 13 | import javax.xml.parsers.SAXParser; 14 | import javax.xml.parsers.SAXParserFactory; 15 | import javax.xml.transform.sax.SAXSource; 16 | 17 | import java.io.ByteArrayInputStream; 18 | import java.io.InputStream; 19 | import java.util.HashMap; 20 | 21 | public class JAXBResultParser implements ResultParser { 22 | 23 | private static final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); 24 | // Because JAXBContext.newInstance() is a very slow method, 25 | // it can improve performance a lot to cache the instances of JAXBContext 26 | // for used context paths or class types. 27 | private static HashMap cachedContexts = new HashMap(); 28 | 29 | static { 30 | saxParserFactory.setNamespaceAware(true); 31 | saxParserFactory.setValidating(false); 32 | } 33 | 34 | // It allows to specify the class type, if the class type is specified, 35 | // the contextPath will be ignored. 36 | private Class modelClass; 37 | 38 | public JAXBResultParser(Class modelClass) { 39 | assert (modelClass != null); 40 | this.modelClass = modelClass; 41 | } 42 | 43 | private static synchronized void initJAXBContext(Class c) throws JAXBException { 44 | if (!cachedContexts.containsKey(c)) { 45 | JAXBContext jc = JAXBContext.newInstance(c); 46 | cachedContexts.put(c, jc); 47 | } 48 | } 49 | 50 | private static SAXSource getSAXSource(InputStream content) throws SAXException, ParserConfigurationException { 51 | 52 | SAXParser saxParser = saxParserFactory.newSAXParser(); 53 | return new SAXSource(saxParser.getXMLReader(), new InputSource(content)); 54 | } 55 | 56 | public Object parse(String content) throws ResultParseException { 57 | assert (content != null); 58 | return parse(new ByteArrayInputStream(content.getBytes())); 59 | } 60 | 61 | public Object parse(ResponseMessage response) throws ResultParseException { 62 | assert (response != null && response.getContent() != null); 63 | return parse(response.getContent()); 64 | } 65 | 66 | public Object parse(InputStream is) throws ResultParseException { 67 | assert (is != null); 68 | 69 | try { 70 | if (!cachedContexts.containsKey(modelClass)) { 71 | initJAXBContext(modelClass); 72 | } 73 | 74 | assert (cachedContexts.containsKey(modelClass)); 75 | JAXBContext jc = cachedContexts.get(modelClass); 76 | Unmarshaller um = jc.createUnmarshaller(); 77 | // It performs better to call Unmarshaller#unmarshal(Source) 78 | // than to call Unmarshaller#unmarshall(InputStream) 79 | // if XMLReader is specified in the SAXSource instance. 80 | return um.unmarshal(getSAXSource(is)); 81 | } catch (JAXBException e) { 82 | throw new ResultParseException(ResourceManager.getInstance(ServiceConstants.RESOURCE_NAME_COMMON) 83 | .getString("FailedToParseResponse"), e); 84 | } catch (SAXException e) { 85 | throw new ResultParseException(ResourceManager.getInstance(ServiceConstants.RESOURCE_NAME_COMMON) 86 | .getString("FailedToParseResponse"), e); 87 | } catch (ParserConfigurationException e) { 88 | throw new ResultParseException(ResourceManager.getInstance(ServiceConstants.RESOURCE_NAME_COMMON) 89 | .getString("FailedToParseResponse"), e); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/parser/ResultParseException.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.parser; 2 | 3 | /** 4 | * The exception from parsing service result. 5 | */ 6 | public class ResultParseException extends Exception { 7 | private static final long serialVersionUID = -6660159156997037589L; 8 | 9 | public ResultParseException() { 10 | super(); 11 | } 12 | 13 | public ResultParseException(String message) { 14 | super(message); 15 | } 16 | 17 | public ResultParseException(Throwable cause) { 18 | super(cause); 19 | } 20 | 21 | public ResultParseException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/parser/ResultParser.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.parser; 2 | 3 | import com.aliyun.mq.http.common.http.ResponseMessage; 4 | 5 | /** 6 | * Used to convert an result stream to a java object. 7 | */ 8 | public interface ResultParser { 9 | /** 10 | * Converts the result from stream to a java object. 11 | * 12 | * @param response The stream of the result. 13 | * @return The java Type T object that the result stands for. 14 | * @throws ResultParseException Failed to parse the result. 15 | */ 16 | T parse(ResponseMessage response) throws ResultParseException; 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/BinaryUtil.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import org.apache.commons.codec.binary.Base64; 4 | 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | public class BinaryUtil { 9 | public static String toBase64String(byte[] binaryData) { 10 | return new String(Base64.encodeBase64(binaryData)); 11 | } 12 | 13 | public static byte[] fromBase64String(String base64String) { 14 | return Base64.decodeBase64(base64String); 15 | } 16 | 17 | public static byte[] calculateMd5(byte[] binaryData) { 18 | MessageDigest messageDigest = null; 19 | try { 20 | messageDigest = MessageDigest.getInstance("MD5"); 21 | } catch (NoSuchAlgorithmException e) { 22 | throw new RuntimeException("MD5 algorithm not found."); 23 | } 24 | messageDigest.update(binaryData); 25 | return messageDigest.digest(); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/BooleanSerializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | import com.google.gson.JsonDeserializationContext; 6 | import com.google.gson.JsonDeserializer; 7 | import com.google.gson.JsonElement; 8 | import com.google.gson.JsonParseException; 9 | import com.google.gson.JsonPrimitive; 10 | import com.google.gson.JsonSerializationContext; 11 | import com.google.gson.JsonSerializer; 12 | 13 | public class BooleanSerializer implements JsonSerializer, JsonDeserializer { 14 | 15 | @Override 16 | public JsonElement serialize(Boolean arg0, Type arg1, JsonSerializationContext arg2) { 17 | return new JsonPrimitive(arg0 ? 1 : 0); 18 | } 19 | 20 | @Override 21 | public Boolean deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2) throws JsonParseException { 22 | return arg0.getAsInt() == 1; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/CaseInsensitiveMap.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import java.util.Collection; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | import static com.aliyun.mq.http.common.utils.CodingUtils.assertParameterNotNull; 9 | 10 | public class CaseInsensitiveMap implements Map { 11 | 12 | private Map wrappedMap; 13 | 14 | public CaseInsensitiveMap() { 15 | this(new HashMap()); 16 | } 17 | 18 | public CaseInsensitiveMap(Map wrappedMap) { 19 | assertParameterNotNull(wrappedMap, "wrappedMap"); 20 | 21 | this.wrappedMap = wrappedMap; 22 | } 23 | 24 | public int size() { 25 | return wrappedMap.size(); 26 | } 27 | 28 | public boolean isEmpty() { 29 | return wrappedMap.isEmpty(); 30 | } 31 | 32 | public boolean containsKey(Object key) { 33 | return wrappedMap.containsKey(key.toString().toLowerCase()); 34 | } 35 | 36 | public boolean containsValue(Object value) { 37 | return wrappedMap.containsValue(value); 38 | } 39 | 40 | public V get(Object key) { 41 | return wrappedMap.get(key.toString().toLowerCase()); 42 | } 43 | 44 | public V put(String key, V value) { 45 | return wrappedMap.put(key.toLowerCase(), value); 46 | } 47 | 48 | public V remove(Object key) { 49 | return wrappedMap.remove(key.toString().toLowerCase()); 50 | } 51 | 52 | public void putAll(Map m) { 53 | for (java.util.Map.Entry entry : m.entrySet()) { 54 | this.put(entry.getKey(), entry.getValue()); 55 | } 56 | } 57 | 58 | public void clear() { 59 | wrappedMap.clear(); 60 | } 61 | 62 | public Set keySet() { 63 | return wrappedMap.keySet(); 64 | } 65 | 66 | public Collection values() { 67 | return wrappedMap.values(); 68 | } 69 | 70 | public Set> entrySet() { 71 | return wrappedMap.entrySet(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/CodingUtils.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Utils for common coding. 7 | */ 8 | public class CodingUtils { 9 | private static ResourceManager rm = ResourceManager 10 | .getInstance(ServiceConstants.RESOURCE_NAME_COMMON); 11 | 12 | public static void assertParameterNotNull(Object param, String paramName) { 13 | if (param == null) { 14 | throw new NullPointerException(rm.getFormattedString( 15 | "ParameterIsNull", paramName)); 16 | } 17 | } 18 | 19 | public static void assertStringNotNullOrEmpty(String param, String paramName) { 20 | assertParameterNotNull(param, paramName); 21 | assert (param != null); 22 | if (param.length() == 0) { 23 | throw new IllegalArgumentException(rm.getFormattedString( 24 | "ParameterStringIsEmpty", paramName)); 25 | } 26 | } 27 | 28 | @SuppressWarnings("rawtypes") 29 | public static void assertListNotNullOrEmpty(List param, String paramName) { 30 | assertParameterNotNull(param, paramName); 31 | if (param.size() == 0) { 32 | throw new IllegalArgumentException(rm.getFormattedString( 33 | "ParameterListIsEmpty", paramName)); 34 | } 35 | } 36 | 37 | 38 | public static boolean isNullOrEmpty(String value) { 39 | return value == null || value.length() == 0; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import java.text.DateFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | import java.util.Locale; 8 | import java.util.SimpleTimeZone; 9 | 10 | /** 11 | * Util class for Date. 12 | */ 13 | public class DateUtil { 14 | // RFC 822 Date Format 15 | private static final String RFC822_DATE_FORMAT = 16 | "EEE, dd MMM yyyy HH:mm:ss z"; 17 | 18 | // ISO 8601 format 19 | private static final String ISO8601_DATE_FORMAT = 20 | "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; 21 | 22 | // Alternate ISO 8601 format without fractional seconds 23 | private static final String ALTERNATIVE_ISO8601_DATE_FORMAT = 24 | "yyyy-MM-dd'T'HH:mm:ss'Z'"; 25 | 26 | /** 27 | * Formats Date to GMT string. 28 | * 29 | * @param date Date 30 | * @return string date 31 | */ 32 | public static String formatRfc822Date(Date date) { 33 | return getRfc822DateFormat().format(date); 34 | } 35 | 36 | /** 37 | * Parses a GMT-format string. 38 | * 39 | * @param dateString string date 40 | * @return Date 41 | * @throws ParseException error format 42 | */ 43 | public static Date parseRfc822Date(String dateString) throws ParseException { 44 | return getRfc822DateFormat().parse(dateString); 45 | } 46 | 47 | private static DateFormat getRfc822DateFormat() { 48 | SimpleDateFormat rfc822DateFormat = 49 | new SimpleDateFormat(RFC822_DATE_FORMAT, Locale.US); 50 | rfc822DateFormat.setTimeZone(new SimpleTimeZone(0, "GMT")); 51 | 52 | return rfc822DateFormat; 53 | } 54 | 55 | public static String formatIso8601Date(Date date) { 56 | return getIso8601DateFormat().format(date); 57 | } 58 | 59 | public static String formatAlternativeIso8601Date(Date date) { 60 | return getAlternativeIso8601DateFormat().format(date); 61 | } 62 | 63 | /** 64 | * Parse a date string in the format of ISO 8601. 65 | * 66 | * @param dateString string date 67 | * @return Date 68 | * @throws ParseException error format 69 | */ 70 | public static Date parseIso8601Date(String dateString) throws ParseException { 71 | try { 72 | return getIso8601DateFormat().parse(dateString); 73 | } catch (ParseException e) { 74 | return getAlternativeIso8601DateFormat().parse(dateString); 75 | } 76 | } 77 | 78 | private static DateFormat getIso8601DateFormat() { 79 | SimpleDateFormat df = 80 | new SimpleDateFormat(ISO8601_DATE_FORMAT, Locale.US); 81 | df.setTimeZone(new SimpleTimeZone(0, "GMT")); 82 | 83 | return df; 84 | } 85 | 86 | private static DateFormat getAlternativeIso8601DateFormat() { 87 | SimpleDateFormat df = 88 | new SimpleDateFormat(ALTERNATIVE_ISO8601_DATE_FORMAT, Locale.US); 89 | df.setTimeZone(new SimpleTimeZone(0, "GMT")); 90 | 91 | return df; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/HttpHeaders.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.aliyun.mq.http.common.utils; 5 | 6 | /** 7 | * Contains the common HTTP headers. 8 | */ 9 | public interface HttpHeaders { 10 | 11 | String AUTHORIZATION = "Authorization"; 12 | String CONTENT_LENGTH = "Content-Length"; 13 | String CONTENT_MD5 = "Content-MD5"; 14 | String CONTENT_TYPE = "Content-Type"; 15 | String DATE = "Date"; 16 | String HOST = "Host"; 17 | String SECURITY_TOKEN = "security-token"; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | 8 | public class HttpUtil { 9 | 10 | private static final String ISO_8859_1_CHARSET = "iso-8859-1"; 11 | private static final String JAVA_CHARSET = "utf-8"; 12 | 13 | public static String urlEncode(String value, String charset) 14 | throws UnsupportedEncodingException { 15 | return value != null ? URLEncoder.encode(value, charset) 16 | .replace("+", "%20").replace("*", "%2A").replace("%7E", "~") 17 | : null; 18 | } 19 | 20 | public static String paramToQueryString(Map params, 21 | String charset) throws UnsupportedEncodingException { 22 | if (params == null || params.size() == 0) { 23 | return null; 24 | } 25 | 26 | StringBuilder paramString = new StringBuilder(); 27 | boolean first = true; 28 | for (Entry p : params.entrySet()) { 29 | String key = p.getKey(); 30 | String val = p.getValue(); 31 | 32 | if (!first) { 33 | paramString.append("&"); 34 | } 35 | 36 | paramString.append(key); 37 | if (val != null) { 38 | // The query string in URL should be encoded with URLEncoder 39 | // standard. 40 | paramString.append("=") 41 | .append(HttpUtil.urlEncode(val, charset)); 42 | // TODO: Should use URLEncoder.encode(val, charset)) instead of 43 | // HttpUril#urlEncode; 44 | } 45 | 46 | first = false; 47 | } 48 | 49 | return paramString.toString(); 50 | } 51 | 52 | // To fix the bug that the header value could not be unicode chars. 53 | // Because HTTP headers are encoded in iso-8859-1, 54 | // we need to convert the utf-8(java encoding) strings to iso-8859-1 ones. 55 | public static void convertHeaderCharsetFromIso88591( 56 | Map headers) { 57 | convertHeaderCharset(headers, ISO_8859_1_CHARSET, JAVA_CHARSET); 58 | } 59 | 60 | // For response, convert from iso-8859-1 to utf-8. 61 | public static void convertHeaderCharsetToIso88591( 62 | Map headers) { 63 | convertHeaderCharset(headers, JAVA_CHARSET, ISO_8859_1_CHARSET); 64 | } 65 | 66 | private static void convertHeaderCharset(Map headers, 67 | String fromCharset, String toCharset) { 68 | assert (headers != null); 69 | 70 | for (Map.Entry header : headers.entrySet()) { 71 | if (header.getValue() == null) { 72 | continue; 73 | } 74 | 75 | try { 76 | header.setValue(new String(header.getValue().getBytes( 77 | fromCharset), toCharset)); 78 | } catch (UnsupportedEncodingException e) { 79 | throw new AssertionError("Invalid charset name."); 80 | } 81 | } 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/IOUtils.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import java.io.*; 4 | 5 | public class IOUtils { 6 | 7 | public static String readStreamAsString(InputStream in, String charset) 8 | throws IOException { 9 | if (in == null) 10 | return ""; 11 | 12 | Reader reader = null; 13 | Writer writer = new StringWriter(); 14 | String result; 15 | 16 | char[] buffer = new char[1024]; 17 | try { 18 | reader = new BufferedReader( 19 | new InputStreamReader(in, charset)); 20 | 21 | int n; 22 | while ((n = reader.read(buffer)) > 0) { 23 | writer.write(buffer, 0, n); 24 | } 25 | 26 | result = writer.toString(); 27 | } finally { 28 | in.close(); 29 | if (reader != null) { 30 | reader.close(); 31 | } 32 | if (writer != null) { 33 | writer.close(); 34 | } 35 | } 36 | 37 | return result; 38 | } 39 | 40 | public static byte[] readStreamAsBytesArray(InputStream in) 41 | throws IOException { 42 | if (in == null) { 43 | return new byte[0]; 44 | } 45 | 46 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 47 | byte[] buffer = new byte[1024]; 48 | int len; 49 | while ((len = in.read(buffer)) > -1) { 50 | output.write(buffer, 0, len); 51 | } 52 | output.flush(); 53 | return output.toByteArray(); 54 | } 55 | 56 | public static void safeClose(InputStream inputStream) { 57 | if (inputStream != null) { 58 | try { 59 | inputStream.close(); 60 | } catch (IOException e) { 61 | } 62 | } 63 | } 64 | 65 | public static void safeClose(OutputStream outputStream) { 66 | if (outputStream != null) { 67 | try { 68 | outputStream.close(); 69 | } catch (IOException e) { 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/ResourceManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.aliyun.mq.http.common.utils; 5 | 6 | import java.text.MessageFormat; 7 | import java.util.Locale; 8 | import java.util.ResourceBundle; 9 | 10 | /** 11 | * Manager class to get localized resources. 12 | */ 13 | public class ResourceManager { 14 | private ResourceBundle bundle; 15 | 16 | ResourceManager(String baseName, Locale locale) { 17 | this.bundle = ResourceBundle.getBundle(baseName, locale); 18 | } 19 | 20 | public static ResourceManager getInstance(String baseName) { 21 | return new ResourceManager(baseName, Locale.getDefault()); 22 | } 23 | 24 | public static ResourceManager getInstance(String baseName, Locale locale) { 25 | return new ResourceManager(baseName, locale); 26 | } 27 | 28 | public String getString(String key) { 29 | return bundle.getString(key); 30 | } 31 | 32 | public String getFormattedString(String key, Object... args) { 33 | return MessageFormat.format(getString(key), args); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/ServiceConstants.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | public interface ServiceConstants { 4 | 5 | String DEFAULT_ENCODING = "utf-8"; 6 | 7 | String RESOURCE_NAME_COMMON = "common"; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | 5 | import java.net.URI; 6 | import java.net.URISyntaxException; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class Utils { 11 | public static Logger logger = LoggerFactory.getLogger(Utils.class); 12 | 13 | public static URI getHttpURI(String endpoint) { 14 | if (endpoint == null) { 15 | logger.warn("Endpoint is null"); 16 | throw new NullPointerException("Endpoint is null"); 17 | } 18 | 19 | try { 20 | if (!endpoint.startsWith(Constants.HTTP_PREFIX) && !endpoint.startsWith(Constants.HTTPS_PREFIX)) { 21 | logger.warn("Only support http or https protocol.Endpoint must be started by http:// or https://."); 22 | throw new IllegalArgumentException("Only support http or https protocol。Endpoint must be started by http:// or https://."); 23 | } 24 | while (endpoint.endsWith(Constants.SLASH)) { 25 | endpoint = endpoint.substring(0, endpoint.length() - 1); 26 | } 27 | 28 | if (endpoint.length() < Constants.HTTP_PREFIX.length()) { 29 | logger.warn("Invalid endpoint."); 30 | throw new IllegalArgumentException("Invalid endpoint."); 31 | } 32 | return new URI(endpoint); 33 | 34 | } catch (URISyntaxException e) { 35 | logger.warn("uri syntax error"); 36 | throw new IllegalArgumentException(e); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/common/utils/VersionInfoUtils.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.common.utils; 2 | 3 | public class VersionInfoUtils { 4 | private static String version = "1.0.3.2"; 5 | 6 | private static String defaultUserAgent = null; 7 | 8 | public static String getDefaultUserAgent() { 9 | if (defaultUserAgent == null) { 10 | String platformInfo = System.getProperty("os.name") + "/" 11 | + System.getProperty("os.version") + "/" 12 | + System.getProperty("os.arch") + ";" 13 | + System.getProperty("java.version"); 14 | 15 | defaultUserAgent = "mq-java-sdk" + "/" + version + "(" 16 | + platformInfo + ")"; 17 | } 18 | return defaultUserAgent; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/AbstractRequest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | 4 | public class AbstractRequest { 5 | protected String requestPath; 6 | protected String instanceId; 7 | 8 | public String getRequestPath() { 9 | return requestPath; 10 | } 11 | 12 | public void setRequestPath(String requestPath) { 13 | this.requestPath = requestPath; 14 | } 15 | 16 | public String getInstanceId() { 17 | return instanceId; 18 | } 19 | 20 | public void setInstanceId(String instanceId) { 21 | this.instanceId = instanceId; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/AbstractResponse.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | public class AbstractResponse { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/AsyncCallback.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | public interface AsyncCallback { 4 | /** 5 | * Async callback handler at successfully return. 6 | * 7 | * @param result result 8 | */ 9 | void onSuccess(T result); 10 | 11 | /** 12 | * Async callback handler at failed return. 13 | * 14 | * @param ex error 15 | */ 16 | void onFail(Exception ex); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/AsyncResult.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | import org.apache.http.HttpResponse; 4 | 5 | import java.util.concurrent.Future; 6 | 7 | /** 8 | * asynchronous call result 9 | * 10 | * @param type of Result model 11 | */ 12 | public interface AsyncResult { 13 | /** 14 | * get response result 15 | * 16 | * @return the result aysnc call return, 17 | * not null meaning async call successful, 18 | * wait result until call end. 19 | */ 20 | T getResult(); 21 | 22 | /** 23 | * get response result blocked timewait 24 | * 25 | * @param timewait wait for result in 'timewait' milliseconds. 26 | * @return as async call result. 27 | */ 28 | T getResult(long timewait); 29 | 30 | /** 31 | * check request is success. 32 | * 33 | * @return async call is successful(true) or not (false) 34 | */ 35 | boolean isSuccess(); 36 | 37 | /** 38 | * get error 39 | * 40 | * @return async call exception 41 | */ 42 | Exception getException(); 43 | 44 | /** 45 | * time wait 46 | * 47 | * @param timewait wait for result in 'timewait' milliseconds. 48 | */ 49 | void setTimewait(long timewait); 50 | 51 | /** 52 | * set future 53 | * 54 | * @param future thre http client Future response 55 | */ 56 | void setFuture(Future future); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/BaseMessage.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public abstract class BaseMessage { 10 | 11 | private String requestId; 12 | private String messageId; 13 | private String messageBodyMD5; 14 | private byte[] messageBodyBytes; 15 | private Map properties; 16 | 17 | 18 | public BaseMessage() { 19 | this.requestId = null; 20 | this.messageId = null; 21 | this.messageBodyMD5 = null; 22 | this.messageBodyBytes = null; 23 | this.properties = new HashMap(4); 24 | } 25 | 26 | public String getRequestId() { 27 | return requestId; 28 | } 29 | 30 | public void setRequestId(String requestId) { 31 | this.requestId = requestId; 32 | } 33 | 34 | /** 35 | * getMessageId 36 | * 37 | * @return message id 38 | */ 39 | public String getMessageId() { 40 | return messageId; 41 | } 42 | 43 | public void setMessageId(String messageId) { 44 | this.messageId = messageId; 45 | } 46 | 47 | /** 48 | * getMessageBodyMD5 49 | * 50 | * @return message body md5 51 | */ 52 | public String getMessageBodyMD5() { 53 | return messageBodyMD5; 54 | } 55 | 56 | public void setMessageBodyMD5(String messageBodyMD5) { 57 | this.messageBodyMD5 = messageBodyMD5; 58 | } 59 | 60 | /** 61 | * set body through byte 62 | * 63 | * @param messageBodyBytes byte body 64 | */ 65 | public void setMessageBody(byte[] messageBodyBytes) { 66 | this.messageBodyBytes = messageBodyBytes; 67 | } 68 | 69 | /** 70 | * get body as byte 71 | * 72 | * @return messageBody 73 | */ 74 | public byte[] getMessageBodyBytes() { 75 | return messageBodyBytes; 76 | } 77 | 78 | /** 79 | * get body as string, encoded by utf-8 80 | * 81 | * @return string body 82 | */ 83 | public String getMessageBodyString() { 84 | byte[] messageBodyAsBytes = getMessageBodyBytes(); 85 | if (messageBodyAsBytes == null) { 86 | return null; 87 | } 88 | try { 89 | return new String(messageBodyAsBytes, Constants.DEFAULT_CHARSET); 90 | } catch (UnsupportedEncodingException e) { 91 | throw new RuntimeException("Not support encoding: " + Constants.DEFAULT_CHARSET); 92 | } 93 | } 94 | 95 | 96 | /** 97 | * set body through string, encoded by utf-8 98 | * 99 | * @param messageBody string body 100 | * @throws UnsupportedEncodingException not support encode charset 101 | */ 102 | public void setMessageBody(String messageBody) throws UnsupportedEncodingException { 103 | setMessageBody(messageBody.getBytes(Constants.DEFAULT_CHARSET)); 104 | } 105 | 106 | public Map getProperties() { 107 | return properties; 108 | } 109 | 110 | public void setProperties(Map properties) { 111 | this.properties = properties; 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | StringBuffer sb = new StringBuffer(); 117 | 118 | if (messageId != null) { 119 | sb.append("MessageID:" + this.messageId + ","); 120 | } 121 | 122 | if (messageBodyMD5 != null) { 123 | sb.append("MessageMD5:" + this.messageBodyMD5 + ","); 124 | } 125 | 126 | if (requestId != null) { 127 | sb.append("RequestID:" + this.requestId + ","); 128 | } 129 | 130 | if (properties != null && !properties.isEmpty()) { 131 | sb.append("Properties:" + this.properties); 132 | } 133 | return sb.toString(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/ErrorMessage.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | 5 | import javax.xml.bind.annotation.XmlElement; 6 | import javax.xml.bind.annotation.XmlRootElement; 7 | 8 | @XmlRootElement(name = "Error", namespace = Constants.DEFAULT_XML_NAMESPACE) 9 | public class ErrorMessage { 10 | @XmlElement(name = "Code", namespace = Constants.DEFAULT_XML_NAMESPACE) 11 | public String Code; 12 | 13 | @XmlElement(name = "Message", namespace = Constants.DEFAULT_XML_NAMESPACE) 14 | public String Message; 15 | 16 | @XmlElement(name = "RequestId", namespace = Constants.DEFAULT_XML_NAMESPACE) 17 | public String RequestId; 18 | 19 | @XmlElement(name = "Method", namespace = Constants.DEFAULT_XML_NAMESPACE) 20 | public String Method; 21 | 22 | @XmlElement(name = "HostId", namespace = Constants.DEFAULT_XML_NAMESPACE) 23 | public String HostId; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/ErrorMessageResult.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | public class ErrorMessageResult { 4 | protected String errorCode; 5 | protected String errorMessage; 6 | 7 | public String getErrorCode() { 8 | return errorCode; 9 | } 10 | 11 | public void setErrorCode(String errorCode) { 12 | this.errorCode = errorCode; 13 | } 14 | 15 | public String getErrorMessage() { 16 | return errorMessage; 17 | } 18 | 19 | public void setErrorMessage(String errorMessage) { 20 | this.errorMessage = errorMessage; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/Message.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | 5 | public final class Message extends BaseMessage { 6 | /** 7 | * the handle of message, used to ack 8 | */ 9 | private String receiptHandle; 10 | /** 11 | * publish time 12 | */ 13 | private long publishTime; 14 | /** 15 | * if the message is cnosume but not acked, will be consumable at nextConsumeTime 16 | */ 17 | private long nextConsumeTime; 18 | /** 19 | * first consume time, it's meaningless for orderly consume. 20 | */ 21 | private long firstConsumeTime; 22 | /** 23 | * already consumed times 24 | */ 25 | private Integer consumedTimes; 26 | private String messageTag; 27 | private ErrorMessageResult errorMessage; 28 | 29 | 30 | public Message() { 31 | super(); 32 | } 33 | 34 | public Message(byte[] messageBody) { 35 | super(); 36 | setMessageBody(messageBody); 37 | } 38 | 39 | public String getReceiptHandle() { 40 | return receiptHandle; 41 | } 42 | 43 | public void setReceiptHandle(String receiptHandle) { 44 | this.receiptHandle = receiptHandle; 45 | } 46 | 47 | public long getPublishTime() { 48 | return publishTime; 49 | } 50 | 51 | public void setPublishTime(long publishTime) { 52 | this.publishTime = publishTime; 53 | } 54 | 55 | public long getNextConsumeTime() { 56 | return nextConsumeTime; 57 | } 58 | 59 | public void setNextConsumeTime(long nextConsumeTime) { 60 | this.nextConsumeTime = nextConsumeTime; 61 | } 62 | 63 | public long getFirstConsumeTime() { 64 | return firstConsumeTime; 65 | } 66 | 67 | public void setFirstConsumeTime(long firstConsumeTime) { 68 | this.firstConsumeTime = firstConsumeTime; 69 | } 70 | 71 | public Integer getConsumedTimes() { 72 | return consumedTimes; 73 | } 74 | 75 | public void setConsumedTimes(Integer consumedTimes) { 76 | this.consumedTimes = consumedTimes; 77 | } 78 | 79 | public String getMessageTag() { 80 | return messageTag; 81 | } 82 | 83 | public void setMessageTag(String messageTag) { 84 | this.messageTag = messageTag; 85 | } 86 | 87 | public long getStartDeliverTime() { 88 | String value = getProperties().get(Constants.MESSAGE_PROPERTIES_TIMER_KEY); 89 | if (value == null) { 90 | return 0; 91 | } 92 | return Long.valueOf(value); 93 | } 94 | 95 | public int getTransCheckImmunityTime() { 96 | String value = getProperties().get(Constants.MESSAGE_PROPERTIES_TRANS_CHECK_KEY); 97 | if (value == null) { 98 | return 0; 99 | } 100 | return Integer.valueOf(value); 101 | } 102 | 103 | public String getMessageKey() { 104 | return getProperties().get(Constants.MESSAGE_PROPERTIES_MSG_KEY); 105 | } 106 | 107 | public String getShardingKey() { 108 | return getProperties().get(Constants.MESSAGE_PROPERTIES_SHARDING); 109 | } 110 | 111 | public ErrorMessageResult getErrorMessageDetail() { 112 | return this.errorMessage; 113 | } 114 | 115 | public boolean isErrorMessage() { 116 | return errorMessage != null; 117 | } 118 | 119 | public ErrorMessageResult getErrorMessage() { 120 | return errorMessage; 121 | } 122 | 123 | public void setErrorMessage(ErrorMessageResult errorMessage) { 124 | this.errorMessage = errorMessage; 125 | } 126 | 127 | @Override 128 | public String toString() { 129 | final StringBuilder sb = new StringBuilder("Message{"); 130 | sb.append(super.toString()); 131 | sb.append(", receiptHandle='").append(receiptHandle).append('\''); 132 | sb.append(", publishTime=").append(publishTime); 133 | sb.append(", nextConsumeTime=").append(nextConsumeTime); 134 | sb.append(", firstConsumeTime=").append(firstConsumeTime); 135 | sb.append(", consumedTimes=").append(consumedTimes); 136 | sb.append(", messageTag='").append(messageTag).append('\''); 137 | sb.append(", errorMessage=").append(errorMessage); 138 | sb.append('}'); 139 | return sb.toString(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/TopicMessage.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model; 2 | 3 | 4 | import com.aliyun.mq.http.common.Constants; 5 | 6 | public class TopicMessage extends BaseMessage { 7 | 8 | private String messageTag; 9 | 10 | /** 11 | * only transaction msg have; 12 | */ 13 | private String receiptHandle; 14 | 15 | public TopicMessage() { 16 | super(); 17 | } 18 | 19 | public TopicMessage(byte[] body) { 20 | super(); 21 | setMessageBody(body); 22 | } 23 | 24 | public TopicMessage(byte[] body, String messageTag) { 25 | super(); 26 | setMessageBody(body); 27 | this.messageTag = messageTag; 28 | } 29 | 30 | public String getMessageTag() { 31 | return messageTag; 32 | } 33 | 34 | public void setMessageTag(String messageTag) { 35 | this.messageTag = messageTag; 36 | } 37 | 38 | public String getReceiptHandle() { 39 | return receiptHandle; 40 | } 41 | 42 | public void setReceiptHandle(String receiptHandle) { 43 | this.receiptHandle = receiptHandle; 44 | } 45 | 46 | /** 47 | * 定时消息,单位毫秒(ms),在指定时间戳(当前时间之后)进行投递。 48 | * 如果被设置成当前时间戳之前的某个时刻,消息将立刻投递给消费者 49 | * @param time 50 | */ 51 | public void setStartDeliverTime(long time) { 52 | getProperties().put(Constants.MESSAGE_PROPERTIES_TIMER_KEY, String.valueOf(time)); 53 | } 54 | 55 | /** 56 | * 在消息属性中添加第一次消息回查的最快时间,单位秒,并且表征这是一条事务消息 57 | * @param seconds 58 | */ 59 | public void setTransCheckImmunityTime(int seconds) { 60 | getProperties().put(Constants.MESSAGE_PROPERTIES_TRANS_CHECK_KEY, String.valueOf(seconds)); 61 | } 62 | 63 | /** 64 | * 设置消息KEY,如果没有设置,则消息的KEY为RequestId 65 | * 66 | * @param key 消息KEY 67 | */ 68 | public void setMessageKey(String key) { 69 | getProperties().put(Constants.MESSAGE_PROPERTIES_MSG_KEY, key); 70 | } 71 | 72 | /** 73 | * 分区顺序消息中区分不同分区的关键字段,sharding key 于普通消息的 key 是完全不同的概念。 74 | * 全局顺序消息,该字段可以设置为任意非空字符串。 75 | * 76 | * @param shardingKey 77 | */ 78 | public void setShardingKey(String shardingKey) { 79 | getProperties().put(Constants.MESSAGE_PROPERTIES_SHARDING, shardingKey); 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | final StringBuilder sb = new StringBuilder("TopicMessage{"); 85 | sb.append(super.toString()); 86 | sb.append("messageTag='").append(messageTag).append('\''); 87 | sb.append(", receiptHandle='").append(receiptHandle).append('\''); 88 | sb.append('}'); 89 | return sb.toString(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/action/AbstractAction.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.action; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.Constants; 5 | import com.aliyun.mq.http.common.HttpMethod; 6 | import com.aliyun.mq.http.common.ServiceException; 7 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 8 | import com.aliyun.mq.http.common.auth.ServiceSignature; 9 | import com.aliyun.mq.http.common.utils.Utils; 10 | import com.aliyun.mq.http.model.AbstractRequest; 11 | import com.aliyun.mq.http.model.AsyncCallback; 12 | import com.aliyun.mq.http.model.AsyncResult; 13 | import com.aliyun.mq.http.common.comm.ExecutionContext; 14 | import com.aliyun.mq.http.common.http.ExceptionResultParser; 15 | import com.aliyun.mq.http.common.http.HttpCallback; 16 | import com.aliyun.mq.http.common.http.RequestMessage; 17 | import com.aliyun.mq.http.common.http.ServiceClient; 18 | import com.aliyun.mq.http.common.parser.ResultParser; 19 | import com.aliyun.mq.http.common.utils.DateUtil; 20 | import org.apache.http.HttpResponse; 21 | 22 | import java.net.URI; 23 | import java.util.Date; 24 | import java.util.Map; 25 | import java.util.Set; 26 | import java.util.TreeMap; 27 | import java.util.concurrent.Future; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | public abstract class AbstractAction implements 32 | Action { 33 | 34 | public static Logger logger = LoggerFactory.getLogger(Utils.class); 35 | protected String actionName = ""; 36 | private ServiceClient client; 37 | private ServiceCredentials credentials; 38 | private HttpMethod method; 39 | private URI endpoint; 40 | 41 | public AbstractAction(HttpMethod method, String actionName, 42 | ServiceClient client, ServiceCredentials credentials, URI endpoint) { 43 | this.method = method; 44 | this.actionName = actionName; 45 | this.client = client; 46 | this.endpoint = endpoint; 47 | this.credentials = credentials; 48 | } 49 | 50 | private static TreeMap sortHeader( 51 | Map headers) { 52 | TreeMap tmpHeaders = new TreeMap(); 53 | Set keySet = headers.keySet(); 54 | for (String key : keySet) { 55 | if (key.toLowerCase().startsWith(Constants.X_HEADER_PREFIX)) { 56 | tmpHeaders.put(key.toLowerCase(), headers.get(key)); 57 | } else { 58 | tmpHeaders.put(key, headers.get(key)); 59 | } 60 | } 61 | return tmpHeaders; 62 | } 63 | 64 | private static String safeGetHeader(String key, Map headers) { 65 | if (headers == null) { 66 | return ""; 67 | } 68 | String value = headers.get(key); 69 | if (value == null) { 70 | return ""; 71 | } 72 | return value; 73 | } 74 | 75 | public String getActionName() { 76 | return actionName; 77 | } 78 | 79 | public ServiceClient getClient() { 80 | return client; 81 | } 82 | 83 | public ServiceCredentials getCredentials() { 84 | return credentials; 85 | } 86 | 87 | public HttpMethod getMethod() { 88 | return method; 89 | } 90 | 91 | public URI getEndpoint() { 92 | return this.endpoint; 93 | } 94 | 95 | public AsyncResult execute(T reqObject, AsyncCallback asyncHandler) 96 | throws ClientException, ServiceException { 97 | return this.executeWithCustomHeaders(reqObject, asyncHandler, null); 98 | } 99 | 100 | public AsyncResult executeWithCustomHeaders(T reqObject, AsyncCallback asyncHandler, 101 | Map customHeaders) 102 | throws ClientException, ServiceException { 103 | RequestMessage request = buildRequestMessage(reqObject); 104 | request.setMethod(this.getMethod()); 105 | this.addRequiredHeader(request); 106 | this.addCustomHeader(request, customHeaders); 107 | this.addSignatureHeader(request); 108 | 109 | HttpCallback callback = new HttpCallback( 110 | this.buildResultParser(), this.buildExceptionParser(), asyncHandler); 111 | AsyncResult asyncResult = callback.getAsyncResult(); 112 | asyncResult.setTimewait(this.client.getClientConfiguration().getSocketTimeout()); 113 | Future future = client.sendRequest(request, new ExecutionContext(), callback); 114 | asyncResult.setFuture(future); 115 | return asyncResult; 116 | } 117 | 118 | public V execute(T reqObject) throws ClientException, ServiceException { 119 | return this.executeWithCustomHeaders(reqObject, null); 120 | } 121 | 122 | public V executeWithCustomHeaders(T reqObject, Map customHeaders) 123 | throws ClientException, ServiceException { 124 | AsyncResult result = executeWithCustomHeaders(reqObject, null, customHeaders); 125 | V value = result.getResult(); 126 | if (result.isSuccess()) { 127 | return value; 128 | } 129 | 130 | if (result.getException() instanceof ClientException) { 131 | throw (ClientException) result.getException(); 132 | } else if (result.getException() instanceof ServiceException) { 133 | throw (ServiceException) result.getException(); 134 | } else { 135 | ClientException ce = new ClientException(result.getException().toString(), 136 | null, result.getException()); 137 | ce.setStackTrace(result.getException().getStackTrace()); 138 | throw ce; 139 | } 140 | } 141 | 142 | private void addCustomHeader(RequestMessage request, Map customHeaders) { 143 | if (customHeaders == null || customHeaders.size() == 0) { 144 | return; 145 | } 146 | for (String key : customHeaders.keySet()) { 147 | request.getHeaders().put(key, customHeaders.get(key)); 148 | } 149 | } 150 | 151 | protected void addRequiredHeader(RequestMessage request) { 152 | request.getHeaders().put(Constants.X_HEADER_API_VERSION, 153 | Constants.X_HEADER_API_VERSION_VALUE); 154 | 155 | if (request.getHeaders().get(Constants.DATE) == null) { 156 | request.getHeaders().put(Constants.DATE, 157 | DateUtil.formatRfc822Date(new Date())); 158 | } 159 | 160 | if (request.getHeaders().get(Constants.CONTENT_TYPE) == null) { 161 | request.getHeaders().put(Constants.CONTENT_TYPE, 162 | Constants.DEFAULT_CONTENT_TYPE); 163 | } 164 | } 165 | 166 | protected void addSignatureHeader(RequestMessage request) 167 | throws ClientException { 168 | if (credentials != null && credentials.getAccessKeyId() != null 169 | && credentials.getAccessKeySecret() != null) { 170 | // Add signature 171 | request.addHeader(Constants.AUTHORIZATION, 172 | "MQ " + credentials.getAccessKeyId() + ":" 173 | + getSignature(request) 174 | ); 175 | 176 | // add security_token if security token is not empty. 177 | String securityToken = credentials.getSecurityToken(); 178 | if (securityToken != null && !"".equals(securityToken)) { 179 | request.addHeader(Constants.SECURITY_TOKEN, securityToken); 180 | } 181 | } 182 | } 183 | 184 | private String getRelativeResourcePath(String subPath) { 185 | String rootPath = endpoint.getPath(); 186 | if (subPath != null && !"".equals(subPath.trim())) { 187 | if (subPath.startsWith("/")) { 188 | subPath = subPath.substring(1); 189 | } 190 | if (!rootPath.endsWith("/")) { 191 | return rootPath + "/" + subPath; 192 | } 193 | return rootPath + subPath; 194 | } 195 | return rootPath; 196 | } 197 | 198 | private String getSignature(RequestMessage request) throws ClientException { 199 | Map headers = request.getHeaders(); 200 | 201 | StringBuffer canonicalizedHeaders = new StringBuffer(); 202 | StringBuffer stringToSign = new StringBuffer(); 203 | String contentMd5 = safeGetHeader(Constants.CONTENT_MD5, headers); 204 | String contentType = safeGetHeader(Constants.CONTENT_TYPE, headers); 205 | String date = safeGetHeader(Constants.DATE, headers); 206 | String canonicalizedResource = getRelativeResourcePath(request 207 | .getResourcePath()); 208 | 209 | TreeMap tmpHeaders = sortHeader(request.getHeaders()); 210 | if (tmpHeaders.size() > 0) { 211 | Set keySet = tmpHeaders.keySet(); 212 | for (String key : keySet) { 213 | if (key.toLowerCase().startsWith( 214 | Constants.X_HEADER_PREFIX)) { 215 | canonicalizedHeaders.append(key).append(":") 216 | .append(tmpHeaders.get(key)).append("\n"); 217 | } 218 | } 219 | } 220 | stringToSign.append(method).append("\n").append(contentMd5) 221 | .append("\n").append(contentType).append("\n").append(date) 222 | .append("\n").append(canonicalizedHeaders) 223 | .append(canonicalizedResource); 224 | String signature; 225 | 226 | try { 227 | signature = ServiceSignature.create().computeSignature( 228 | credentials.getAccessKeySecret(), stringToSign.toString()); 229 | } catch (Exception e) { 230 | throw new ClientException("Signature fail", null, e); 231 | } 232 | 233 | return signature; 234 | } 235 | 236 | protected RequestMessage buildRequestMessage(T reqObject) 237 | throws ClientException { 238 | RequestMessage request = buildRequest(reqObject); 239 | String requestPath = request.getResourcePath(); 240 | if (requestPath != null && (requestPath.startsWith("http://") || requestPath.startsWith("https://"))) { 241 | if (!requestPath.startsWith(endpoint.toString())) { 242 | throw new IllegalArgumentException("Endpoint[" 243 | + endpoint.toString() + "]和访问地址[" + requestPath 244 | + "]不匹配."); 245 | } else { 246 | requestPath = requestPath.substring(endpoint.toString().length()); 247 | if (requestPath.startsWith("/")) 248 | requestPath = requestPath.substring(1); 249 | request.setResourcePath(requestPath); 250 | } 251 | } 252 | request.setEndpoint(endpoint); 253 | 254 | return request; 255 | } 256 | 257 | protected ResultParser buildResultParser() { 258 | return null; 259 | } 260 | 261 | protected ResultParser buildExceptionParser() { 262 | return new ExceptionResultParser(null); 263 | } 264 | 265 | protected abstract RequestMessage buildRequest(T reqObject) 266 | throws ClientException; 267 | } 268 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/action/AckMessageAction.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.action; 2 | 3 | import com.aliyun.mq.http.common.AckMessageException; 4 | import com.aliyun.mq.http.common.ClientException; 5 | import com.aliyun.mq.http.common.Constants; 6 | import com.aliyun.mq.http.common.HttpMethod; 7 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 8 | import com.aliyun.mq.http.common.http.RequestMessage; 9 | import com.aliyun.mq.http.common.http.ServiceClient; 10 | import com.aliyun.mq.http.common.parser.ResultParseException; 11 | import com.aliyun.mq.http.common.parser.ResultParser; 12 | import com.aliyun.mq.http.model.serialize.ErrorReceiptHandleListDeserializer; 13 | import com.aliyun.mq.http.model.serialize.ReceiptHandleListSerializer; 14 | import com.aliyun.mq.http.common.http.ResponseMessage; 15 | import com.aliyun.mq.http.model.request.AckMessageRequest; 16 | 17 | import java.io.InputStream; 18 | import java.net.URI; 19 | 20 | public class AckMessageAction extends AbstractAction { 21 | 22 | public AckMessageAction(ServiceClient client, 23 | ServiceCredentials credentials, URI endpoint) { 24 | super(HttpMethod.DELETE, "AckMessage", client, credentials, 25 | endpoint); 26 | } 27 | 28 | @Override 29 | protected RequestMessage buildRequest(AckMessageRequest reqObject) 30 | throws ClientException { 31 | RequestMessage requestMessage = new RequestMessage(); 32 | 33 | if (reqObject.getReceiptHandles() == null || reqObject.getReceiptHandles().isEmpty()) { 34 | throw new ClientException("ReceiptHandles can not be null or empty!", "LocalCheck"); 35 | } 36 | 37 | if (reqObject.getConsumer() == null || reqObject.getConsumer().isEmpty()) { 38 | throw new ClientException("Consumer can not be empty!", "LocalCheck"); 39 | } 40 | 41 | String uri = reqObject.getRequestPath() + "?" + Constants.PARAM_CONSUMER + "=" + reqObject.getConsumer(); 42 | if (reqObject.getInstanceId() != null && reqObject.getInstanceId() != "") { 43 | uri += "&" + Constants.PARAM_NS + "=" + reqObject.getInstanceId(); 44 | } 45 | if (reqObject.getTrans() != null && reqObject.getTrans().length() > 0) { 46 | uri += "&" + Constants.PARAM_TRANSACTION + "=" + reqObject.getTrans(); 47 | } 48 | requestMessage.setResourcePath(uri); 49 | try { 50 | ReceiptHandleListSerializer serializer = new ReceiptHandleListSerializer(); 51 | InputStream is = serializer.serialize( 52 | reqObject.getReceiptHandles(), Constants.DEFAULT_CHARSET); 53 | requestMessage.setContent(is); 54 | requestMessage.setContentLength(is.available()); 55 | } catch (Exception e) { 56 | throw new ClientException(e.getMessage(), null, e); 57 | } 58 | return requestMessage; 59 | } 60 | 61 | @Override 62 | protected ResultParser buildResultParser() { 63 | return null; 64 | } 65 | 66 | @Override 67 | protected ResultParser buildExceptionParser() { 68 | return new ResultParser() { 69 | public Exception parse(ResponseMessage response) 70 | throws ResultParseException { 71 | ErrorReceiptHandleListDeserializer deserializer = new ErrorReceiptHandleListDeserializer(); 72 | try { 73 | Exception ret = deserializer.deserialize(response.getContent()); 74 | if (ret instanceof AckMessageException) { 75 | ((AckMessageException) ret).setRequestId(response.getHeader(Constants.X_HEADER_REQUEST_ID)); 76 | } 77 | return ret; 78 | } catch (Exception e) { 79 | throw new ResultParseException("Unmarshal error,cause by:" + e.getMessage(), e); 80 | } 81 | } 82 | }; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/action/Action.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.action; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.HttpMethod; 5 | import com.aliyun.mq.http.common.ServiceException; 6 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 7 | import com.aliyun.mq.http.model.AbstractRequest; 8 | import com.aliyun.mq.http.model.AsyncCallback; 9 | import com.aliyun.mq.http.model.AsyncResult; 10 | import com.aliyun.mq.http.common.http.ServiceClient; 11 | 12 | 13 | public interface Action { 14 | public String getActionName(); 15 | 16 | public HttpMethod getMethod(); 17 | 18 | public ServiceClient getClient(); 19 | 20 | public ServiceCredentials getCredentials(); 21 | 22 | public AsyncResult execute(T reqObject, AsyncCallback asyncHandler) throws ClientException, ServiceException; 23 | 24 | public V execute(T reqObject) throws ClientException, ServiceException; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/action/ConsumeMessageAction.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.action; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.Constants; 5 | import com.aliyun.mq.http.common.HttpMethod; 6 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 7 | import com.aliyun.mq.http.common.http.RequestMessage; 8 | import com.aliyun.mq.http.common.http.ServiceClient; 9 | import com.aliyun.mq.http.common.parser.ResultParseException; 10 | import com.aliyun.mq.http.common.parser.ResultParser; 11 | import com.aliyun.mq.http.model.Message; 12 | import com.aliyun.mq.http.model.request.ConsumeMessageRequest; 13 | import com.aliyun.mq.http.model.serialize.MessageListDeserializer; 14 | import com.aliyun.mq.http.common.http.ResponseMessage; 15 | 16 | import java.net.URI; 17 | import java.util.List; 18 | 19 | public class ConsumeMessageAction extends 20 | AbstractAction> { 21 | 22 | public ConsumeMessageAction(ServiceClient client, ServiceCredentials credentials, URI endpoint) { 23 | super(HttpMethod.GET, "ConsumeMessage", client, credentials, endpoint); 24 | } 25 | 26 | @Override 27 | protected RequestMessage buildRequest(ConsumeMessageRequest reqObject) 28 | throws ClientException { 29 | RequestMessage requestMessage = new RequestMessage(); 30 | 31 | if (reqObject.getBatchSize() < 1) { 32 | throw new ClientException("Consume num should greater then 0!", "LocalCheck"); 33 | } 34 | 35 | String uri = reqObject.getRequestPath() + "?" + Constants.PARAM_CONSUME_NUM + "=" + reqObject.getBatchSize(); 36 | 37 | if (reqObject.getConsumer() != null && reqObject.getConsumer().length() > 0) { 38 | uri += "&" + Constants.PARAM_CONSUMER + "=" + reqObject.getConsumer(); 39 | } else { 40 | throw new ClientException("Consumer can not be empty!", "LocalCheck"); 41 | } 42 | if (reqObject.getInstanceId() != null && reqObject.getInstanceId() != "") { 43 | uri += "&" + Constants.PARAM_NS + "=" + reqObject.getInstanceId(); 44 | } 45 | if (reqObject.getTag() != null && reqObject.getTag().length() > 0) { 46 | uri += "&" + Constants.PARAM_CONSUMER_TAG + "=" + reqObject.getTag(); 47 | } 48 | if (reqObject.getWaitSeconds() > 0) { 49 | uri += "&" + Constants.PARAM_WAIT_SECONDS + "=" + reqObject.getWaitSeconds(); 50 | } 51 | if (reqObject.getTrans() != null && reqObject.getTrans().length() > 0) { 52 | uri += "&" + Constants.PARAM_TRANSACTION + "=" + reqObject.getTrans(); 53 | } 54 | 55 | requestMessage.setResourcePath(uri); 56 | return requestMessage; 57 | } 58 | 59 | @Override 60 | protected ResultParser> buildResultParser() { 61 | return new ResultParser>() { 62 | public List parse(ResponseMessage response) throws ResultParseException { 63 | MessageListDeserializer deserializer = new MessageListDeserializer(); 64 | try { 65 | List msgs = deserializer.deserialize(response.getContent()); 66 | for (Message msg : msgs) { 67 | msg.setRequestId(response.getHeader(Constants.X_HEADER_REQUEST_ID)); 68 | } 69 | return msgs; 70 | } catch (Exception e) { 71 | throw new ResultParseException("Unmarshal error,cause by:" 72 | + e.getMessage(), e); 73 | } 74 | } 75 | }; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/action/PublishMessageAction.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.action; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.Constants; 5 | import com.aliyun.mq.http.common.HttpMethod; 6 | import com.aliyun.mq.http.common.auth.ServiceCredentials; 7 | import com.aliyun.mq.http.model.TopicMessage; 8 | import com.aliyun.mq.http.common.http.RequestMessage; 9 | import com.aliyun.mq.http.common.http.ResponseMessage; 10 | import com.aliyun.mq.http.common.http.ServiceClient; 11 | import com.aliyun.mq.http.common.parser.ResultParseException; 12 | import com.aliyun.mq.http.common.parser.ResultParser; 13 | import com.aliyun.mq.http.model.request.PublishMessageRequest; 14 | import com.aliyun.mq.http.model.serialize.TopicMessageDeserializer; 15 | import com.aliyun.mq.http.model.serialize.TopicMessageSerializer; 16 | 17 | import java.io.InputStream; 18 | import java.net.URI; 19 | 20 | public class PublishMessageAction extends AbstractAction { 21 | 22 | public PublishMessageAction(ServiceClient client, 23 | ServiceCredentials credentials, URI endpoint) { 24 | super(HttpMethod.POST, "PublishMessage", client, credentials, endpoint); 25 | } 26 | 27 | @Override 28 | protected RequestMessage buildRequest(PublishMessageRequest reqObject) 29 | throws ClientException { 30 | RequestMessage requestMessage = new RequestMessage(); 31 | if (reqObject.getInstanceId() != null && reqObject.getInstanceId() != "") { 32 | requestMessage.setResourcePath(reqObject.getRequestPath() + "?" + Constants.PARAM_NS + "=" + reqObject.getInstanceId()); 33 | } else { 34 | requestMessage.setResourcePath(reqObject.getRequestPath()); 35 | } 36 | TopicMessageSerializer serializer = new TopicMessageSerializer(); 37 | 38 | try { 39 | InputStream is = serializer.serialize(reqObject, 40 | Constants.DEFAULT_CHARSET); 41 | requestMessage.setContent(is); 42 | requestMessage.setContentLength(is.available()); 43 | } catch (Exception e) { 44 | throw new ClientException(e.getMessage(), null, e); 45 | } 46 | return requestMessage; 47 | } 48 | 49 | @Override 50 | protected ResultParser buildResultParser() { 51 | return new ResultParser() { 52 | public TopicMessage parse(ResponseMessage response) throws ResultParseException { 53 | 54 | TopicMessageDeserializer deserializer = new TopicMessageDeserializer(); 55 | try { 56 | return deserializer.deserialize(response.getContent()); 57 | } catch (Exception e) { 58 | logger.warn("Unmarshal error,cause by:" + e.getMessage()); 59 | throw new ResultParseException( 60 | "Unmarshal error,cause by:" + e.getMessage(), e); 61 | } 62 | } 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/jaxb.index: -------------------------------------------------------------------------------- 1 | ErrorMessage 2 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/request/AckMessageRequest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.request; 2 | 3 | import com.aliyun.mq.http.model.AbstractRequest; 4 | 5 | import java.util.List; 6 | 7 | public class AckMessageRequest extends AbstractRequest { 8 | private String consumer; 9 | private List receiptHandles; 10 | private String trans; 11 | 12 | public String getConsumer() { 13 | return consumer; 14 | } 15 | 16 | public void setConsumer(String consumer) { 17 | this.consumer = consumer; 18 | } 19 | 20 | public List getReceiptHandles() { 21 | return receiptHandles; 22 | } 23 | 24 | public void setReceiptHandles(List receiptHandles) { 25 | this.receiptHandles = receiptHandles; 26 | } 27 | 28 | public String getTrans() { 29 | return trans; 30 | } 31 | 32 | public void setTrans(String trans) { 33 | this.trans = trans; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/request/ConsumeMessageRequest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.request; 2 | 3 | import com.aliyun.mq.http.model.AbstractRequest; 4 | 5 | public class ConsumeMessageRequest extends AbstractRequest { 6 | private int waitSeconds = 0; 7 | private int batchSize = 1; 8 | private String tag; 9 | private String consumer; 10 | private String trans = null; 11 | 12 | public int getWaitSeconds() { 13 | return waitSeconds; 14 | } 15 | 16 | public void setWaitSeconds(int waitSeconds) { 17 | this.waitSeconds = waitSeconds; 18 | } 19 | 20 | public int getBatchSize() { 21 | return batchSize; 22 | } 23 | 24 | public void setBatchSize(int batchSize) { 25 | this.batchSize = batchSize; 26 | } 27 | 28 | public String getTag() { 29 | return tag; 30 | } 31 | 32 | public void setTag(String tag) { 33 | this.tag = tag; 34 | } 35 | 36 | public String getConsumer() { 37 | return consumer; 38 | } 39 | 40 | public void setConsumer(String consumer) { 41 | this.consumer = consumer; 42 | } 43 | 44 | public String getTrans() { 45 | return trans; 46 | } 47 | 48 | public void setTrans(String trans) { 49 | this.trans = trans; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/request/PublishMessageRequest.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.request; 2 | 3 | import com.aliyun.mq.http.model.AbstractRequest; 4 | import com.aliyun.mq.http.model.TopicMessage; 5 | 6 | 7 | public class PublishMessageRequest extends AbstractRequest { 8 | private TopicMessage message; 9 | 10 | public TopicMessage getMessage() { 11 | return message; 12 | } 13 | 14 | public void setMessage(TopicMessage message) { 15 | this.message = message; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/BaseXMLSerializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import javax.xml.parsers.DocumentBuilder; 4 | import javax.xml.parsers.DocumentBuilderFactory; 5 | import javax.xml.parsers.ParserConfigurationException; 6 | 7 | public class BaseXMLSerializer { 8 | 9 | protected static DocumentBuilderFactory factory = DocumentBuilderFactory 10 | .newInstance(); 11 | 12 | private static ThreadLocal sps = new ThreadLocal(); 13 | 14 | protected DocumentBuilder getDocmentBuilder() throws ParserConfigurationException { 15 | DocumentBuilder db = sps.get(); 16 | if (db == null) { 17 | db = factory.newDocumentBuilder(); 18 | sps.set(db); 19 | } 20 | return db; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/Deserializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import java.io.InputStream; 4 | 5 | public interface Deserializer { 6 | T deserialize(InputStream stream) throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/ErrorReceiptHandleListDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import com.aliyun.mq.http.common.AckMessageException; 4 | import com.aliyun.mq.http.common.Constants; 5 | import com.aliyun.mq.http.common.ServiceException; 6 | import com.aliyun.mq.http.model.ErrorMessageResult; 7 | import org.w3c.dom.Document; 8 | import org.w3c.dom.Element; 9 | import org.w3c.dom.NodeList; 10 | 11 | import java.io.InputStream; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class ErrorReceiptHandleListDeserializer extends XMLDeserializer { 16 | @Override 17 | public Exception deserialize(InputStream stream) throws Exception { 18 | 19 | // byte[] bytes = new byte[1024]; 20 | // while(stream.read(bytes, 0, stream.available())>0){ 21 | // System.out.println(new String(bytes)); 22 | // } 23 | Document doc = getDocmentBuilder().parse(stream); 24 | Exception ret = null; 25 | Element root = doc.getDocumentElement(); 26 | 27 | if (root != null) { 28 | String rootName = root.getNodeName(); 29 | 30 | if (rootName == Constants.ERROR_LIST_TAG) { 31 | NodeList list = doc.getElementsByTagName(Constants.ERROR_TAG); 32 | if (list != null && list.getLength() > 0) { 33 | Map results = new HashMap(8); 34 | 35 | for (int i = 0; i < list.getLength(); i++) { 36 | String receiptHandle = parseReceiptHandle((Element) list.item(i)); 37 | ErrorMessageResult result = parseErrorMessageResult((Element) list.item(i)); 38 | results.put(receiptHandle, result); 39 | 40 | } 41 | ret = new AckMessageException(results); 42 | } 43 | } else if (rootName == Constants.ERROR_TAG) { 44 | String code = safeGetElementContent(root, Constants.ERROR_CODE_TAG, ""); 45 | String message = safeGetElementContent(root, Constants.ERROR_MESSAGE_TAG, ""); 46 | String requestId = safeGetElementContent(root, Constants.ERROR_REQUEST_ID_TAG, ""); 47 | String hostId = safeGetElementContent(root, Constants.ERROR_HOST_ID_TAG, ""); 48 | ret = new ServiceException(message, null, code, requestId, hostId); 49 | } 50 | } 51 | return ret; 52 | } 53 | 54 | private String parseReceiptHandle(Element root) { 55 | return safeGetElementContent(root, Constants.RECEIPT_HANDLE_TAG, 56 | null); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/MessageListDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import com.aliyun.mq.http.common.Constants; 5 | import com.aliyun.mq.http.model.Message; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Element; 8 | import org.w3c.dom.NodeList; 9 | 10 | import java.io.InputStream; 11 | import java.io.UnsupportedEncodingException; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class MessageListDeserializer extends XMLDeserializer> { 16 | @Override 17 | public List deserialize(InputStream stream) throws Exception { 18 | 19 | // byte[] bytes = new byte[1024]; 20 | // while(stream.read(bytes, 0, stream.available())>0){ 21 | // System.out.println(new String(bytes)); 22 | // } 23 | Document doc = getDocmentBuilder().parse(stream); 24 | return deserialize(doc); 25 | 26 | } 27 | 28 | public List deserialize(Document doc) { 29 | NodeList list = doc.getElementsByTagName(Constants.MESSAGE_TAG); 30 | if (list != null && list.getLength() > 0) { 31 | List results = new ArrayList(); 32 | 33 | for (int i = 0; i < list.getLength(); i++) { 34 | Message msg = parseMessage((Element) list.item(i)); 35 | results.add(msg); 36 | } 37 | return results; 38 | } 39 | return null; 40 | } 41 | 42 | private Message parseMessage(Element root) throws ClientException { 43 | Message message = new Message(); 44 | 45 | String messageId = safeGetElementContent(root, Constants.MESSAGE_ID_TAG, null); 46 | if (messageId == null) { 47 | message.setErrorMessage(parseErrorMessageResult(root)); 48 | return message; 49 | } 50 | 51 | message.setMessageId(messageId); 52 | String messageBody = safeGetElementContent(root, Constants.MESSAGE_BODY_TAG, null); 53 | if (messageBody != null) { 54 | try { 55 | message.setMessageBody(messageBody); 56 | } catch (UnsupportedEncodingException e) { 57 | throw new RuntimeException("Not support enconding:" + Constants.DEFAULT_CHARSET); 58 | } 59 | } 60 | 61 | String messageBodyMD5 = safeGetElementContent(root, 62 | Constants.MESSAGE_BODY_MD5_TAG, null); 63 | message.setMessageBodyMD5(messageBodyMD5); 64 | 65 | String receiptHandle = safeGetElementContent(root, Constants.RECEIPT_HANDLE_TAG, 66 | null); 67 | message.setReceiptHandle(receiptHandle); 68 | 69 | String publishTime = safeGetElementContent(root, Constants.PUBLISH_TIME_TAG, null); 70 | if (publishTime != null) { 71 | message.setPublishTime(Long.parseLong(publishTime)); 72 | } 73 | 74 | String nextConsumeTime = safeGetElementContent(root, Constants.NEXT_CONSUME_TIME_TAG, null); 75 | if (nextConsumeTime != null) { 76 | message.setNextConsumeTime(Long.parseLong(nextConsumeTime)); 77 | } 78 | 79 | String firstConsumeTime = safeGetElementContent(root, Constants.FIRST_CONSUME_TIME_TAG, null); 80 | if (firstConsumeTime != null) { 81 | message.setFirstConsumeTime(Long.parseLong(firstConsumeTime)); 82 | } 83 | 84 | String consumedTimes = safeGetElementContent(root, Constants.CONSUMED_TIMES_TAG, 85 | null); 86 | if (consumedTimes != null) { 87 | message.setConsumedTimes(Integer.parseInt(consumedTimes)); 88 | } 89 | 90 | message.setMessageTag(safeGetElementContent(root, Constants.MESSAGE_TAG_TAG, null)); 91 | 92 | String properties = safeGetElementContent(root, Constants.MESSAGE_PROPERTIES, 93 | null); 94 | if (properties != null) { 95 | message.setProperties(XmlUtil.stringTopMap(properties)); 96 | } 97 | 98 | return message; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/ReceiptHandleListSerializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | import com.aliyun.mq.http.model.serialize.XMLSerializer; 5 | import com.aliyun.mq.http.model.serialize.XmlUtil; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Element; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.InputStream; 11 | import java.util.List; 12 | 13 | public class ReceiptHandleListSerializer extends XMLSerializer> { 14 | 15 | @Override 16 | public InputStream serialize(List receipts, String encoding) throws Exception { 17 | Document doc = getDocmentBuilder().newDocument(); 18 | 19 | Element root = doc.createElementNS(Constants.DEFAULT_XML_NAMESPACE, Constants.RECEIPT_HANDLE_LIST_TAG); 20 | 21 | doc.appendChild(root); 22 | 23 | for (String receipt : receipts) { 24 | Element node = safeCreateContentElement(doc, Constants.RECEIPT_HANDLE_TAG, 25 | receipt, ""); 26 | 27 | if (node != null) { 28 | root.appendChild(node); 29 | } 30 | } 31 | String xml = XmlUtil.xmlNodeToString(doc, encoding); 32 | 33 | return new ByteArrayInputStream(xml.getBytes(encoding)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import java.io.InputStream; 4 | 5 | public interface Serializer { 6 | InputStream serialize(T obj, String encoding) throws Exception; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/TopicMessageDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | 4 | import com.aliyun.mq.http.common.ClientException; 5 | import com.aliyun.mq.http.common.Constants; 6 | import com.aliyun.mq.http.model.TopicMessage; 7 | import com.aliyun.mq.http.model.serialize.XMLDeserializer; 8 | import org.w3c.dom.Document; 9 | import org.w3c.dom.Element; 10 | 11 | import java.io.InputStream; 12 | 13 | public class TopicMessageDeserializer extends XMLDeserializer { 14 | public TopicMessageDeserializer() { 15 | } 16 | 17 | public TopicMessage deserialize(InputStream stream) throws Exception { 18 | Document doc = getDocmentBuilder().parse(stream); 19 | 20 | Element root = doc.getDocumentElement(); 21 | return parseMessage(root); 22 | 23 | 24 | } 25 | 26 | private TopicMessage parseMessage(Element root) throws ClientException { 27 | TopicMessage message = new TopicMessage(); 28 | 29 | String messageId = safeGetElementContent(root, Constants.MESSAGE_ID_TAG, null); 30 | message.setMessageId(messageId); 31 | 32 | String messageBodyMD5 = safeGetElementContent(root, 33 | Constants.MESSAGE_BODY_MD5_TAG, null); 34 | message.setMessageBodyMD5(messageBodyMD5); 35 | 36 | String handle = safeGetElementContent(root, Constants.RECEIPT_HANDLE_TAG, null); 37 | message.setReceiptHandle(handle); 38 | 39 | return message; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/TopicMessageSerializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | import com.aliyun.mq.http.model.TopicMessage; 5 | import com.aliyun.mq.http.model.request.PublishMessageRequest; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Element; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.InputStream; 11 | 12 | public class TopicMessageSerializer extends XMLSerializer { 13 | 14 | @Override 15 | public InputStream serialize(PublishMessageRequest request, String encoding) throws Exception { 16 | Document doc = getDocmentBuilder().newDocument(); 17 | 18 | TopicMessage msg = request.getMessage(); 19 | Element root = doc.createElementNS(Constants.DEFAULT_XML_NAMESPACE, Constants.MESSAGE_TAG); 20 | doc.appendChild(root); 21 | 22 | Element node = safeCreateContentElement(doc, Constants.MESSAGE_BODY_TAG, 23 | msg.getMessageBodyString(), ""); 24 | if (node != null) { 25 | root.appendChild(node); 26 | } 27 | 28 | if (msg.getMessageTag() != null && msg.getMessageTag().length() > 0) { 29 | node = safeCreateContentElement(doc, Constants.MESSAGE_TAG_TAG, msg.getMessageTag(), null); 30 | if (node != null) { 31 | root.appendChild(node); 32 | } 33 | } 34 | 35 | if (msg.getProperties() != null && !msg.getProperties().isEmpty()) { 36 | node = safeCreateContentElement(doc, Constants.MESSAGE_PROPERTIES, XmlUtil.mapToString(msg.getProperties()), null); 37 | if (node != null) { 38 | root.appendChild(node); 39 | } 40 | } 41 | 42 | String xml = XmlUtil.xmlNodeToString(doc, encoding); 43 | 44 | return new ByteArrayInputStream(xml.getBytes(encoding)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/XMLDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import com.aliyun.mq.http.common.Constants; 4 | import com.aliyun.mq.http.model.ErrorMessageResult; 5 | import org.w3c.dom.Element; 6 | import org.w3c.dom.Node; 7 | import org.w3c.dom.NodeList; 8 | 9 | public abstract class XMLDeserializer extends BaseXMLSerializer implements Deserializer { 10 | 11 | public String safeGetElementContent(Element root, String tagName, 12 | String defualValue) { 13 | NodeList nodes = root.getElementsByTagName(tagName); 14 | if (nodes != null) { 15 | Node node = nodes.item(0); 16 | if (node == null) { 17 | return defualValue; 18 | } else { 19 | return node.getTextContent(); 20 | } 21 | } 22 | return defualValue; 23 | } 24 | 25 | protected ErrorMessageResult parseErrorMessageResult(Element root) { 26 | ErrorMessageResult result = new ErrorMessageResult(); 27 | String errorCode = safeGetElementContent(root, Constants.MESSAGE_ERRORCODE_TAG, 28 | null); 29 | result.setErrorCode(errorCode); 30 | 31 | String errorMessage = safeGetElementContent(root, 32 | Constants.MESSAGE_ERRORMESSAGE_TAG, null); 33 | result.setErrorMessage(errorMessage); 34 | return result; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/XMLSerializer.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.Element; 5 | 6 | public abstract class XMLSerializer extends BaseXMLSerializer implements Serializer { 7 | 8 | public static Element safeCreateContentElement(Document doc, String tagName, 9 | Object value, String defaultValue) { 10 | if (value == null && defaultValue == null) { 11 | return null; 12 | } 13 | 14 | Element node = doc.createElement(tagName); 15 | if (value != null) { 16 | node.setTextContent(value.toString()); 17 | } else { 18 | node.setTextContent(defaultValue); 19 | } 20 | return node; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/aliyun/mq/http/model/serialize/XmlUtil.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.mq.http.model.serialize; 2 | 3 | import com.aliyun.mq.http.common.ClientException; 4 | import org.w3c.dom.Node; 5 | 6 | import javax.xml.transform.Transformer; 7 | import javax.xml.transform.TransformerException; 8 | import javax.xml.transform.TransformerFactory; 9 | import javax.xml.transform.dom.DOMSource; 10 | import javax.xml.transform.stream.StreamResult; 11 | import java.io.OutputStream; 12 | import java.io.StringWriter; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class XmlUtil { 17 | private static TransformerFactory transFactory = TransformerFactory.newInstance(); 18 | public static final String PROPERTY_SEPARATOR = "|"; 19 | public static final String PROPERTY_SEPARATOR_SPLIT = "\\|"; 20 | public static final String PROPERTY_KV_SEPARATOR = ":"; 21 | public static final String PROPERTY_KV_SEPARATOR_SPLIT = "\\:"; 22 | 23 | 24 | public static void output(Node node, String encoding, 25 | OutputStream outputStream) throws TransformerException { 26 | Transformer transformer = transFactory.newTransformer(); 27 | transformer.setOutputProperty("encoding", encoding); 28 | 29 | DOMSource source = new DOMSource(); 30 | source.setNode(node); 31 | 32 | StreamResult result = new StreamResult(); 33 | result.setOutputStream(outputStream); 34 | 35 | transformer.transform(source, result); 36 | } 37 | 38 | public static String xmlNodeToString(Node node, String encoding) 39 | throws TransformerException { 40 | Transformer transformer = transFactory.newTransformer(); 41 | transformer.setOutputProperty("encoding", encoding); 42 | StringWriter strWtr = new StringWriter(); 43 | 44 | DOMSource source = new DOMSource(); 45 | source.setNode(node); 46 | StreamResult result = new StreamResult(strWtr); 47 | transformer.transform(source, result); 48 | return strWtr.toString(); 49 | 50 | } 51 | 52 | public static String mapToString(Map properties) { 53 | StringBuilder sb = new StringBuilder(); 54 | if (properties != null) { 55 | for (final Map.Entry entry : properties.entrySet()) { 56 | final String name = entry.getKey(); 57 | final String value = entry.getValue(); 58 | 59 | checkPropValid(name, value); 60 | 61 | sb.append(name); 62 | sb.append(PROPERTY_KV_SEPARATOR); 63 | sb.append(value); 64 | sb.append(PROPERTY_SEPARATOR); 65 | } 66 | } 67 | return sb.toString(); 68 | } 69 | 70 | public static Map stringTopMap(String input) { 71 | Map map = new HashMap(); 72 | if (input != null) { 73 | String[] items = input.split(PROPERTY_SEPARATOR_SPLIT); 74 | for (String i : items) { 75 | String[] nv = i.split(PROPERTY_KV_SEPARATOR_SPLIT); 76 | if (2 == nv.length) { 77 | map.put(nv[0], nv[1]); 78 | } 79 | } 80 | } 81 | return map; 82 | } 83 | 84 | public static void checkPropValid(String key, String value) { 85 | if (key == null || key.length() <= 0 86 | || value == null || value.length() <=0) { 87 | throw new ClientException("Message's property can't be null or empty", "Local"); 88 | } 89 | 90 | if (isContainSpecialChar(key) || isContainSpecialChar(value)) { 91 | throw new ClientException("Message's property[" + key + ":" + value 92 | + "] can't contains: & \" ' < > : |", "LocalClientError"); 93 | } 94 | } 95 | 96 | public static boolean isContainSpecialChar(String str) { 97 | return str.contains("&") || str.contains("\"") || str.contains("'") 98 | || str.contains("<") || str.contains(">") 99 | || str.contains(XmlUtil.PROPERTY_KV_SEPARATOR) 100 | || str.contains(XmlUtil.PROPERTY_SEPARATOR); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/resources/common.properties: -------------------------------------------------------------------------------- 1 | ConnectionError=Connection error due to: {0} 2 | EncodingFailed=Encoding failed due to: {0} 3 | FailedToParseResponse=Failed to parse the response result. 4 | ParameterIsNull=The parameter "{0}" is null. 5 | ParameterStringIsEmpty=The parameter "{0}" is a zero-length string. 6 | ServerReturnsUnknownError=The server returns an unknown error. -------------------------------------------------------------------------------- /src/main/resources/common_zh_CN.properties: -------------------------------------------------------------------------------- 1 | ConnectionError=\u7f51\u7edc\u8fde\u63a5\u9519\u8bef\uff0c\u8be6\u7ec6\u4fe1\u606f\uff1a{0} EncodingFailed=\u7f16\u7801\u5931\u8d25\uff1a {0} FailedToParseResponse=\u8fd4\u56de\u7ed3\u679c\u65e0\u6548\uff0c\u65e0\u6cd5\u89e3\u6790\u3002 ParameterIsNull=\u53c2\u6570"{0}"\u4e3a\u7a7a\u6307\u9488\u3002 ParameterStringIsEmpty=\u53c2\u6570"{0}"\u662f\u957f\u5ea6\u4e3a0\u7684\u5b57\u7b26\u4e32\u3002 ServerReturnsUnknownError=\u670d\u52a1\u5668\u8fd4\u56de\u672a\u77e5\u9519\u8bef\u3002 --------------------------------------------------------------------------------