diff --git a/pom.xml b/pom.xml
index ae3e4e1..3b59740 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
ai.finity
aws-java-sdk-awis
jar
- 0.2
+ 0.3
aws-java-sdk-awis
Amazon Alexa Web Information Service client
diff --git a/src/main/java/net/distributary/tahseen/awis/AlexaWebInformationServiceClient.java b/src/main/java/net/distributary/tahseen/awis/AlexaWebInformationServiceClient.java
index 26c60dc..18eafe4 100644
--- a/src/main/java/net/distributary/tahseen/awis/AlexaWebInformationServiceClient.java
+++ b/src/main/java/net/distributary/tahseen/awis/AlexaWebInformationServiceClient.java
@@ -35,7 +35,7 @@
import java.util.stream.Collectors;
public class AlexaWebInformationServiceClient {
- protected final static Logger logger = LoggerFactory.getLogger(AlexaWebInformationServiceClient.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(AlexaWebInformationServiceClient.class);
private static final String SERVICE_HOST = "awis.amazonaws.com";
private static final String SERVICE_ENDPOINT = "awis.us-west-1.amazonaws.com";
@@ -49,31 +49,17 @@ public class AlexaWebInformationServiceClient {
private static final String ALGORITHM = "AWS4-HMAC-SHA256";
private static final String SIGNED_HEADERS = "host;x-amz-date";
- private AWSCredentials credentials;
+ // static init as TimeZone.getTimeZone is synchronised => lead to contention!
+ private static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
- public String amzDate;
- public String dateStamp;
+ private final AWSCredentials credentials;
- private Map queryParams;
-
- public AlexaWebInformationServiceClient(AWSCredentials credentials) {
+ public AlexaWebInformationServiceClient(final AWSCredentials credentials) {
if (credentials == null) {
throw new IllegalArgumentException("Parameter credentials can not be null.");
}
this.credentials = credentials;
-
- queryParams = new TreeMap<>();
- queryParams.put("AWSAccessKeyId", credentials.getAWSAccessKeyId());
-
- Date now = new Date();
- SimpleDateFormat formatAWS = new SimpleDateFormat(DATEFORMAT_AWS);
- formatAWS.setTimeZone(TimeZone.getTimeZone("GMT"));
- this.amzDate = formatAWS.format(now);
-
- SimpleDateFormat formatCredential = new SimpleDateFormat(DATEFORMAT_CREDENTIAL);
- formatCredential.setTimeZone(TimeZone.getTimeZone("GMT"));
- this.dateStamp = formatCredential.format(now);
}
/**
@@ -82,14 +68,25 @@ public AlexaWebInformationServiceClient(AWSCredentials credentials) {
* @param date current date
* @return timestamp
*/
- protected static String getTimestamp(Date date) {
- SimpleDateFormat format = new SimpleDateFormat(DATEFORMAT_AWS);
- format.setTimeZone(TimeZone.getTimeZone("GMT"));
+ protected static String getTimestamp(final Date date) {
+ final SimpleDateFormat format = new SimpleDateFormat(DATEFORMAT_AWS);
+ format.setTimeZone(GMT_TIMEZONE);
+ return format.format(date);
+ }
+
+ /**
+ * Genereates a timestamp for use with AWS credential scope signing
+ * @param date current date
+ * @return properly formatted timestamp
+ */
+ protected static String getCredentialScopeTimestamp(final Date date) {
+ final SimpleDateFormat format = new SimpleDateFormat(DATEFORMAT_CREDENTIAL);
+ format.setTimeZone(GMT_TIMEZONE);
return format.format(date);
}
- protected String sha256(String textToHash) throws UnsupportedEncodingException, NoSuchAlgorithmException {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ protected String sha256(final String textToHash) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] byteOfTextToHash = textToHash.getBytes("UTF-8");
byte[] hashedByteArray = digest.digest(byteOfTextToHash);
return bytesToHex(hashedByteArray);
@@ -102,7 +99,7 @@ protected byte[] HmacSHA256(String data, byte[] key) throws UnsupportedEncodingE
}
protected String bytesToHex(byte[] bytes) {
- StringBuffer result = new StringBuffer();
+ final StringBuilder result = new StringBuilder();
for (byte byt : bytes)
result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
return result.toString();
@@ -204,22 +201,25 @@ protected String buildQueryString(Request request) throws UnsupportedEnco
* Computes RFC 2104-compliant HMAC signature.
*
* @param query The data to be signed.
+ * @param credentialScope credential scope.
+ * @param amzDate Date in DATEFORMAT_AWS format.
+ * @param dateStamp Date in YYYYMMDD format
+ *
* @return The base64-encoded RFC 2104-compliant HMAC signature.
* @throws java.security.SignatureException when signature generation fails
*/
- protected String generateSignature(String query, String credentialScope) throws SignatureException {
- if (credentials == null) {
- throw new IllegalStateException("AWS credentials are not intialized.");
- }
+ protected String generateSignature(String query, String credentialScope, String amzDate, String dateStamp)
+ throws SignatureException {
+
try {
- String canonicalHeaders = "host:" + SERVICE_ENDPOINT + "\n" + "x-amz-date:" + this.amzDate + "\n";
+ final String canonicalHeaders = "host:" + SERVICE_ENDPOINT + "\n" + "x-amz-date:" + amzDate + "\n";
- String payloadHash = this.sha256("");
- String canonicalRequest =
+ final String payloadHash = this.sha256("");
+ final String canonicalRequest =
"GET" + "\n" + SERVICE_URI + "\n" + query + "\n" + canonicalHeaders + "\n" + SIGNED_HEADERS + "\n" + payloadHash;
- String stringToSign = ALGORITHM + '\n' + this.amzDate + '\n' + credentialScope + '\n' + this.sha256(canonicalRequest);
- byte[] signingKey = getSignatureKey(credentials.getAWSSecretKey(), this.dateStamp, SERVICE_REGION, SERVICE_NAME);
+ final String stringToSign = ALGORITHM + '\n' + amzDate + '\n' + credentialScope + '\n' + this.sha256(canonicalRequest);
+ byte[] signingKey = getSignatureKey(credentials.getAWSSecretKey(), dateStamp, SERVICE_REGION, SERVICE_NAME);
// Sign the string_to_sign using the signing_key
return bytesToHex(HmacSHA256(stringToSign, signingKey));
@@ -243,12 +243,10 @@ protected String generateSignature(String query, String credentialScope) throws
public UrlInfoResponse getUrlInfo(UrlInfoRequest request) throws SignatureException, IOException, JAXBException {
String xmlResponse = getResponse(request);
- JAXBContext jc = JAXBContext.newInstance(UrlInfoResponse.class);
+ final JAXBContext jc = JAXBContext.newInstance(UrlInfoResponse.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
- UrlInfoResponse response = (UrlInfoResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
-
- return response;
+ return (UrlInfoResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
}
/**
@@ -264,12 +262,10 @@ public UrlInfoResponse getUrlInfo(UrlInfoRequest request) throws SignatureExcept
public TrafficHistoryResponse getTrafficHistory(TrafficHistoryRequest request) throws JAXBException, IOException, SignatureException {
String xmlResponse = getResponse(request);
- JAXBContext jc = JAXBContext.newInstance(TrafficHistoryResponse.class);
+ final JAXBContext jc = JAXBContext.newInstance(TrafficHistoryResponse.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
- TrafficHistoryResponse response = (TrafficHistoryResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
-
- return response;
+ return (TrafficHistoryResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
}
/**
@@ -292,9 +288,7 @@ public CategoryBrowseResponse getCategoryBrowse(CategoryBrowseRequest request) t
JAXBContext jc = JAXBContext.newInstance(CategoryBrowseResponse.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
- CategoryBrowseResponse response = (CategoryBrowseResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
-
- return response;
+ return (CategoryBrowseResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
}
/***
@@ -314,9 +308,7 @@ public CategoryListingsResponse getCategoryListings(CategoryListingsRequest requ
JAXBContext jc = JAXBContext.newInstance(CategoryListingsResponse.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
- CategoryListingsResponse response = (CategoryListingsResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
-
- return response;
+ return (CategoryListingsResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
}
/**
@@ -336,29 +328,33 @@ public SitesLinkingInResponse getSitesLinkingIn(SitesLinkingInRequest request) t
JAXBContext jc = JAXBContext.newInstance(SitesLinkingInResponse.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
- SitesLinkingInResponse response = (SitesLinkingInResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
-
- return response;
+ return (SitesLinkingInResponse) unmarshaller.unmarshal(new StringReader(xmlResponse));
}
private String getResponse(Request request) throws IOException, SignatureException {
- String query = buildQueryString(request);
- String credentialScope = this.dateStamp + "/" + SERVICE_REGION + "/" + SERVICE_NAME + "/" + "aws4_request";
+ final Date now = new Date();
+ final String amzDate = getTimestamp(now);
+ final String dateStamp = getCredentialScopeTimestamp(now);
+
+ final String query = buildQueryString(request);
+ final String credentialScope = dateStamp + "/" + SERVICE_REGION + "/" + SERVICE_NAME + "/" + "aws4_request";
- String signature = generateSignature(query, credentialScope);
+ final String signature = generateSignature(query, credentialScope, amzDate, dateStamp);
- String uri = AWS_BASE_URL + "?" + query;
+ final String uri = AWS_BASE_URL + "?" + query;
- logger.info("Request Url: {}", uri);
+ LOGGER.debug("Request Url: {}", uri);
- String authorization =
+ final String authorization =
ALGORITHM + " " + "Credential=" + credentials.getAWSAccessKeyId() + "/" + credentialScope + ", " + "SignedHeaders=" + SIGNED_HEADERS
+ ", " + "Signature=" + signature;
- String xmlResponse = makeRequest(uri, authorization, this.amzDate);
+ String xmlResponse = makeRequest(uri, authorization, amzDate);
xmlResponse = xmlResponse.replace("xmlns:aws=\"http://awis.amazonaws.com/doc/2005-07-11\"", "");
xmlResponse = xmlResponse.replace("xmlns:aws=\"http://alexa.amazonaws.com/doc/2005-10-05/\"", "");
- logger.info(xmlResponse);
+
+ LOGGER.debug(xmlResponse);
+
return xmlResponse;
}
@@ -380,7 +376,7 @@ public String makeRequest(String requestUrl, String authorization, String amzDat
try {
in = conn.getInputStream();
} catch (Exception e) {
- logger.error("Http request failed.", e);
+ LOGGER.error("Http request failed.", e);
in = conn.getErrorStream();
}