import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.Collator; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * This class provides business delegate to access SDB web service using REST approach. * */ public class SdbDelegate { private static final String DEFAULT_ENCODING = "UTF-8"; private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; private static final String MORETOKEN_STRING = "MoreToken"; private final String accessId; private final String secretKey; private final String uri; private final String version; public static class Response { public final Document rootDocument; public final Element rootElement; public final String requestId; public final String boxUsage; public final String moreToken; public final Object result; public Response(Document rootDocument, Element rootElement, String requestId, String boxUsage, String moreToken, Object result) { this.rootDocument = rootDocument; this.rootElement = rootElement; this.requestId = requestId; this.boxUsage = boxUsage; this.moreToken = moreToken; this.result = result; } } public SdbDelegate() { this("key", "secretkey", "http://sdb.amazonaws.com", "2007-11-07"); } public SdbDelegate(String accessId, String secretKey, String uri, String version) { this.accessId = accessId; this.secretKey = secretKey; this.uri = uri; this.version = version; } public void delete(String bucket, String name) { deleteAttributes(bucket, name); } public void createDomain(String domainName) { Map parameters = getBaseParameters("CreateDomain"); parameters.put("DomainName",encode(domainName)); invoke(parameters); } public void deleteDomain(String domainName) { Map parameters = getBaseParameters("DeleteDomain"); parameters.put("DomainName",encode(domainName)); invoke(parameters); } public List listDomains() { return listDomains(null); } public List listDomains(StringBuilder moreToken) { return (List) listDomains(moreToken, null); } public List listDomains(StringBuilder moreToken, Integer maxResults) { Map parameters = getBaseParameters("ListDomains"); if (moreToken != null && moreToken.length() > 0) { parameters.put(MORETOKEN_STRING, moreToken.toString()); } if (maxResults != null) { parameters.put("MaxResults",maxResults.toString()); } Response response = invoke(parameters); if (moreToken != null) { moreToken.setLength(0); moreToken.append(response.moreToken); } Element rootElement = (Element) response.rootElement; NodeList domainNodes = rootElement.getElementsByTagName("DomainName"); List domains = new ArrayList(); for (int i = 0; i < domainNodes.getLength(); ++i) { Node domainNode = domainNodes.item(i); Node domainNameNode = domainNode.getFirstChild(); String domainName = domainNameNode.getNodeValue(); domains.add(domainName); } // NodeList moreTokenNodes = rootElement.getElementsByTagName(MORETOKEN_STRING); if (moreTokenNodes.getLength() > 0) { Element moreTokenElement = (Element) moreTokenNodes.item(0); moreTokenElement.normalize(); Node tokenNode = moreTokenElement.getFirstChild(); String newMoreToken = tokenNode.getNodeValue(); if (moreToken != null && (newMoreToken.compareToIgnoreCase("null") == 0 || newMoreToken.length() == 0)) { moreToken.setLength(0); } } return domains; } public void deleteAttributes(String domain, String identifier) { deleteAttributes(domain, identifier, new HashMap()); } /** * Delete specified atrributes and/or values from item * * @param attributes set of attributes * @return result of the operation */ public void deleteAttributes(String domain, String identifier, Map attributes) { Map parameters = getBaseParameters("DeleteAttributes"); parameters.put("ItemName",identifier); parameters.put("DomainName",domain); if (attributes != null) { int i = 0; for (Map.Entry attr : attributes.entrySet()) { String name = "Attribute."+ i + ".Name"; parameters.put(name,attr.getKey()); if (attr.getValue() != null) { String value = "Attribute."+ i + ".Value"; parameters.put(value, attr.getValue()); } ++i; } } invoke(parameters); } public void putAttributes(String domain, String identifier, Map attributes) { putAttributes(domain, identifier, attributes, true); } public void putAttributes(String domain, String identifier, Map attributes, boolean replace) { Map parameters = getBaseParameters("PutAttributes"); parameters.put("ItemName", identifier); parameters.put("DomainName", domain); int i = 0; if (attributes.size() > 255) throw new IllegalArgumentException("more than 255 attributes not supported"); System.out.println("Writing " + attributes.size() + " attributes for " + identifier); for (Map.Entry attr : attributes.entrySet()) { if (attr.getKey().length() > 700) throw new IllegalArgumentException("name '" + attr.getKey() + "' more than 700 bytes"); if (attr.getValue().length() > 700) throw new IllegalArgumentException("value '" + attr.getValue() + "' of name '" + attr.getKey() + "' more than 700 bytes"); String name = "Attribute."+ i + ".Name"; parameters.put(name,attr.getKey()); String value = "Attribute."+ i + ".Value"; parameters.put(value, attr.getValue()); i++; } // if (replace) { parameters.put("Replace","true"); } invoke(parameters); } public void replaceAttributes(String domain, String identifier, Map attributes) { putAttributes(domain, identifier, attributes,true); } public Map getAttributes(String domain, String identifier){ return getAttributes(domain, identifier, new HashMap()); } public Map getAttributes(String domain, String identifier, Map attributes) { String NAME_STRING = "Name"; String VALUE_STRING = "Value"; String ATTRIBUTE_STRING = "Attribute"; Map parameters = getBaseParameters("GetAttributes"); parameters.put("ItemName", identifier); parameters.put("DomainName",domain); int k = 0; for (Map.Entry attr : attributes.entrySet()) { String name = "AttributeName."+k; parameters.put(name,attr.getKey()); ++k; } // Response response = invoke(parameters); Element rootElement = (Element) response.rootElement; NodeList attributeNodes = rootElement.getElementsByTagName(ATTRIBUTE_STRING); Map returnAttributes = new TreeMap(String.CASE_INSENSITIVE_ORDER); // for (int i = 0; i < attributeNodes.getLength(); ++i) { Node attributeNode = attributeNodes.item(i); Element attributeElement = (Element)attributeNode; NodeList nameNodes = attributeElement.getElementsByTagName(NAME_STRING); Node nameNode = nameNodes.item(0); nameNode.normalize(); Node attributeNameNode = nameNode.getFirstChild(); String attributeName = attributeNameNode.getNodeValue(); NodeList valueNodes = attributeElement.getElementsByTagName(VALUE_STRING); Node valueNode = valueNodes.item(0); valueNode.normalize(); Node attributeValueNode = valueNode.getFirstChild(); String attributeValue = attributeValueNode.getNodeValue(); returnAttributes.put(attributeName,attributeValue); } return returnAttributes; } protected Response invoke(Map parameters) { try { Document document = getResponse(parameters); Element rootElement = document.getDocumentElement(); Element responseStatus = (Element) rootElement.getElementsByTagName("ResponseStatus").item(0); Node requestIdNode = responseStatus.getElementsByTagName("RequestId").item(0); String requestId = null; if (requestIdNode != null && requestIdNode.getFirstChild() != null) { requestId = requestIdNode.getFirstChild().getNodeValue(); } String boxUsage = null; Node boxUsagesNode = responseStatus.getElementsByTagName("BoxUsage").item(0); if (boxUsagesNode != null && boxUsagesNode.getFirstChild() != null) { boxUsage = boxUsagesNode.getFirstChild().getNodeValue(); } return new Response(document, rootElement, requestId, boxUsage, null, null); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } private static String getSignature(String data, String key) throws NoSuchAlgorithmException, InvalidKeyException { SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(),HMAC_SHA1_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); byte[] rawHmac = mac.doFinal(data.getBytes()); return new sun.misc.BASE64Encoder().encode(rawHmac); } private Map getBaseParameters(String action) { Collator myCollator = Collator.getInstance(); // Note that these parameters must be sent in order Map baseParameters = new TreeMap(myCollator); baseParameters.put("Action", action); baseParameters.put("Version", version); baseParameters.put("AWSAccessKeyId", accessId); baseParameters.put("SignatureVersion", "1"); Date curTime = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); StringBuffer timeBuffer = new StringBuffer(); FieldPosition fp = new FieldPosition(NumberFormat.INTEGER_FIELD); dateFormat.format(curTime, timeBuffer, fp); timeBuffer.insert(timeBuffer.length()-2, ":"); baseParameters.put("Timestamp",timeBuffer.toString()); return baseParameters; } private Document getResponse(Map parameters) throws UnsupportedEncodingException, MalformedURLException, IOException, NoSuchAlgorithmException, SAXException, ParserConfigurationException, InvalidKeyException { String path = uri + getUrlPath(parameters); URL url = new URL(path); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestMethod("GET"); connection.connect(); System.out.println("Connecting to " + url); int respCode = connection.getResponseCode(); if (respCode == HttpURLConnection.HTTP_OK) { InputSource inputSource = new InputSource(new BufferedReader(new InputStreamReader(connection.getInputStream()))); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); return documentBuilder.parse(inputSource); } else { InputSource inputSource = new InputSource(new BufferedReader(new InputStreamReader(connection.getErrorStream()))); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(inputSource); Element rootElement = document.getDocumentElement(); Node requestIdNode = rootElement.getElementsByTagName("RequestID").item(0); StringBuilder errorMessage = new StringBuilder("Failed to invoke input request '" + path + "'"); // if (requestIdNode != null && requestIdNode.getFirstChild() != null) { errorMessage.append(" with requestId " + requestIdNode.getFirstChild().getNodeValue()); Element errors = (Element) rootElement.getElementsByTagName("Errors").item(0); if (errors != null) { Element error = (Element) errors.getElementsByTagName("Error").item(0); if (error != null) { Node messageNode = error.getElementsByTagName("Message").item(0); if (messageNode != null) errorMessage.append(" due to " + messageNode.getFirstChild().getNodeValue()); } } } throw new RuntimeException(errorMessage.toString()); } } private String encode(String name){ try { return URLEncoder.encode(name, DEFAULT_ENCODING); } catch (UnsupportedEncodingException ex) { throw new RuntimeException("Failed to encode '" + name + "'"); } } private String getUrlPath(Map parameters) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException { StringBuilder queryString = new StringBuilder("?"); StringBuilder signatureData = new StringBuilder(); boolean first = true; for (Map.Entry param : parameters.entrySet()) { if (first) { first = false; } else { queryString.append("&"); } signatureData.append(param.getKey() + param.getValue()); queryString.append(param.getKey() + "=" + URLEncoder.encode((String)param.getValue(), DEFAULT_ENCODING)); } queryString.append("&Signature=" + URLEncoder.encode(getSignature(signatureData.toString(),secretKey), DEFAULT_ENCODING)); return queryString.toString(); } }