Java tutorial
/* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.carbon.connector.integration.test.amazonsqs; import org.apache.synapse.MessageContext; import org.apache.synapse.SynapseConstants; import org.json.JSONException; import org.json.JSONObject; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; /** * Class AmazonSQSAuthConnector which helps to generate authentication signature for Amazon SQS WSO2 * ESB Connector. */ public class AmazonSQSAuthConnector { /** * Connect method which is generating authentication of the connector for each request. * * @param messageContext ESB messageContext. * @throws java.io.UnsupportedEncodingException * @throws IllegalStateException * @throws java.security.NoSuchAlgorithmException * @throws java.security.InvalidKeyException * @throws JSONException */ public final Map<String, String> getRequestPayload(final JSONObject signatureRequestObject) throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException, JSONException { final StringBuilder canonicalRequest = new StringBuilder(); final StringBuilder stringToSign = new StringBuilder(); final StringBuilder payloadBuilder = new StringBuilder(); final StringBuilder payloadStrBuilder = new StringBuilder(); final StringBuilder authHeader = new StringBuilder(); init(signatureRequestObject); // Generate time-stamp which will be sent to API and to be used in Signature final Date date = new Date(); final TimeZone timeZone = TimeZone.getTimeZone(AmazonSQSConstants.GMT); final DateFormat dateFormat = new SimpleDateFormat(AmazonSQSConstants.ISO8601_BASIC_DATE_FORMAT); dateFormat.setTimeZone(timeZone); final String amzDate = dateFormat.format(date); final DateFormat shortDateFormat = new SimpleDateFormat(AmazonSQSConstants.SHORT_DATE_FORMAT); shortDateFormat.setTimeZone(timeZone); final String shortDate = shortDateFormat.format(date); signatureRequestObject.put(AmazonSQSConstants.AMZ_DATE, amzDate); final Map<String, String> parameterNamesMap = getParameterNamesMap(); final Map<String, String> parametersMap = getSortedParametersMap(signatureRequestObject, parameterNamesMap); canonicalRequest.append(signatureRequestObject.get(AmazonSQSConstants.HTTP_METHOD)); canonicalRequest.append(AmazonSQSConstants.NEW_LINE); final String charSet = Charset.defaultCharset().toString(); if (signatureRequestObject.has(AmazonSQSConstants.URL_QUEUE_NAME) && !("").equals(signatureRequestObject.get(AmazonSQSConstants.URL_QUEUE_NAME)) && signatureRequestObject.has(AmazonSQSConstants.QUEUE_ID) && !("").equals(signatureRequestObject.get(AmazonSQSConstants.QUEUE_ID))) { // queue ID and queue name should be encoded twise to match the Signature being generated by API, // Note that API it looks encodes the incoming URL once before creating the signature, SInce // we send url encoded URLs, API signatures are twise encoded final String encodedQueueID = URLEncoder .encode(signatureRequestObject.get(AmazonSQSConstants.QUEUE_ID).toString(), charSet); final String encodedQueueName = URLEncoder .encode(signatureRequestObject.get(AmazonSQSConstants.URL_QUEUE_NAME).toString(), charSet); canonicalRequest.append((AmazonSQSConstants.FORWARD_SLASH + URLEncoder.encode(encodedQueueID, charSet) + AmazonSQSConstants.FORWARD_SLASH + URLEncoder.encode(encodedQueueName, charSet) + AmazonSQSConstants.FORWARD_SLASH).replaceAll(AmazonSQSConstants.REGEX_ASTERISK, AmazonSQSConstants.URL_ENCODED_ASTERISK)); // Sets the http request Uri to message context signatureRequestObject.put(AmazonSQSConstants.HTTP_REQUEST_URI, AmazonSQSConstants.FORWARD_SLASH + encodedQueueID + AmazonSQSConstants.FORWARD_SLASH + encodedQueueName + AmazonSQSConstants.FORWARD_SLASH); } else { canonicalRequest.append(AmazonSQSConstants.FORWARD_SLASH); } canonicalRequest.append(AmazonSQSConstants.NEW_LINE); final Set<String> keySet = parametersMap.keySet(); for (String key : keySet) { payloadBuilder.append(URLEncoder.encode(key, charSet)); payloadBuilder.append(AmazonSQSConstants.EQUAL); payloadBuilder.append(URLEncoder.encode(parametersMap.get(key), charSet)); payloadBuilder.append(AmazonSQSConstants.AMPERSAND); payloadStrBuilder.append(AmazonSQSConstants.QUOTE); payloadStrBuilder.append(key); payloadStrBuilder.append(AmazonSQSConstants.QUOTE); payloadStrBuilder.append(AmazonSQSConstants.COLON); payloadStrBuilder.append(AmazonSQSConstants.QUOTE); payloadStrBuilder.append(parametersMap.get(key)); payloadStrBuilder.append(AmazonSQSConstants.QUOTE); payloadStrBuilder.append(AmazonSQSConstants.COMMA); } // Adds authorization header to message context, removes additionally appended comma at the end if (payloadStrBuilder.length() > 0) { signatureRequestObject.put(AmazonSQSConstants.REQUEST_PAYLOAD, payloadStrBuilder.substring(0, payloadStrBuilder.length() - 1)); } // Appends empty string since no URL parameters are used in POST API requests canonicalRequest.append(""); canonicalRequest.append(AmazonSQSConstants.NEW_LINE); final Map<String, String> headersMap = getSortedHeadersMap(signatureRequestObject, parameterNamesMap); final StringBuilder canonicalHeaders = new StringBuilder(); final StringBuilder signedHeader = new StringBuilder(); final Set<String> keysSet = headersMap.keySet(); for (String key : keysSet) { canonicalHeaders.append(key); canonicalHeaders.append(AmazonSQSConstants.COLON); canonicalHeaders.append(headersMap.get(key)); canonicalHeaders.append(AmazonSQSConstants.NEW_LINE); signedHeader.append(key); signedHeader.append(AmazonSQSConstants.SEMI_COLON); } canonicalRequest.append(canonicalHeaders.toString()); canonicalRequest.append(AmazonSQSConstants.NEW_LINE); // Remove unwanted semi-colon at the end of the signedHeader string String signedHeaders = ""; if (signedHeader.length() > 0) { signedHeaders = signedHeader.substring(0, signedHeader.length() - 1); } canonicalRequest.append(signedHeaders); canonicalRequest.append(AmazonSQSConstants.NEW_LINE); // HashedPayload = HexEncode(Hash(requestPayload)) String requestPayload = ""; if (payloadBuilder.length() > 0) { /* * First removes the additional ampersand appended to the end of the payloadBuilder, then o * further modifications to preserve unreserved characters as per the API guide * (http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html) */ requestPayload = payloadBuilder.substring(0, payloadBuilder.length() - 1).toString() .replace(AmazonSQSConstants.PLUS, AmazonSQSConstants.URL_ENCODED_PLUS) .replace(AmazonSQSConstants.URL_ENCODED_TILT, AmazonSQSConstants.TILT) .replace(AmazonSQSConstants.ASTERISK, AmazonSQSConstants.URL_ENCODED_ASTERISK); } canonicalRequest.append(bytesToHex(hash(requestPayload)).toLowerCase()); stringToSign.append(AmazonSQSConstants.AWS4_HMAC_SHA_256); stringToSign.append(AmazonSQSConstants.NEW_LINE); stringToSign.append(amzDate); stringToSign.append(AmazonSQSConstants.NEW_LINE); stringToSign.append(shortDate); stringToSign.append(AmazonSQSConstants.FORWARD_SLASH); stringToSign.append(signatureRequestObject.get(AmazonSQSConstants.REGION)); stringToSign.append(AmazonSQSConstants.FORWARD_SLASH); stringToSign.append(signatureRequestObject.get(AmazonSQSConstants.SERVICE)); stringToSign.append(AmazonSQSConstants.FORWARD_SLASH); stringToSign.append(signatureRequestObject.get(AmazonSQSConstants.TERMINATION_STRING)); stringToSign.append(AmazonSQSConstants.NEW_LINE); stringToSign.append(bytesToHex(hash(canonicalRequest.toString())).toLowerCase()); final byte[] signingKey = getSignatureKey(signatureRequestObject, signatureRequestObject.get(AmazonSQSConstants.SECRET_ACCESS_KEY).toString(), shortDate, signatureRequestObject.get(AmazonSQSConstants.REGION).toString(), signatureRequestObject.get(AmazonSQSConstants.SERVICE).toString()); // Construction of authorization header value to be in cluded in API request authHeader.append(AmazonSQSConstants.AWS4_HMAC_SHA_256); authHeader.append(AmazonSQSConstants.COMMA); authHeader.append(AmazonSQSConstants.CREDENTIAL); authHeader.append(AmazonSQSConstants.EQUAL); authHeader.append(signatureRequestObject.get(AmazonSQSConstants.ACCESS_KEY_ID)); authHeader.append(AmazonSQSConstants.FORWARD_SLASH); authHeader.append(shortDate); authHeader.append(AmazonSQSConstants.FORWARD_SLASH); authHeader.append(signatureRequestObject.get(AmazonSQSConstants.REGION)); authHeader.append(AmazonSQSConstants.FORWARD_SLASH); authHeader.append(signatureRequestObject.get(AmazonSQSConstants.SERVICE)); authHeader.append(AmazonSQSConstants.FORWARD_SLASH); authHeader.append(signatureRequestObject.get(AmazonSQSConstants.TERMINATION_STRING)); authHeader.append(AmazonSQSConstants.COMMA); authHeader.append(AmazonSQSConstants.SIGNED_HEADERS); authHeader.append(AmazonSQSConstants.EQUAL); authHeader.append(signedHeaders); authHeader.append(AmazonSQSConstants.COMMA); authHeader.append(AmazonSQSConstants.API_SIGNATURE); authHeader.append(AmazonSQSConstants.EQUAL); authHeader.append(bytesToHex(hmacSHA256(signingKey, stringToSign.toString())).toLowerCase()); // Adds authorization header to message context signatureRequestObject.put(AmazonSQSConstants.AUTHORIZATION_HEADER, authHeader.toString()); Map<String, String> responseMap = new HashMap<String, String>(); responseMap.put(AmazonSQSConstants.AUTHORIZATION_HEADER, authHeader.toString()); responseMap.put(AmazonSQSConstants.AMZ_DATE, amzDate); responseMap.put(AmazonSQSConstants.REQUEST_PAYLOAD, requestPayload); return responseMap; } /** * @param signatureRequestObject * @throws JSONException */ private void init(JSONObject signatureRequestObject) throws JSONException { signatureRequestObject.put(AmazonSQSConstants.SERVICE, "sqs"); signatureRequestObject.put(AmazonSQSConstants.SIGNATURE_METHOD, "HmacSHA256"); signatureRequestObject.put(AmazonSQSConstants.SIGNATURE_VERSION, "4"); signatureRequestObject.put(AmazonSQSConstants.CONTENT_TYPE, "application/x-www-form-urlencoded"); signatureRequestObject.put(AmazonSQSConstants.HTTP_METHOD, "POST"); signatureRequestObject.put(AmazonSQSConstants.TERMINATION_STRING, "aws4_request"); signatureRequestObject.put(AmazonSQSConstants.HOST, "sqs." + signatureRequestObject.get(AmazonSQSConstants.REGION) + ".amazonaws.com"); } /** * getKeys method returns a list of parameter keys. * * @return list of parameter key value. */ private String[] getParameterKeys() { return new String[] { AmazonSQSConstants.ACTION, AmazonSQSConstants.EXPIRES, AmazonSQSConstants.SECURITY_TOKEN, AmazonSQSConstants.SIGNATURE, AmazonSQSConstants.SIGNATURE_METHOD, AmazonSQSConstants.SIGNATURE_VERSION, AmazonSQSConstants.TIMESTAMP, AmazonSQSConstants.VERSION, AmazonSQSConstants.QUEUE_NAME_PREFIX, AmazonSQSConstants.QUEUE_URLS, AmazonSQSConstants.ACCESS_KEY_ID, AmazonSQSConstants.PAYLOAD_QUEUE_NAME, AmazonSQSConstants.LABEL, AmazonSQSConstants.MESSAGE_BODY, AmazonSQSConstants.LABEL, AmazonSQSConstants.MESSAGE_BODY, AmazonSQSConstants.RECEIPT_HANDLE, AmazonSQSConstants.MAX_NO_OF_MESSAGES, AmazonSQSConstants.VISIBILITY_TIMEOUT, AmazonSQSConstants.WAIT_TIME_SECONDS, AmazonSQSConstants.DELAY_SECONDS, AmazonSQSConstants.API_ACCOUNT_ID }; } /** * getKeys method returns a list of header keys. * * @return list of header key value. */ private String[] getHeaderKeys() { return new String[] { AmazonSQSConstants.HOST, AmazonSQSConstants.CONTENT_TYPE, AmazonSQSConstants.AMZ_DATE, }; } /** * getCollectionParameterKeys method returns a list of predefined parameter keys which users will be used. * to send collection of values in each parameter. * * @return list of parameter key value. */ private String[] getMultivaluedParameterKeys() { return new String[] { AmazonSQSConstants.AWS_ACCOUNT_NUMBERS, AmazonSQSConstants.ACTION_NAMES, AmazonSQSConstants.REQUEST_ENTRIES, AmazonSQSConstants.ATTRIBUTE_ENTRIES, AmazonSQSConstants.ATTRIBUTES, AmazonSQSConstants.MESSAGE_ATTRIBUTE_NAMES, AmazonSQSConstants.MESSAGE_ATTRIBUTES, AmazonSQSConstants.MESSAGE_REQUEST_ENTRY }; } /** * getParametersMap method used to return list of parameter values sorted by expected API parameter names. * * @param signatureRequestObject ESB messageContext. * @param namesMap contains a map of esb parameter names and matching API parameter names * @return assigned parameter values as a HashMap. * @throws JSONException */ private Map<String, String> getSortedParametersMap(final JSONObject signatureRequestObject, final Map<String, String> namesMap) throws JSONException { final String[] singleValuedKeys = getParameterKeys(); final Map<String, String> parametersMap = new TreeMap<String, String>(); // Stores sorted, single valued API parameters for (byte index = 0; index < singleValuedKeys.length; index++) { final String key = singleValuedKeys[index]; // builds the parameter map only if provided by the user if (signatureRequestObject.has(key) && !("").equals((String) signatureRequestObject.get(key))) { parametersMap.put(namesMap.get(key), (String) signatureRequestObject.get(key)); } } final String[] multiValuedKeys = getMultivaluedParameterKeys(); // Stores sorted, multi-valued API parameters for (byte index = 0; index < multiValuedKeys.length; index++) { final String key = multiValuedKeys[index]; // builds the parameter map only if provided by the user if (signatureRequestObject.has(key) && !("").equals((String) signatureRequestObject.get(key))) { final String collectionParam = (String) signatureRequestObject.get(key); // Splits the collection parameter to retrieve parameters separately final String[] keyValuepairs = collectionParam.split(AmazonSQSConstants.AMPERSAND); for (String keyValue : keyValuepairs) { if (keyValue.contains(AmazonSQSConstants.EQUAL) && keyValue.split(AmazonSQSConstants.EQUAL).length == AmazonSQSConstants.TWO) { // Split the key and value of parameters to be sent to API parametersMap.put(keyValue.split(AmazonSQSConstants.EQUAL)[0], keyValue.split(AmazonSQSConstants.EQUAL)[1]); } else { throw new IllegalArgumentException(); } } } } return parametersMap; } /** * getSortedHeadersMap method used to return list of header values sorted by expected API parameter names. * * @param signatureRequestObject ESB messageContext. * @param namesMap contains a map of esb parameter names and matching API parameter names * @return assigned header values as a HashMap. * @throws JSONException */ private Map<String, String> getSortedHeadersMap(final JSONObject signatureRequestObject, final Map<String, String> namesMap) throws JSONException { final String[] headerKeys = getHeaderKeys(); final Map<String, String> parametersMap = new TreeMap<String, String>(); // Stores sorted, single valued API parameters for (byte index = 0; index < headerKeys.length; index++) { final String key = headerKeys[index]; // builds the parameter map only if provided by the user if (signatureRequestObject.has(key) && !("").equals((String) signatureRequestObject.get(key))) { parametersMap.put(namesMap.get(key).toLowerCase(), signatureRequestObject.get(key).toString().trim() .replaceAll(AmazonSQSConstants.TRIM_SPACE_REGEX, AmazonSQSConstants.SPACE)); } } return parametersMap; } /** * getparameterNamesMap returns a map of esb parameter names and corresponding API parameter names. * * @return generated map. */ private Map<String, String> getParameterNamesMap() { final Map<String, String> map = new HashMap<String, String>(); map.put(AmazonSQSConstants.ACTION, AmazonSQSConstants.API_ACTION); map.put(AmazonSQSConstants.EXPIRES, AmazonSQSConstants.API_EXPIRES); map.put(AmazonSQSConstants.SECURITY_TOKEN, AmazonSQSConstants.API_SECURITY_TOKEN); map.put(AmazonSQSConstants.SIGNATURE, AmazonSQSConstants.API_SIGNATURE); map.put(AmazonSQSConstants.SIGNATURE_METHOD, AmazonSQSConstants.API_SIGNATURE_METHOD); map.put(AmazonSQSConstants.SIGNATURE_VERSION, AmazonSQSConstants.API_SIGNATURE_VERSION); map.put(AmazonSQSConstants.TIMESTAMP, AmazonSQSConstants.API_TIMESTAMP); map.put(AmazonSQSConstants.VERSION, AmazonSQSConstants.API_VERSION); map.put(AmazonSQSConstants.ACCESS_KEY_ID, AmazonSQSConstants.AWS_ACCESS_KEY_ID); map.put(AmazonSQSConstants.QUEUE_NAME_PREFIX, AmazonSQSConstants.API_QUEUE_NAME_PREFIX); map.put(AmazonSQSConstants.QUEUE_URLS, AmazonSQSConstants.API_QUEUE_URLS); map.put(AmazonSQSConstants.LABEL, AmazonSQSConstants.API_LABEL); map.put(AmazonSQSConstants.PAYLOAD_QUEUE_NAME, AmazonSQSConstants.API_QUEUE_NAME); map.put(AmazonSQSConstants.MESSAGE_BODY, AmazonSQSConstants.API_MESSAGE_BODY); map.put(AmazonSQSConstants.RECEIPT_HANDLE, AmazonSQSConstants.API_RECEIPT_HANDLE); map.put(AmazonSQSConstants.MAX_NO_OF_MESSAGES, AmazonSQSConstants.API_MAX_NO_OF_MESSAGES); map.put(AmazonSQSConstants.VISIBILITY_TIMEOUT, AmazonSQSConstants.API_VISIBILITY_TIMEOUT); map.put(AmazonSQSConstants.WAIT_TIME_SECONDS, AmazonSQSConstants.API_WAIT_TIME_SECONDS); map.put(AmazonSQSConstants.DELAY_SECONDS, AmazonSQSConstants.API_DELAY_SECONDS); map.put(AmazonSQSConstants.ACCOUNT_ID, AmazonSQSConstants.API_ACCOUNT_ID); // Header parameters map.put(AmazonSQSConstants.HOST, AmazonSQSConstants.API_HOST); map.put(AmazonSQSConstants.CONTENT_TYPE, AmazonSQSConstants.API_CONTENT_TYPE); map.put(AmazonSQSConstants.AMZ_DATE, AmazonSQSConstants.API_AMZ_DATE); return map; } /** * Add a Throwable to a message context, the message from the throwable is embedded as the Synapse. * Constant ERROR_MESSAGE. * * @param ctxt message context to which the error tags need to be added * @param throwable Throwable that needs to be parsed and added * @param errorCode errorCode mapped to the exception */ public final void storeErrorResponseStatus(final MessageContext ctxt, final Throwable throwable, final int errorCode) { ctxt.setProperty(SynapseConstants.ERROR_CODE, errorCode); ctxt.setProperty(SynapseConstants.ERROR_MESSAGE, throwable.getMessage()); ctxt.setFaultResponse(true); } /** * Add a message to message context, the message from the throwable is embedded as the Synapse Constant * ERROR_MESSAGE. * * @param ctxt message context to which the error tags need to be added * @param message message to be returned to the user * @param errorCode errorCode mapped to the exception */ public final void storeErrorResponseStatus(final MessageContext ctxt, final String message, final int errorCode) { ctxt.setProperty(SynapseConstants.ERROR_CODE, errorCode); ctxt.setProperty(SynapseConstants.ERROR_MESSAGE, message); ctxt.setFaultResponse(true); } /** * Hashes the string contents (assumed to be UTF-8) using the SHA-256 algorithm. * * @param messageContext of the connector * @param text text to be hashed * * @return SHA-256 hashed text * @throws java.security.NoSuchAlgorithmException * @throws java.io.UnsupportedEncodingException */ public final byte[] hash(final String text) throws NoSuchAlgorithmException, UnsupportedEncodingException { MessageDigest messageDigest = null; messageDigest = MessageDigest.getInstance(AmazonSQSConstants.SHA_256); messageDigest.update(text.getBytes(AmazonSQSConstants.UTF_8)); return messageDigest.digest(); } /** * bytesToHex method HexEncoded the received byte array. * * @param bytes bytes to be hex encoded * @return hex encoded String of the given byte array */ public static String bytesToHex(final byte[] bytes) { final char[] hexArray = AmazonSQSConstants.HEX_ARRAY_STRING.toCharArray(); char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { final int byteVal = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[byteVal >>> 4]; hexChars[j * 2 + 1] = hexArray[byteVal & 0x0F]; } return new String(hexChars); } /** * Returns the encoded signature key to be used for further encodings as per API doc. * * @param signatureRequestObject message context of the connector * @param key key to be used for signing * @param dateStamp current date stamp * @param regionName region name given to the connector * @param serviceName Name of the service being addressed * * @return Signature key * @throws java.io.UnsupportedEncodingException Unsupported Encoding Exception * @throws IllegalStateException Illegal Argument Exception * @throws java.security.NoSuchAlgorithmException No Such Algorithm Exception * @throws java.security.InvalidKeyException Invalid Key Exception * @throws JSONException */ private byte[] getSignatureKey(final JSONObject signatureRequestObject, final String key, final String dateStamp, final String regionName, final String serviceName) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, JSONException { final byte[] kSecret = (AmazonSQSConstants.AWS4 + key).getBytes(AmazonSQSConstants.UTF8); final byte[] kDate = hmacSHA256(kSecret, dateStamp); final byte[] kRegion = hmacSHA256(kDate, regionName); final byte[] kService = hmacSHA256(kRegion, serviceName); return hmacSHA256(kService, signatureRequestObject.get(AmazonSQSConstants.TERMINATION_STRING).toString()); } /** * Provides the HMAC SHA 256 encoded value(using the provided key) of the given data. * * @param key to use for encoding * @param data to be encoded * * @return HMAC SHA 256 encoded byte array * @throws java.security.NoSuchAlgorithmException No such algorithm Exception * @throws java.security.InvalidKeyException Invalid key Exception * @throws java.io.UnsupportedEncodingException Unsupported Encoding Exception * @throws IllegalStateException Illegal State Exception */ private byte[] hmacSHA256(final byte[] key, final String data) throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException, UnsupportedEncodingException { final String algorithm = AmazonSQSConstants.HAMC_SHA_256; final Mac mac = Mac.getInstance(algorithm); mac.init(new SecretKeySpec(key, algorithm)); return mac.doFinal(data.getBytes(AmazonSQSConstants.UTF8)); } }