Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<groupId>ai.finity</groupId>
<artifactId>aws-java-sdk-awis</artifactId>
<packaging>jar</packaging>
<version>0.2</version>
<version>0.3</version>

<name>aws-java-sdk-awis</name>
<description>Amazon Alexa Web Information Service client</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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<String, String> 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);
}

/**
Expand All @@ -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);
Expand All @@ -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();
Expand Down Expand Up @@ -204,22 +201,25 @@ protected <T> String buildQueryString(Request<T> 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));
Expand All @@ -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));
}

/**
Expand All @@ -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));
}

/**
Expand All @@ -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));
}

/***
Expand All @@ -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));
}

/**
Expand All @@ -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 <T> String getResponse(Request<T> 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;
}

Expand All @@ -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();
}

Expand Down