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