/** This method creates a detail packed exception to pass up */ private AWSException createException(String errorResponse, String msgPrefix) throws JAXBException { String errorMsg; String requestId; List<AWSError> errors = null; ByteArrayInputStream bais = new ByteArrayInputStream(errorResponse.getBytes()); if (errorResponse.indexOf("<ErrorResponse") > -1) { try { // this comes form the SQS2 schema, and is the standard new response ErrorResponse resp = JAXBuddy.deserializeXMLStream(ErrorResponse.class, bais); List<Error> errs = resp.getErrors(); errorMsg = "(" + errs.get(0).getCode() + ") " + errs.get(0).getMessage(); requestId = resp.getRequestID(); errors = new ArrayList<AWSError>(); for (Error e : errs) { errors.add( new AWSError( AWSError.ErrorType.getTypeFromString(e.getType()), e.getCode(), e.getMessage())); } } catch (UnmarshalException ex) { // this comes form the DevpayLS schema, duplicated because of the different namespace bais = new ByteArrayInputStream(errorResponse.getBytes()); com.xerox.amazonws.typica.jaxb.ErrorResponse resp = JAXBuddy.deserializeXMLStream(com.xerox.amazonws.typica.jaxb.ErrorResponse.class, bais); List<com.xerox.amazonws.typica.jaxb.Error> errs = resp.getErrors(); errorMsg = "(" + errs.get(0).getCode() + ") " + errs.get(0).getMessage(); requestId = resp.getRequestID(); errors = new ArrayList<AWSError>(); for (com.xerox.amazonws.typica.jaxb.Error e : errs) { errors.add( new AWSError( AWSError.ErrorType.getTypeFromString(e.getType()), e.getCode(), e.getMessage())); } } } else { Response resp = JAXBuddy.deserializeXMLStream(Response.class, bais); String errorCode = resp.getErrors().getError().getCode(); errorMsg = resp.getErrors().getError().getMessage(); requestId = resp.getRequestID(); if (errorCode != null && !errorCode.trim().equals("")) { errors = new ArrayList<AWSError>(); errors.add(new AWSError(AWSError.ErrorType.SENDER, errorCode, errorMsg)); } } return new AWSException(msgPrefix + errorMsg, requestId, errors); }
/** * Make a http request and process the response. This method also performs automatic retries. * * @param method The HTTP method to use (GET, POST, DELETE, etc) * @param action the name of the action for this query request * @param params map of request params * @param respType the class that represents the desired/expected return type */ protected <T> T makeRequest( HttpMethodBase method, String action, Map<String, String> params, Class<T> respType) throws HttpException, IOException, JAXBException, AWSException { // add auth params, and protocol specific headers Map<String, String> qParams = new HashMap<String, String>(params); qParams.put("Action", action); qParams.put("AWSAccessKeyId", getAwsAccessKeyId()); qParams.put("SignatureVersion", "" + getSignatureVersion()); qParams.put("Timestamp", httpDate()); if (getSignatureVersion() == 2) { qParams.put("SignatureMethod", getAlgorithm()); } if (headers != null) { for (Iterator<String> i = headers.keySet().iterator(); i.hasNext(); ) { String key = i.next(); for (Iterator<String> j = headers.get(key).iterator(); j.hasNext(); ) { qParams.put(key, j.next()); } } } // sort params by key ArrayList<String> keys = new ArrayList<String>(qParams.keySet()); if (getSignatureVersion() == 2) { Collections.sort(keys); } else { Collator stringCollator = Collator.getInstance(); stringCollator.setStrength(Collator.PRIMARY); Collections.sort(keys, stringCollator); } // build param string StringBuilder resource = new StringBuilder(); if (getSignatureVersion() == 0) { // ensure Action, Timestamp come first! resource.append(qParams.get("Action")); resource.append(qParams.get("Timestamp")); } else if (getSignatureVersion() == 2) { resource.append(method.getName()); resource.append("\n"); resource.append(getServer().toLowerCase()); resource.append("\n/"); String reqURL = makeURL("").toString(); // see if there is something after the host:port/ in the URL if (reqURL.lastIndexOf('/') < (reqURL.length() - 1)) { // if so, put that here in the string to sign resource.append(reqURL.substring(reqURL.lastIndexOf('/') + 1)); } resource.append("\n"); boolean first = true; for (String key : keys) { if (!first) { resource.append("&"); } else { first = false; } resource.append(key); resource.append("="); resource.append(SignerEncoder.encode(qParams.get(key))); // System.err.println("encoded params "+key+" :"+(urlencode(qParams.get(key)))); } } else { for (String key : keys) { resource.append(key); resource.append(qParams.get(key)); } } // System.err.println("String to sign :"+resource.toString()); // calculate signature String encoded = encode(getSecretAccessKey(), resource.toString(), true); // build param string, encoding values and adding request signature resource = new StringBuilder(); for (String key : keys) { resource.append("&"); resource.append(key); resource.append("="); resource.append(urlencode(qParams.get(key))); } resource.setCharAt(0, '?'); // set first param delimeter resource.append("&Signature="); resource.append(encoded); // finally, build request object URL url = makeURL(resource.toString()); method.setURI(new URI(url.toString(), true)); method.setRequestHeader(new Header("User-Agent", userAgent)); if (getSignatureVersion() == 0) { method.setRequestHeader( new Header("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")); } Object response = null; boolean done = false; int retries = 0; boolean doRetry = false; AWSException error = null; do { int responseCode = 600; // default to high value, so we don't think it is valid try { responseCode = getHttpClient().executeMethod(method); } catch (SocketException ex) { // these can generally be retried. Treat it like a 500 error doRetry = true; error = new AWSException(ex.getMessage(), ex); } // 100's are these are handled by httpclient if (responseCode < 300) { // 200's : parse normal response into requested object if (respType != null) { InputStream iStr = method.getResponseBodyAsStream(); response = JAXBuddy.deserializeXMLStream(respType, iStr); } done = true; } else if (responseCode < 400) { // 300's : what to do? throw new HttpException("redirect error : " + responseCode); } else if (responseCode < 500) { // 400's : parse client error message String body = getStringFromStream(method.getResponseBodyAsStream()); throw createException(body, "Client error : "); } else if (responseCode < 600) { // 500's : retry... doRetry = true; String body = getStringFromStream(method.getResponseBodyAsStream()); error = createException(body, ""); } if (doRetry) { retries++; if (retries > maxRetries) { throw new HttpException("Number of retries exceeded : " + action, error); } doRetry = false; try { Thread.sleep((int) Math.pow(2.0, retries) * 1000); } catch (InterruptedException ex) { } } } while (!done); return (T) response; }