All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser Maven / Gradle / Ivy

Go to download

The AWS Java SDK for Amazon S3 module holds the client classes that are used for communicating with Amazon Simple Storage Service

There is a newer version: 1.12.778
Show newest version
/*
 * Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Portions copyright 2006-2009 James Murty. Please see LICENSE.txt
 * for applicable license terms and NOTICE.txt for applicable notices.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.s3.model.transform;

import com.amazonaws.services.s3.TargetObjectKeyFormat;
import com.amazonaws.services.s3.model.*;

import static com.amazonaws.util.StringUtils.UTF8;

import com.amazonaws.services.s3.model.BucketLifecycleConfiguration.NoncurrentVersionExpiration;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringAccessTier;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringAndOperator;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringConfiguration;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringFilter;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringFilterPredicate;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringPrefixPredicate;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringStatus;
import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringTagPredicate;
import com.amazonaws.services.s3.model.intelligenttiering.Tiering;
import com.amazonaws.services.s3.model.inventory.ServerSideEncryptionKMS;
import com.amazonaws.services.s3.model.inventory.ServerSideEncryptionS3;
import com.amazonaws.services.s3.model.ownership.OwnershipControls;
import com.amazonaws.services.s3.model.ownership.OwnershipControlsRule;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.amazonaws.services.s3.model.lifecycle.LifecycleAndOperator;
import com.amazonaws.services.s3.model.lifecycle.LifecycleFilter;
import com.amazonaws.services.s3.model.lifecycle.LifecycleFilterPredicate;
import com.amazonaws.services.s3.model.lifecycle.LifecycleObjectSizeGreaterThanPredicate;
import com.amazonaws.services.s3.model.lifecycle.LifecycleObjectSizeLessThanPredicate;
import com.amazonaws.services.s3.model.lifecycle.LifecyclePrefixPredicate;
import com.amazonaws.services.s3.model.lifecycle.LifecycleTagPredicate;
import com.amazonaws.services.s3.model.metrics.MetricsAccessPointArnPredicate;
import com.amazonaws.services.s3.model.metrics.MetricsAndOperator;
import com.amazonaws.services.s3.model.metrics.MetricsConfiguration;
import com.amazonaws.services.s3.model.metrics.MetricsFilter;
import com.amazonaws.services.s3.model.metrics.MetricsFilterPredicate;
import com.amazonaws.services.s3.model.metrics.MetricsPrefixPredicate;
import com.amazonaws.services.s3.model.metrics.MetricsTagPredicate;
import com.amazonaws.services.s3.model.analytics.AnalyticsAndOperator;
import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration;
import com.amazonaws.services.s3.model.analytics.AnalyticsExportDestination;
import com.amazonaws.services.s3.model.analytics.AnalyticsFilter;
import com.amazonaws.services.s3.model.analytics.AnalyticsFilterPredicate;
import com.amazonaws.services.s3.model.analytics.AnalyticsPrefixPredicate;
import com.amazonaws.services.s3.model.analytics.AnalyticsS3BucketDestination;
import com.amazonaws.services.s3.model.analytics.AnalyticsTagPredicate;
import com.amazonaws.services.s3.model.analytics.StorageClassAnalysis;
import com.amazonaws.services.s3.model.analytics.StorageClassAnalysisDataExport;
import com.amazonaws.services.s3.model.inventory.InventoryConfiguration;
import com.amazonaws.services.s3.model.inventory.InventoryDestination;
import com.amazonaws.services.s3.model.inventory.InventoryFilter;
import com.amazonaws.services.s3.model.inventory.InventoryPrefixPredicate;
import com.amazonaws.services.s3.model.inventory.InventoryS3BucketDestination;
import com.amazonaws.services.s3.model.inventory.InventorySchedule;

import com.amazonaws.services.s3.model.replication.ReplicationAndOperator;
import com.amazonaws.services.s3.model.replication.ReplicationFilter;
import com.amazonaws.services.s3.model.replication.ReplicationFilterPredicate;
import com.amazonaws.services.s3.model.replication.ReplicationPrefixPredicate;
import com.amazonaws.services.s3.model.replication.ReplicationTagPredicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.internal.Constants;
import com.amazonaws.services.s3.internal.DeleteObjectsResponse;
import com.amazonaws.services.s3.internal.ObjectExpirationResult;
import com.amazonaws.services.s3.internal.S3RequesterChargedResult;
import com.amazonaws.services.s3.internal.S3VersionResult;
import com.amazonaws.services.s3.internal.ServerSideEncryptionResult;
import com.amazonaws.services.s3.internal.ServiceUtils;
import com.amazonaws.services.s3.model.BucketLifecycleConfiguration.NoncurrentVersionTransition;
import com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Rule;
import com.amazonaws.services.s3.model.BucketLifecycleConfiguration.Transition;
import com.amazonaws.services.s3.model.CORSRule.AllowedMethods;
import com.amazonaws.services.s3.model.DeleteObjectsResult.DeletedObject;
import com.amazonaws.services.s3.model.MultiObjectDeleteException.DeleteError;
import com.amazonaws.services.s3.model.RequestPaymentConfiguration.Payer;
import com.amazonaws.util.DateUtils;
import com.amazonaws.util.SdkHttpUtils;
import com.amazonaws.util.StringUtils;

/**
 * XML Sax parser to read XML documents returned by S3 via the REST interface,
 * converting these documents into objects.
 */
public class XmlResponsesSaxParser {
    private static final Log log = LogFactory.getLog(XmlResponsesSaxParser.class);

    private XMLReader xr = null;

    private boolean sanitizeXmlDocument = true;

    /**
     * Constructs the XML SAX parser.
     *
     * @throws SdkClientException
     */
    public XmlResponsesSaxParser() throws SdkClientException {
        // Ensure we can load the XML Reader.
        try {
            xr = XMLReaderFactory.createXMLReader();
            disableExternalResourceFetching(xr);
        } catch (SAXException e) {
            throw new SdkClientException("Couldn't initialize a SAX driver to create an XMLReader", e);
        }
    }

    /**
     * Parses an XML document from an input stream using a document handler.
     *
     * @param handler
     *            the handler for the XML document
     * @param inputStream
     *            an input stream containing the XML document to parse
     *
     * @throws IOException
     *             on error reading from the input stream (ie connection reset)
     * @throws SdkClientException
     *             on error with malformed XML, etc
     */
    protected void parseXmlInputStream(DefaultHandler handler, InputStream inputStream)
            throws IOException {
        try {

            if (log.isDebugEnabled()) {
                log.debug("Parsing XML response document with handler: " + handler.getClass());
            }

            BufferedReader breader = new BufferedReader(new InputStreamReader(inputStream,
                Constants.DEFAULT_ENCODING));
            xr.setContentHandler(handler);
            xr.setErrorHandler(handler);
            xr.parse(new InputSource(breader));

        } catch (IOException e) {
            throw e;

        } catch (Throwable t) {
            try {
                inputStream.close();
            } catch (IOException e) {
                if (log.isErrorEnabled()) {
                    log.error("Unable to close response InputStream up after XML parse failure", e);
                }
            }
            throw new SdkClientException("Failed to parse XML document with handler "
                + handler.getClass(), t);
        }
    }

    protected InputStream sanitizeXmlDocument(DefaultHandler handler, InputStream inputStream)
            throws IOException {

        if (!sanitizeXmlDocument) {
            // No sanitizing will be performed, return the original input stream unchanged.
            return inputStream;
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Sanitizing XML document destined for handler " + handler.getClass());
            }

            InputStream sanitizedInputStream = null;

            try {

                /*
                 * Read object listing XML document from input stream provided into a
                 * string buffer, so we can replace troublesome characters before
                 * sending the document to the XML parser.
                 */
                StringBuilder listingDocBuffer = new StringBuilder();
                BufferedReader br = new BufferedReader(
                    new InputStreamReader(inputStream, Constants.DEFAULT_ENCODING));

                char[] buf = new char[8192];
                int read = -1;
                while ((read = br.read(buf)) != -1) {
                    listingDocBuffer.append(buf, 0, read);
                }
                br.close();

                /*
                 * Replace any carriage return (\r) characters with explicit XML
                 * character entities, to prevent the SAX parser from
                 * misinterpreting 0x0D characters as 0x0A and being unable to
                 * parse the XML.
                 */
                String listingDoc = listingDocBuffer.toString().replaceAll("\r", "
");

                sanitizedInputStream = new ByteArrayInputStream(
                    listingDoc.getBytes(UTF8));

            } catch (IOException e) {
                throw e;

            } catch (Throwable t) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    if (log.isErrorEnabled()) {
                        log.error("Unable to close response InputStream after failure sanitizing XML document", e);
                    }
                }
                throw new SdkClientException("Failed to sanitize XML document destined for handler "
                    + handler.getClass(), t);
            }
            return sanitizedInputStream;
        }
    }

    /**
     * Disables certain dangerous features that attempt to automatically fetch DTDs
     *
     * See OWASP XXE Cheat Sheet
     * @param reader the reader to disable the features on
     * @throws SAXNotRecognizedException
     * @throws SAXNotSupportedException
     */
    private void disableExternalResourceFetching(XMLReader reader) throws SAXNotRecognizedException, SAXNotSupportedException {
        reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
        reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
    }

    /**
     * Checks if the specified string is empty or null and if so, returns null.
     * Otherwise simply returns the string.
     *
     * @param s
     *            The string to check.
     * @return Null if the specified string was null, or empty, otherwise
     *         returns the string the caller passed in.
     */
    private static String checkForEmptyString(String s) {
        if (s == null) return null;
        if (s.length() == 0) return null;

        return s;
    }

    /**
     * Safely parses the specified string as an integer and returns the value.
     * If a NumberFormatException occurs while parsing the integer, an error is
     * logged and -1 is returned.
     *
     * @param s
     *            The string to parse and return as an integer.
     *
     * @return The integer value of the specified string, otherwise -1 if there
     *         were any problems parsing the string as an integer.
     */
    private static int parseInt(String s) {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException nfe) {
            log.error("Unable to parse integer value '" + s + "'", nfe);
        }

        return -1;
    }

    /**
     * Safely parses the specified string as a long and returns the value. If a
     * NumberFormatException occurs while parsing the long, an error is logged
     * and -1 is returned.
     *
     * @param s
     *            The string to parse and return as a long.
     *
     * @return The long value of the specified string, otherwise -1 if there
     *         were any problems parsing the string as a long.
     */
    private static long parseLong(String s) {
        try {
            return Long.parseLong(s);
        } catch (NumberFormatException nfe) {
            log.error("Unable to parse long value '" + s + "'", nfe);
        }

        return -1;
    }

    /**
     * Perform a url decode on the given value if specified.
     * Return value by default;
     */
    private static String decodeIfSpecified(String value, boolean decode) {
        return decode ? SdkHttpUtils.urlDecode(value) : value;
    }

    /**
     * Parses a ListBucket response XML document from an input stream.
     *
     * @param inputStream
     *            XML data input stream.
     * @return the XML handler object populated with data parsed from the XML
     *         stream.
     * @throws SdkClientException
     */
    public ListBucketHandler parseListBucketObjectsResponse(InputStream inputStream, final boolean shouldSDKDecodeResponse)
            throws IOException {
        ListBucketHandler handler = new ListBucketHandler(shouldSDKDecodeResponse);
        parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream));

        return handler;
    }

    /**
     * Parses a ListBucketV2 response XML document from an input stream.
     *
     * @param inputStream
     *            XML data input stream.
     * @return the XML handler object populated with data parsed from the XML
     *         stream.
     * @throws SdkClientException
     */
    public ListObjectsV2Handler parseListObjectsV2Response(InputStream inputStream, final boolean shouldSDKDecodeResponse)
            throws IOException {
        ListObjectsV2Handler handler = new ListObjectsV2Handler(shouldSDKDecodeResponse);
        parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream));

        return handler;
    }

    /**
     * Parses a ListVersions response XML document from an input stream.
     *
     * @param inputStream
     *            XML data input stream.
     * @return the XML handler object populated with data parsed from the XML
     *         stream.
     * @throws SdkClientException
     */
    public ListVersionsHandler parseListVersionsResponse(InputStream inputStream, final boolean shouldSDKDecodeResponse)
            throws IOException {
        ListVersionsHandler handler = new ListVersionsHandler(shouldSDKDecodeResponse);
        parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream));
        return handler;
    }

    /**
     * Parses a ListAllMyBuckets response XML document from an input stream.
     *
     * @param inputStream
     *            XML data input stream.
     * @return the XML handler object populated with data parsed from the XML
     *         stream.
     * @throws SdkClientException
     */
    public ListAllMyBucketsHandler parseListMyBucketsResponse(InputStream inputStream)
            throws IOException {
        ListAllMyBucketsHandler handler = new ListAllMyBucketsHandler();
        parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream));
        return handler;
    }

    /**
     * Parses an AccessControlListHandler response XML document from an input
     * stream.
     *
     * @param inputStream
     *            XML data input stream.
     * @return the XML handler object populated with data parsed from the XML
     *         stream.
     *
     * @throws SdkClientException
     */
    public AccessControlListHandler parseAccessControlListResponse(InputStream inputStream)
            throws IOException {
        AccessControlListHandler handler = new AccessControlListHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    /**
     * Parses a LoggingStatus response XML document for a bucket from an input
     * stream.
     *
     * @param inputStream
     *            XML data input stream.
     * @return the XML handler object populated with data parsed from the XML
     *         stream.
     *
     * @throws SdkClientException
     */
    public BucketLoggingConfigurationHandler parseLoggingStatusResponse(InputStream inputStream)
            throws IOException {
        BucketLoggingConfigurationHandler handler = new BucketLoggingConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public BucketLifecycleConfigurationHandler parseBucketLifecycleConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketLifecycleConfigurationHandler handler = new BucketLifecycleConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public BucketCrossOriginConfigurationHandler parseBucketCrossOriginConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketCrossOriginConfigurationHandler handler = new BucketCrossOriginConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public String parseBucketLocationResponse(InputStream inputStream)
            throws IOException {
        BucketLocationHandler handler = new BucketLocationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler.getLocation();
    }

    public BucketVersioningConfigurationHandler parseVersioningConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketVersioningConfigurationHandler handler = new BucketVersioningConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public BucketWebsiteConfigurationHandler parseWebsiteConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketWebsiteConfigurationHandler handler = new BucketWebsiteConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }


    public BucketReplicationConfigurationHandler parseReplicationConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketReplicationConfigurationHandler handler = new BucketReplicationConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public BucketTaggingConfigurationHandler parseTaggingConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketTaggingConfigurationHandler handler = new BucketTaggingConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public BucketAccelerateConfigurationHandler parseAccelerateConfigurationResponse(InputStream inputStream)
            throws IOException {
        BucketAccelerateConfigurationHandler handler = new BucketAccelerateConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public DeleteObjectsHandler parseDeletedObjectsResult(InputStream inputStream)
            throws IOException {
        DeleteObjectsHandler handler = new DeleteObjectsHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public CopyObjectResultHandler parseCopyObjectResponse(InputStream inputStream)
            throws IOException {
        CopyObjectResultHandler handler = new CopyObjectResultHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public CompleteMultipartUploadHandler parseCompleteMultipartUploadResponse(InputStream inputStream)
            throws IOException {
        CompleteMultipartUploadHandler handler = new CompleteMultipartUploadHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public InitiateMultipartUploadHandler parseInitiateMultipartUploadResponse(InputStream inputStream)
            throws IOException {
        InitiateMultipartUploadHandler handler = new InitiateMultipartUploadHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public ListMultipartUploadsHandler parseListMultipartUploadsResponse(InputStream inputStream)
            throws IOException {
        ListMultipartUploadsHandler handler = new ListMultipartUploadsHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public ListPartsHandler parseListPartsResponse(InputStream inputStream)
            throws IOException {
        ListPartsHandler handler = new ListPartsHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetObjectTaggingHandler parseObjectTaggingResponse(InputStream inputStream) throws IOException {
        GetObjectTaggingHandler handler = new GetObjectTaggingHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetBucketMetricsConfigurationHandler parseGetBucketMetricsConfigurationResponse(InputStream inputStream)
            throws IOException {
        GetBucketMetricsConfigurationHandler handler = new GetBucketMetricsConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public ListBucketMetricsConfigurationsHandler parseListBucketMetricsConfigurationsResponse(InputStream inputStream)
            throws IOException {
        ListBucketMetricsConfigurationsHandler handler = new ListBucketMetricsConfigurationsHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetBucketOwnershipControlsHandler parseGetBucketOwnershipControlsResponse(InputStream inputStream)
        throws IOException {
        GetBucketOwnershipControlsHandler handler = new GetBucketOwnershipControlsHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetBucketAnalyticsConfigurationHandler parseGetBucketAnalyticsConfigurationResponse(InputStream inputStream)
            throws IOException {
        GetBucketAnalyticsConfigurationHandler handler = new GetBucketAnalyticsConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public ListBucketAnalyticsConfigurationHandler parseListBucketAnalyticsConfigurationResponse(InputStream inputStream)
            throws IOException {
        ListBucketAnalyticsConfigurationHandler handler = new ListBucketAnalyticsConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }


    public GetBucketIntelligentTieringConfigurationHandler parseGetBucketIntelligentTieringConfigurationResponse(InputStream inputStream)
            throws IOException {
        GetBucketIntelligentTieringConfigurationHandler handler = new GetBucketIntelligentTieringConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public ListBucketIntelligentTieringConfigurationHandler parseListBucketIntelligentTieringConfigurationResponse(InputStream inputStream)
            throws IOException {
        ListBucketIntelligentTieringConfigurationHandler handler = new ListBucketIntelligentTieringConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetBucketInventoryConfigurationHandler parseGetBucketInventoryConfigurationResponse(InputStream inputStream)
            throws IOException {
        GetBucketInventoryConfigurationHandler handler = new GetBucketInventoryConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public ListBucketInventoryConfigurationsHandler parseBucketListInventoryConfigurationsResponse(InputStream inputStream)
            throws IOException {
        ListBucketInventoryConfigurationsHandler handler = new ListBucketInventoryConfigurationsHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    /**
     * @param inputStream
     *
     * @return true if the bucket's is configured as Requester Pays, false if it
     *         is configured as Owner pays.
     *
     * @throws SdkClientException
     */
    public RequestPaymentConfigurationHandler parseRequestPaymentConfigurationResponse(InputStream inputStream)
            throws IOException {
        RequestPaymentConfigurationHandler handler = new RequestPaymentConfigurationHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetObjectLegalHoldResponseHandler parseGetObjectLegalHoldResponse(InputStream inputStream) throws IOException {
        GetObjectLegalHoldResponseHandler handler = new GetObjectLegalHoldResponseHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetObjectLockConfigurationResponseHandler parseGetObjectLockConfigurationResponse(InputStream inputStream) throws IOException {
        GetObjectLockConfigurationResponseHandler handler = new GetObjectLockConfigurationResponseHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    public GetObjectRetentionResponseHandler parseGetObjectRetentionResponse(InputStream inputStream) throws IOException {
        GetObjectRetentionResponseHandler handler = new GetObjectRetentionResponseHandler();
        parseXmlInputStream(handler, inputStream);
        return handler;
    }

    // ////////////
    // Handlers //
    // ////////////

    /**
     * Handler for ListBucket response XML documents.
     */
    public static class ListBucketHandler extends AbstractHandler {
        private final ObjectListing objectListing = new ObjectListing();
        private final boolean shouldSDKDecodeResponse;

        private S3ObjectSummary currentObject = null;
        private Owner currentOwner = null;
        private RestoreStatus currentRestoreStatus = null;
        private String lastKey = null;

        public ListBucketHandler(final boolean shouldSDKDecodeResponse) {
            this.shouldSDKDecodeResponse = shouldSDKDecodeResponse;
        }

        public ObjectListing getObjectListing() {
            return objectListing;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("ListBucketResult")) {
                if (name.equals("Contents")) {
                    currentObject = new S3ObjectSummary();
                    currentObject.setBucketName(objectListing.getBucketName());
                }
            }

            else if (in("ListBucketResult", "Contents")) {
                if (name.equals("Owner")) {
                    currentOwner = new Owner();

                } else if (name.equals("RestoreStatus")) {
                    currentRestoreStatus = new RestoreStatus();

                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (atTopLevel()) {
                if (name.equals("ListBucketResult")) {
                    /*
                     * S3 only includes the NextMarker XML element if the
                     * request specified a delimiter, but for consistency we'd
                     * like to always give easy access to the next marker if
                     * we're returning a list of results that's truncated.
                     */
                    if (objectListing.isTruncated()
                        && objectListing.getNextMarker() == null) {

                        String nextMarker = null;
                        if (!objectListing.getObjectSummaries().isEmpty()) {
                            nextMarker = objectListing.getObjectSummaries()
                                .get(objectListing.getObjectSummaries().size() - 1)
                                .getKey();

                        } else if (!objectListing.getCommonPrefixes().isEmpty()) {
                            nextMarker = objectListing.getCommonPrefixes()
                                .get(objectListing.getCommonPrefixes().size() - 1);
                        } else {
                            log.error("S3 response indicates truncated results, "
                                    + "but contains no object summaries or "
                                    + "common prefixes.");
                        }

                        objectListing.setNextMarker(nextMarker);
                    }
                }
            }

            else if (in("ListBucketResult")) {
                if (name.equals("Name")) {
                    objectListing.setBucketName(getText());
                    if (log.isDebugEnabled()) {
                        log.debug("Examining listing for bucket: "
                                + objectListing.getBucketName());
                    }

                } else if (name.equals("Prefix")) {
                    objectListing.setPrefix(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("Marker")) {
                    objectListing.setMarker(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("NextMarker")) {
                    objectListing.setNextMarker(decodeIfSpecified
                            (getText(), shouldSDKDecodeResponse));

                } else if (name.equals("MaxKeys")) {
                    objectListing.setMaxKeys(parseInt(getText()));

                } else if (name.equals("Delimiter")) {
                    objectListing.setDelimiter(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("EncodingType")) {
                    objectListing.setEncodingType(shouldSDKDecodeResponse ?
                            null : checkForEmptyString(getText()));
                } else if (name.equals("IsTruncated")) {
                    String isTruncatedStr =
                        StringUtils.lowerCase(getText());

                    if (isTruncatedStr.startsWith("false")) {
                        objectListing.setTruncated(false);
                    } else if (isTruncatedStr.startsWith("true")) {
                        objectListing.setTruncated(true);
                    } else {
                        throw new IllegalStateException(
                                "Invalid value for IsTruncated field: "
                                + isTruncatedStr);
                    }

                } else if (name.equals("Contents")) {
                    objectListing.getObjectSummaries().add(currentObject);
                    currentObject = null;
                }
            }

            else if (in("ListBucketResult", "Contents")) {
                if (name.equals("Key")) {
                    lastKey = getText();
                    currentObject.setKey(decodeIfSpecified
                                    (lastKey, shouldSDKDecodeResponse));
                } else if (name.equals("LastModified")) {
                    currentObject.setLastModified(
                            ServiceUtils.parseIso8601Date(getText()));

                } else if (name.equals("ETag")) {
                    currentObject.setETag(
                            ServiceUtils.removeQuotes(getText()));

                } else if (name.equals("Size")) {
                    currentObject.setSize(parseLong(getText()));

                } else if (name.equals("StorageClass")) {
                    currentObject.setStorageClass(getText());

                } else if (name.equals("Owner")) {
                    currentObject.setOwner(currentOwner);
                    currentOwner = null;

                } else if (name.equals("RestoreStatus")) {
                    currentObject.setRestoreStatus(currentRestoreStatus);
                    currentRestoreStatus = null;
                }
            }

            else if (in("ListBucketResult", "Contents", "RestoreStatus")) {
                if (name.equals("IsRestoreInProgress")) {
                    currentRestoreStatus.setIsRestoreInProgress(Boolean.parseBoolean(getText()));

                } else if (name.equals("RestoreExpiryDate")) {
                    currentRestoreStatus.setRestoreExpiryDate(ServiceUtils.parseIso8601Date(getText()));
                }
            }

            else if (in("ListBucketResult", "Contents", "Owner")) {
                if (name.equals("ID")) {
                    currentOwner.setId(getText());

                } else if (name.equals("DisplayName")) {
                    currentOwner.setDisplayName(getText());
                }
            }

            else if (in("ListBucketResult", "CommonPrefixes")) {
                if (name.equals("Prefix")) {
                    objectListing.getCommonPrefixes().add
                            (decodeIfSpecified(getText(), shouldSDKDecodeResponse));
                }
            }
        }
    }

    /**
     * Handler for ListObjectsV2 response XML documents.
     */
    public static class ListObjectsV2Handler extends AbstractHandler {
        private final ListObjectsV2Result result = new ListObjectsV2Result();
        private final boolean shouldSDKDecodeResponse;

        private S3ObjectSummary currentObject = null;
        private Owner currentOwner = null;
        private RestoreStatus currentRestoreStatus = null;
        private String lastKey = null;

        public ListObjectsV2Handler(final boolean shouldSDKDecodeResponse) {
            this.shouldSDKDecodeResponse = shouldSDKDecodeResponse;
        }

        public ListObjectsV2Result getResult() {
            return result;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("ListBucketResult")) {
                if (name.equals("Contents")) {
                    currentObject = new S3ObjectSummary();
                    currentObject.setBucketName(result.getBucketName());
                }
            }

            else if (in("ListBucketResult", "Contents")) {
                if (name.equals("Owner")) {
                    currentOwner = new Owner();

                } else if (name.equals("RestoreStatus")) {
                    currentRestoreStatus = new RestoreStatus();

                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (atTopLevel()) {
                if (name.equals("ListBucketResult")) {
                    /*
                     * S3 only includes the NextContinuationToken XML element if the
                     * request specified a delimiter, but for consistency we'd
                     * like to always give easy access to the next token if
                     * we're returning a list of results that's truncated.
                     */
                    if (result.isTruncated()
                            && result.getNextContinuationToken() == null) {

                        String nextContinuationToken = null;
                        if (!result.getObjectSummaries().isEmpty()) {
                            nextContinuationToken = result.getObjectSummaries()
                                    .get(result.getObjectSummaries().size() - 1)
                                    .getKey();

                        } else {
                            log.error("S3 response indicates truncated results, "
                                    + "but contains no object summaries.");
                        }

                        result.setNextContinuationToken(nextContinuationToken);
                    }
                }
            }

            else if (in("ListBucketResult")) {
                if (name.equals("Name")) {
                    result.setBucketName(getText());
                    if (log.isDebugEnabled()) {
                        log.debug("Examining listing for bucket: "
                                + result.getBucketName());
                    }

                } else if (name.equals("Prefix")) {
                    result.setPrefix(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("MaxKeys")) {
                    result.setMaxKeys(parseInt(getText()));

                } else if (name.equals("NextContinuationToken")) {
                    result.setNextContinuationToken(getText());

                } else if (name.equals("ContinuationToken")) {
                    result.setContinuationToken(getText());

                } else if (name.equals("StartAfter")) {
                    result.setStartAfter(decodeIfSpecified
                            (getText(), shouldSDKDecodeResponse));

                } else if (name.equals("KeyCount")) {
                    result.setKeyCount(parseInt(getText()));

                } else if (name.equals("Delimiter")) {
                    result.setDelimiter(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("EncodingType")) {
                    result.setEncodingType(checkForEmptyString(getText()));
                } else if (name.equals("IsTruncated")) {
                    String isTruncatedStr =
                            StringUtils.lowerCase(getText());

                    if (isTruncatedStr.startsWith("false")) {
                        result.setTruncated(false);
                    } else if (isTruncatedStr.startsWith("true")) {
                        result.setTruncated(true);
                    } else {
                        throw new IllegalStateException(
                                "Invalid value for IsTruncated field: "
                                        + isTruncatedStr);
                    }

                } else if (name.equals("Contents")) {
                    result.getObjectSummaries().add(currentObject);
                    currentObject = null;
                }
            }

            else if (in("ListBucketResult", "Contents")) {
                if (name.equals("Key")) {
                    lastKey = getText();
                    currentObject.setKey(decodeIfSpecified
                            (lastKey, shouldSDKDecodeResponse));
                } else if (name.equals("LastModified")) {
                    currentObject.setLastModified(
                            ServiceUtils.parseIso8601Date(getText()));

                } else if (name.equals("ETag")) {
                    currentObject.setETag(
                            ServiceUtils.removeQuotes(getText()));

                } else if (name.equals("Size")) {
                    currentObject.setSize(parseLong(getText()));

                } else if (name.equals("StorageClass")) {
                    currentObject.setStorageClass(getText());

                } else if (name.equals("Owner")) {
                    currentObject.setOwner(currentOwner);
                    currentOwner = null;

                } else if (name.equals("RestoreStatus")) {
                    currentObject.setRestoreStatus(currentRestoreStatus);
                    currentRestoreStatus = null;
                }
            }

            else if (in("ListBucketResult", "Contents", "RestoreStatus")) {
                if (name.equals("IsRestoreInProgress")) {
                    currentRestoreStatus.setIsRestoreInProgress(Boolean.parseBoolean(getText()));

                } else if (name.equals("RestoreExpiryDate")) {
                    currentRestoreStatus.setRestoreExpiryDate(ServiceUtils.parseIso8601Date(getText()));
                }
            }

            else if (in("ListBucketResult", "Contents", "Owner")) {
                if (name.equals("ID")) {
                    currentOwner.setId(getText());

                } else if (name.equals("DisplayName")) {
                    currentOwner.setDisplayName(getText());
                }
            }

            else if (in("ListBucketResult", "CommonPrefixes")) {
                if (name.equals("Prefix")) {
                    result.getCommonPrefixes().add
                            (decodeIfSpecified(getText(), shouldSDKDecodeResponse));
                }
            }
        }
    }

    /**
     * Handler for ListAllMyBuckets response XML documents. The document is
     * parsed into {@link Bucket}s available via the {@link #getBuckets()}
     * method.
     */
    public static class ListAllMyBucketsHandler extends AbstractHandler {

        private final List buckets = new ArrayList();
        private Owner bucketsOwner = null;

        private Bucket currentBucket = null;
        private String continuationToken;
        private ListBucketsPaginatedResult result = new ListBucketsPaginatedResult();

        /**
         * @return the buckets listed in the document.
         */
        public List getBuckets() {
            return buckets;
        }

        /**
         * @return the owner of the buckets.
         */
        public Owner getOwner() {
            return bucketsOwner;
        }

        public ListBucketsPaginatedResult getResult() {
            return  new ListBucketsPaginatedResult().withBuckets(buckets)
                                                    .withOwner(bucketsOwner)
                                                    .withContinuationToken(continuationToken);
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("ListAllMyBucketsResult")) {
                if (name.equals("Owner")) {
                    bucketsOwner = new Owner();
                }
            } else if (in("ListAllMyBucketsResult", "Buckets")) {
                if (name.equals("Bucket")) {
                    currentBucket = new Bucket();
                    currentBucket.setOwner(bucketsOwner);
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("ListAllMyBucketsResult")) {
                 if (name.equals("ContinuationToken")) {
                    continuationToken = getText();
                }
            } else if (in("ListAllMyBucketsResult", "Owner")) {
                if (name.equals("ID")) {
                    bucketsOwner.setId(getText());

                } else if (name.equals("DisplayName")) {
                    bucketsOwner.setDisplayName(getText());
                }
            }

            else if (in("ListAllMyBucketsResult", "Buckets")) {
                if (name.equals("Bucket")) {
                    buckets.add(currentBucket);
                    currentBucket = null;
                }
            }

            else if (in("ListAllMyBucketsResult", "Buckets", "Bucket")) {
                if (name.equals("Name")) {
                    currentBucket.setName(getText());

                } else if (name.equals("CreationDate")) {
                    Date creationDate = DateUtils.parseISO8601Date(getText());
                    currentBucket.setCreationDate(creationDate);
                }
            }
        }
    }

    /**
     * Handler for AccessControlList response XML documents. The document is
     * parsed into an {@link AccessControlList} object available via the
     * {@link #getAccessControlList()} method.
     */
    public static class AccessControlListHandler extends AbstractHandler {

        private final AccessControlList accessControlList =
            new AccessControlList();

        private Grantee currentGrantee = null;
        private Permission currentPermission = null;

        /**
         * @return an object representing the ACL document.
         */
        public AccessControlList getAccessControlList() {
            return accessControlList;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("AccessControlPolicy")) {
                if (name.equals("Owner")) {
                    accessControlList.setOwner(new Owner());

                }
            }

            else if (in("AccessControlPolicy", "AccessControlList", "Grant")) {
                if (name.equals("Grantee")) {
                    String type = XmlResponsesSaxParser
                        .findAttributeValue( "xsi:type", attrs );

                    if ("AmazonCustomerByEmail".equals(type)) {
                        currentGrantee = new EmailAddressGrantee(null);
                    } else if ("CanonicalUser".equals(type)) {
                        currentGrantee = new CanonicalGrantee(null);
                    } else if ("Group".equals(type)) {
                        /*
                         * Nothing to do for GroupGrantees here since we
                         * can't construct an empty enum value early.
                         */
                    }
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("AccessControlPolicy", "Owner")) {
                if (name.equals("ID")) {
                    accessControlList.getOwner().setId(getText());
                } else if (name.equals("DisplayName")) {
                    accessControlList.getOwner().setDisplayName(getText());
                }
            }

            else if (in("AccessControlPolicy", "AccessControlList")) {
                if (name.equals("Grant")) {
                    accessControlList.grantPermission(
                            currentGrantee, currentPermission);

                    currentGrantee = null;
                    currentPermission = null;
                }
            }

            else if (in("AccessControlPolicy", "AccessControlList", "Grant")) {
                if (name.equals("Permission")) {
                    currentPermission = Permission.parsePermission(getText());
                }
            }

            else if (in("AccessControlPolicy", "AccessControlList", "Grant", "Grantee")) {
                if (name.equals("ID")) {
                    currentGrantee.setIdentifier(getText());

                } else if (name.equals("EmailAddress")) {
                    currentGrantee.setIdentifier(getText());

                } else if (name.equals("URI")) {
                    /*
                     * Only GroupGrantees contain an URI element in them, and we
                     * can't construct currentGrantee during startElement for a
                     * GroupGrantee since it's an enum.
                     */
                    currentGrantee = GroupGrantee.parseGroupGrantee(getText());

                } else if (name.equals("DisplayName")) {
                    ((CanonicalGrantee) currentGrantee)
                        .setDisplayName(getText());
                }
            }
        }
    }

    /**
     * Handler for LoggingStatus response XML documents for a bucket. The
     * document is parsed into an {@link BucketLoggingConfiguration} object available
     * via the {@link #getBucketLoggingConfiguration()} method.
     */
    public static class BucketLoggingConfigurationHandler extends AbstractHandler {

        private final BucketLoggingConfiguration bucketLoggingConfiguration =
                new BucketLoggingConfiguration();

        /**
         * @return
         * an object representing the bucket's LoggingStatus document.
         */
        public BucketLoggingConfiguration getBucketLoggingConfiguration() {
            return bucketLoggingConfiguration;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("BucketLoggingStatus", "LoggingEnabled")) {
                if (name.equals("TargetBucket")) {
                    bucketLoggingConfiguration
                        .setDestinationBucketName(getText());
                } else if (name.equals("TargetPrefix")) {
                    bucketLoggingConfiguration
                        .setLogFilePrefix(getText());
                }
            } else if (in("BucketLoggingStatus", "LoggingEnabled", "TargetObjectKeyFormat")) {
                if (name.equals("SimplePrefix")) {
                    bucketLoggingConfiguration.setTargetObjectKeyFormat(
                            new TargetObjectKeyFormat(new SimplePrefix()));
                } else if (name.equals("PartitionedPrefix")) {
                    bucketLoggingConfiguration.setTargetObjectKeyFormat(
                            new TargetObjectKeyFormat(
                                    new PartitionedPrefix().withPartitionDateSource(checkForEmptyString(getText()))));
                }
            }
        }
    }

    /**
     * Handler for CreateBucketConfiguration response XML documents for a
     * bucket. The document is parsed into a String representing the bucket's
     * location, available via the {@link #getLocation()} method.
     */
    public static class BucketLocationHandler extends AbstractHandler {

        private String location = null;

        /**
         * @return
         * the bucket's location.
         */
        public String getLocation() {
            return location;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (atTopLevel()) {
                if (name.equals("LocationConstraint")) {
                    String elementText = getText();
                    if (elementText.length() == 0) {
                        location = null;
                    } else {
                        location = elementText;
                    }
                }
            }
        }
    }

    public static class CopyObjectResultHandler extends AbstractSSEHandler
            implements ObjectExpirationResult, S3RequesterChargedResult, S3VersionResult {

        // Data items for successful copy
        private final CopyObjectResult result = new CopyObjectResult();

        // Data items for failed copy
        private String errorCode = null;
        private String errorMessage = null;
        private String errorRequestId = null;
        private String errorHostId = null;
        private boolean receivedErrorResponse = false;

        @Override
        protected ServerSideEncryptionResult sseResult() {
            return result;
        }

        public Date getLastModified() {
            return result.getLastModifiedDate();
        }

        @Override
        public String getVersionId() {
            return result.getVersionId();
        }

        @Override
        public void setVersionId(String versionId) {
            result.setVersionId(versionId);
        }

        @Override
        public Date getExpirationTime() {
            return result.getExpirationTime();
        }

        @Override
        public void setExpirationTime(Date expirationTime) {
            result.setExpirationTime(expirationTime);
        }

        @Override
        public String getExpirationTimeRuleId() {
            return result.getExpirationTimeRuleId();
        }

        @Override
        public void setExpirationTimeRuleId(String expirationTimeRuleId) {
            result.setExpirationTimeRuleId(expirationTimeRuleId);
        }

        public String getETag() {
            return result.getETag();
        }

        public String getErrorCode() {
            return errorCode;
        }

        public String getErrorHostId() {
            return errorHostId;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public String getErrorRequestId() {
            return errorRequestId;
        }

        public boolean isErrorResponse() {
            return receivedErrorResponse;
        }

        public boolean isRequesterCharged() {
            return result.isRequesterCharged();
        }

        public void setRequesterCharged(boolean isRequesterCharged) {
            result.setRequesterCharged(isRequesterCharged);
        }


        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (atTopLevel()) {
                if (name.equals("CopyObjectResult") || name.equals("CopyPartResult")) {
                    receivedErrorResponse = false;
                } else if (name.equals("Error")) {
                    receivedErrorResponse = true;
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("CopyObjectResult") || in ("CopyPartResult")) {
                if (name.equals("LastModified")) {
                    result.setLastModifiedDate(ServiceUtils.parseIso8601Date(getText()));
                } else if (name.equals("ETag")) {
                    result.setETag(ServiceUtils.removeQuotes(getText()));
                }
            }

            else if (in("Error")) {
                if (name.equals("Code")) {
                    errorCode = getText();
                } else if (name.equals("Message")) {
                    errorMessage = getText();
                } else if (name.equals("RequestId")) {
                    errorRequestId = getText();
                } else if (name.equals("HostId")) {
                    errorHostId = getText();
                }
            }
        }
    }

    /**
     * Handler for parsing RequestPaymentConfiguration XML response associated
     * with an Amazon S3 bucket. The XML response is parsed into a
     * RequestPaymentConfiguration object.
     */
    public static class RequestPaymentConfigurationHandler extends AbstractHandler {

        private String payer = null;

        public RequestPaymentConfiguration getConfiguration(){
            return new RequestPaymentConfiguration(Payer.valueOf(payer));
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("RequestPaymentConfiguration")) {
                if (name.equals("Payer")) {
                    payer = getText();
                }
            }
        }
    }

    /**
     * Handler for ListVersionsResult XML document.
     */
    public static class ListVersionsHandler extends AbstractHandler {

        private final VersionListing versionListing = new VersionListing();
        private final boolean shouldSDKDecodeResponse;

        private S3VersionSummary currentVersionSummary;
        private Owner currentOwner;
        private RestoreStatus currentRestoreStatus;

        public ListVersionsHandler(final boolean shouldSDKDecodeResponse) {
            this.shouldSDKDecodeResponse = shouldSDKDecodeResponse;
        }

        public VersionListing getListing() {
            return versionListing;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("ListVersionsResult")) {
                if (name.equals("Version")) {
                    currentVersionSummary = new S3VersionSummary();
                    currentVersionSummary.setBucketName(
                            versionListing.getBucketName());

                } else if (name.equals("DeleteMarker")) {
                    currentVersionSummary = new S3VersionSummary();
                    currentVersionSummary.setBucketName(
                            versionListing.getBucketName());
                    currentVersionSummary.setIsDeleteMarker(true);
                }
            }

            else if (in("ListVersionsResult", "Version")
                    || in("ListVersionsResult", "DeleteMarker")) {
                if (name.equals("Owner")) {
                    currentOwner = new Owner();

                } else if (name.equals("RestoreStatus")) {
                    currentRestoreStatus = new RestoreStatus();

                }
            }
        }

        @Override
        protected void doEndElement(
                String uri,
                String name,
                String qName) {

            if (in("ListVersionsResult")) {
                if (name.equals("Name")) {
                    versionListing.setBucketName(getText());

                } else if (name.equals("Prefix")) {
                    versionListing.setPrefix(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));
                } else if (name.equals("KeyMarker")) {
                    versionListing.setKeyMarker(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));
                } else if (name.equals("VersionIdMarker")) {
                    versionListing.setVersionIdMarker(checkForEmptyString(
                            getText()));

                } else if (name.equals("MaxKeys")) {
                    versionListing.setMaxKeys(Integer.parseInt(getText()));

                } else if (name.equals("Delimiter")) {
                    versionListing.setDelimiter(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("EncodingType")) {
                    versionListing.setEncodingType(shouldSDKDecodeResponse ?
                            null : checkForEmptyString(getText()));
                } else if (name.equals("NextKeyMarker")) {
                    versionListing.setNextKeyMarker(decodeIfSpecified
                            (checkForEmptyString(getText()), shouldSDKDecodeResponse));

                } else if (name.equals("NextVersionIdMarker")) {
                    versionListing.setNextVersionIdMarker(getText());

                } else if (name.equals("IsTruncated")) {
                    versionListing.setTruncated("true".equals(getText()));

                } else if (name.equals("Version")
                        || name.equals("DeleteMarker")) {

                    versionListing.getVersionSummaries()
                        .add(currentVersionSummary);

                    currentVersionSummary = null;
                }
            }

            else if (in("ListVersionsResult", "CommonPrefixes")) {
                if (name.equals("Prefix")) {
                    final String commonPrefix = checkForEmptyString(getText());
                    versionListing.getCommonPrefixes()
                        .add(shouldSDKDecodeResponse ?
                                SdkHttpUtils.urlDecode(commonPrefix) : commonPrefix);
                }
            }

            else if (in("ListVersionsResult", "Version")
                    || in("ListVersionsResult", "DeleteMarker")) {

                if (name.equals("Key")) {
                    currentVersionSummary.setKey(decodeIfSpecified(getText(), shouldSDKDecodeResponse));

                } else if (name.equals("VersionId")) {
                    currentVersionSummary.setVersionId(getText());

                } else if (name.equals("IsLatest")) {
                    currentVersionSummary.setIsLatest("true".equals(getText()));

                } else if (name.equals("LastModified")) {
                    currentVersionSummary.setLastModified(
                            ServiceUtils.parseIso8601Date(getText()));

                } else if (name.equals("ETag")) {
                    currentVersionSummary.setETag(
                            ServiceUtils.removeQuotes(getText()));

                } else if (name.equals("Size")) {
                    currentVersionSummary.setSize(Long.parseLong(getText()));

                } else if (name.equals("Owner")) {
                    currentVersionSummary.setOwner(currentOwner);
                    currentOwner = null;

                } else if (name.equals("StorageClass")) {
                    currentVersionSummary.setStorageClass(getText());

                } else if (name.equals("RestoreStatus")) {
                    currentVersionSummary.setRestoreStatus(currentRestoreStatus);
                    currentRestoreStatus = null;
                }
            }

            else if (in("ListVersionsResult", "Version", "RestoreStatus")) {
                if (name.equals("IsRestoreInProgress")) {
                    currentRestoreStatus.setIsRestoreInProgress(Boolean.parseBoolean(getText()));
                } else if (name.equals("RestoreExpiryDate")) {
                    currentRestoreStatus.setRestoreExpiryDate(ServiceUtils.parseIso8601Date(getText()));
                }
            }

            else if (in("ListVersionsResult", "Version", "Owner")
                    || in("ListVersionsResult", "DeleteMarker", "Owner")) {

                if (name.equals("ID")) {
                    currentOwner.setId(getText());
                } else if (name.equals("DisplayName")) {
                    currentOwner.setDisplayName(getText());
                }
            }
        }
    }

    public static class BucketWebsiteConfigurationHandler extends AbstractHandler {

        private final BucketWebsiteConfiguration configuration =
                new BucketWebsiteConfiguration(null);

        private RoutingRuleCondition currentCondition = null;
        private RedirectRule currentRedirectRule = null;
        private RoutingRule currentRoutingRule = null;

        public BucketWebsiteConfiguration getConfiguration() {
            return configuration;
        }

        @Override
        protected  void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("WebsiteConfiguration")) {
                if (name.equals("RedirectAllRequestsTo")) {
                    currentRedirectRule = new RedirectRule();
                }
            }

            else if (in("WebsiteConfiguration", "RoutingRules")) {
                if (name.equals("RoutingRule")) {
                    currentRoutingRule = new RoutingRule();
                }
            }

            else if (in("WebsiteConfiguration", "RoutingRules", "RoutingRule")) {
                if (name.equals("Condition")) {
                    currentCondition = new RoutingRuleCondition();
                } else if (name.equals("Redirect")) {
                    currentRedirectRule = new RedirectRule();
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("WebsiteConfiguration")) {
                if (name.equals("RedirectAllRequestsTo")) {
                    configuration.setRedirectAllRequestsTo(currentRedirectRule);
                    currentRedirectRule = null;
                }
            }

            else if (in("WebsiteConfiguration", "IndexDocument")) {
                if (name.equals("Suffix")) {
                    configuration.setIndexDocumentSuffix(getText());
                }
            }

            else if (in("WebsiteConfiguration", "ErrorDocument")) {
                if (name.equals("Key")) {
                    configuration.setErrorDocument(getText());
                }
            }

            else if (in("WebsiteConfiguration", "RoutingRules")) {
                if (name.equals("RoutingRule")) {
                    configuration.getRoutingRules().add(currentRoutingRule);
                    currentRoutingRule = null;
                }
            }

            else if (in("WebsiteConfiguration", "RoutingRules", "RoutingRule")) {
                if (name.equals("Condition")) {
                    currentRoutingRule.setCondition(currentCondition);
                    currentCondition = null;
                } else if (name.equals("Redirect")) {
                    currentRoutingRule.setRedirect(currentRedirectRule);
                    currentRedirectRule = null;
                }
            }

            else if (in("WebsiteConfiguration", "RoutingRules", "RoutingRule", "Condition")) {
                if (name.equals("KeyPrefixEquals")) {
                    currentCondition.setKeyPrefixEquals(getText());
                } else if (name.equals("HttpErrorCodeReturnedEquals")) {
                    currentCondition.setHttpErrorCodeReturnedEquals(getText());
                }
            }

            else if (in("WebsiteConfiguration", "RedirectAllRequestsTo")
                    || in("WebsiteConfiguration", "RoutingRules", "RoutingRule", "Redirect")) {

                if (name.equals("Protocol")) {
                    currentRedirectRule.setProtocol(getText());

                } else if (name.equals("HostName")) {
                    currentRedirectRule.setHostName(getText());

                } else if (name.equals("ReplaceKeyPrefixWith")) {
                    currentRedirectRule.setReplaceKeyPrefixWith(getText());

                } else if (name.equals("ReplaceKeyWith")) {
                    currentRedirectRule.setReplaceKeyWith(getText());

                } else if (name.equals("HttpRedirectCode")) {
                    currentRedirectRule.setHttpRedirectCode(getText());
                }
            }
        }
    }

    public static class BucketVersioningConfigurationHandler extends AbstractHandler {

        private final BucketVersioningConfiguration configuration =
                new BucketVersioningConfiguration();

        public BucketVersioningConfiguration getConfiguration() { return configuration; }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("VersioningConfiguration")) {
                if (name.equals("Status")) {
                    configuration.setStatus(getText());

                } else if (name.equals("MfaDelete")) {
                    String mfaDeleteStatus = getText();

                    if (mfaDeleteStatus.equals("Disabled")) {
                        configuration.setMfaDeleteEnabled(false);
                    } else if (mfaDeleteStatus.equals("Enabled")) {
                        configuration.setMfaDeleteEnabled(true);
                    } else {
                        configuration.setMfaDeleteEnabled(null);
                    }
                }
            }
        }
    }

    public static class BucketAccelerateConfigurationHandler extends AbstractHandler {

        private final BucketAccelerateConfiguration configuration = new BucketAccelerateConfiguration((String) null);

        public BucketAccelerateConfiguration getConfiguration() { return configuration; }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("AccelerateConfiguration")) {
                if (name.equals("Status")) {
                    configuration.setStatus(getText());
                }
            }
        }
    }

    /*
     * 
     * 
     *     http://Example-Bucket.s3.amazonaws.com/Example-Object
     *     Example-Bucket
     *     Example-Object
     *     "3858f62230ac3c915f300c664312c11f-9"
     * 
     *
     * Or if an error occurred while completing:
     *
     * 
     * 
     *     InternalError
     *     We encountered an internal error. Please try again.
     *     656c76696e6727732072657175657374
     *     Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==
     * 
     */
    public static class CompleteMultipartUploadHandler extends AbstractSSEHandler
            implements ObjectExpirationResult, S3VersionResult, S3RequesterChargedResult {
        // Successful completion
        private CompleteMultipartUploadResult result;

        // Error during completion
        private AmazonS3Exception ase;
        private String hostId;
        private String requestId;
        private String errorCode;

        @Override
        protected ServerSideEncryptionResult sseResult() {
            return result;
        }
        /**
         * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#getExpirationTime()
         */
        @Override
        public Date getExpirationTime() {
            return result == null ? null : result.getExpirationTime();
        }

        /**
         * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#setExpirationTime(java.util.Date)
         */
        @Override
        public void setExpirationTime(Date expirationTime) {
            if (result != null) {
                result.setExpirationTime(expirationTime);
            }
        }

        /**
         * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#getExpirationTimeRuleId()
         */
        @Override
        public String getExpirationTimeRuleId() {
            return result == null ? null : result.getExpirationTimeRuleId();
        }

        /**
         * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#setExpirationTimeRuleId(java.lang.String)
         */
        @Override
        public void setExpirationTimeRuleId(String expirationTimeRuleId) {
            if (result != null) {
                result.setExpirationTimeRuleId(expirationTimeRuleId);
            }
        }

        @Override
        public void setVersionId(String versionId) {
            if (result != null) {
                result.setVersionId(versionId);
            }
        }

        @Override
        public String getVersionId() {
            return result == null ? null : result.getVersionId();
        }

        /**
         * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#isRequesterCharged()
         */
        public boolean isRequesterCharged() {
            return result == null ? false : result.isRequesterCharged();
        }

        /**
         * @see com.amazonaws.services.s3.model.CompleteMultipartUploadResult#setRequesterCharged(boolean)
         */
        public void setRequesterCharged(boolean isRequesterCharged) {
            if (result != null) {
                result.setRequesterCharged(isRequesterCharged);
            }
        }

        public CompleteMultipartUploadResult getCompleteMultipartUploadResult() {
            return result;
        }

        public AmazonS3Exception getAmazonS3Exception() {
            return ase;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (atTopLevel()) {
                if (name.equals("CompleteMultipartUploadResult")) {
                    result = new CompleteMultipartUploadResult();
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (atTopLevel()) {
                if (name.equals("Error")) {
                    if (ase != null) {
                        ase.setErrorCode(errorCode);
                        ase.setRequestId(requestId);
                        ase.setExtendedRequestId(hostId);
                    }
                }
            }

            else if (in("CompleteMultipartUploadResult")) {
                if (name.equals("Location")) {
                    result.setLocation(getText());
                } else if (name.equals("Bucket")) {
                    result.setBucketName(getText());
                } else if (name.equals("Key")) {
                    result.setKey(getText());
                } else if (name.equals("ETag")) {
                    result.setETag(ServiceUtils.removeQuotes(getText()));
                }
            }

            else if (in("Error")) {
                if (name.equals("Code")) {
                    errorCode = getText();
                } else if (name.equals("Message")) {
                    ase = new AmazonS3Exception(getText());
                } else if (name.equals("RequestId")) {
                    requestId = getText();
                } else if (name.equals("HostId")) {
                    hostId = getText();
                }
            }
        }
    }

    /*
     * 
     * 
     *     example-bucket
     *     example-object
     *     VXBsb2FkIElEIGZvciA2aWWpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA
     * 
     */
    public static class InitiateMultipartUploadHandler extends AbstractHandler {

        private final InitiateMultipartUploadResult result =
                new InitiateMultipartUploadResult();

        public InitiateMultipartUploadResult getInitiateMultipartUploadResult() {
            return result;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("InitiateMultipartUploadResult")) {
                if (name.equals("Bucket")) {
                    result.setBucketName(getText());

                } else if (name.equals("Key")) {
                    result.setKey(getText());

                } else if (name.equals("UploadId")) {
                    result.setUploadId(getText());
                }
            }
        }
    }

    /*
     * HTTP/1.1 200 OK
     * x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==
     * x-amz-request-id: 656c76696e6727732072657175657374
     * Date: Tue, 16 Feb 2010 20:34:56 GMT
     * Content-Length: 1330
     * Connection: keep-alive
     * Server: AmazonS3
     *
     * 
     * 
     *     bucket
     *     
     *     /
     *     
     *     
     *     my-movie.m2ts
     *     YW55IGlkZWEgd2h5IGVsdmluZydzIHVwbG9hZCBmYWlsZWQ
     *     3
     *     true
     *     
     *         my-divisor
     *         XMgbGlrZSBlbHZpbmcncyBub3QgaGF2aW5nIG11Y2ggbHVjaw
     *         
     *             b1d16700c70b0b05597d7acd6a3f92be
     *             delving
     *         
     *         STANDARD
     *         Tue, 26 Jan 2010 19:42:19 GMT
     *     
     *     
     *         my-movie.m2ts
     *         VXBsb2FkIElEIGZvciBlbHZpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZA
     *         
     *             b1d16700c70b0b05597d7acd6a3f92be
     *             delving
     *         
     *         STANDARD
     *         Tue, 16 Feb 2010 20:34:56 GMT
     *     
     *     
     *         my-movie.m2ts
     *         YW55IGlkZWEgd2h5IGVsdmluZydzIHVwbG9hZCBmYWlsZWQ
     *         
     *             b1d16700c70b0b05597d7acd6a3f92be
     *             delving
     *         
     *         STANDARD
     *         Wed, 27 Jan 2010 03:02:01 GMT
     *     
     *    
     *        photos/
     *    
     *    
     *        videos/
     *    
     * 
     */
    public static class ListMultipartUploadsHandler extends AbstractHandler {

        private final MultipartUploadListing result =
                new MultipartUploadListing();

        private MultipartUpload currentMultipartUpload;
        private Owner currentOwner;

        public MultipartUploadListing getListMultipartUploadsResult() {
            return result;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("ListMultipartUploadsResult")) {
                if (name.equals("Upload")) {
                    currentMultipartUpload = new MultipartUpload();
                }
            } else if (in("ListMultipartUploadsResult", "Upload")) {
                if (name.equals("Owner") || name.equals("Initiator")) {
                    currentOwner = new Owner();
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("ListMultipartUploadsResult")) {
                if (name.equals("Bucket")) {
                    result.setBucketName(getText());
                } else if (name.equals("KeyMarker")) {
                    result.setKeyMarker(checkForEmptyString(getText()));
                } else if (name.equals("Delimiter")) {
                    result.setDelimiter(checkForEmptyString(getText()));
                } else if (name.equals("Prefix")) {
                    result.setPrefix(checkForEmptyString(getText()));
                } else if (name.equals("UploadIdMarker")) {
                    result.setUploadIdMarker(checkForEmptyString(getText()));
                } else if (name.equals("NextKeyMarker")) {
                    result.setNextKeyMarker(checkForEmptyString(getText()));
                } else if (name.equals("NextUploadIdMarker")) {
                    result.setNextUploadIdMarker(checkForEmptyString(getText()));
                } else if (name.equals("MaxUploads")) {
                    result.setMaxUploads(Integer.parseInt(getText()));
                } else if (name.equals("EncodingType")) {
                    result.setEncodingType(checkForEmptyString(getText()));
                } else if (name.equals("IsTruncated")) {
                    result.setTruncated(Boolean.parseBoolean(getText()));
                } else if (name.equals("Upload")) {
                    result.getMultipartUploads().add(currentMultipartUpload);
                    currentMultipartUpload = null;
                }
            }

            else if (in("ListMultipartUploadsResult", "CommonPrefixes")) {
                if (name.equals("Prefix")) {
                    result.getCommonPrefixes().add(getText());
                }
            }

            else if (in("ListMultipartUploadsResult", "Upload")) {
                if (name.equals("Key")) {
                    currentMultipartUpload.setKey(getText());
                } else if (name.equals("UploadId")) {
                    currentMultipartUpload.setUploadId(getText());
                } else if (name.equals("Owner")) {
                    currentMultipartUpload.setOwner(currentOwner);
                    currentOwner = null;
                } else if (name.equals("Initiator")) {
                    currentMultipartUpload.setInitiator(currentOwner);
                    currentOwner = null;
                } else if (name.equals("StorageClass")) {
                    currentMultipartUpload.setStorageClass(getText());
                } else if (name.equals("Initiated")) {
                    currentMultipartUpload.setInitiated(
                            ServiceUtils.parseIso8601Date(getText()));
                }
            }

            else if (in("ListMultipartUploadsResult", "Upload", "Owner")
                  || in("ListMultipartUploadsResult", "Upload", "Initiator")) {

                if (name.equals("ID")) {
                    currentOwner.setId(checkForEmptyString(getText()));
                } else if (name.equals("DisplayName")) {
                    currentOwner.setDisplayName(checkForEmptyString(getText()));
                }
            }
        }
    }

    /*
     * HTTP/1.1 200 OK
     * x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==
     * x-amz-request-id: 656c76696e6727732072657175657374
     * Date: Tue, 16 Feb 2010 20:34:56 GMT
     * Content-Length: 985
     * Connection: keep-alive
     * Server: AmazonS3
     *
     * 
     * 
     *     example-bucket
     *     example-object
     *     XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA
     *     
     *         x1x16700c70b0b05597d7ecd6a3f92be
     *         username
     *     
     *     
     *         x1x16700c70b0b05597d7ecd6a3f92be
     *         username
     *     
     *     STANDARD
     *     1
     *     3
     *     2
     *     true
     *     
     *         2
     *         Wed, 27 Jan 2010 03:02:03 GMT
     *         "7778aef83f66abc1fa1e8477f296d394"
     *         10485760
     *     
     *     
     *        3
     *        Wed, 27 Jan 2010 03:02:02 GMT
     *        "aaaa18db4cc2f85cedef654fccc4a4x8"
     *        10485760
     *     
     * 
     */
    public static class ListPartsHandler extends AbstractHandler {

        private final PartListing result = new PartListing();

        private PartSummary currentPart;
        private Owner currentOwner;

        public PartListing getListPartsResult() {
            return result;
        }

        @Override
        protected void doStartElement(
                String uri,
                String name,
                String qName,
                Attributes attrs) {

            if (in("ListPartsResult")) {
                if (name.equals("Part")) {
                    currentPart = new PartSummary();
                } else if (name.equals("Owner") || name.equals("Initiator")) {
                    currentOwner = new Owner();
                }
            }
        }

        @Override
        protected void doEndElement(String uri, String name, String qName) {
            if (in("ListPartsResult")) {
                if (name.equals("Bucket")) {
                    result.setBucketName(getText());
                } else if (name.equals("Key")) {
                    result.setKey(getText());
                } else if (name.equals("UploadId")) {
                    result.setUploadId(getText());
                } else if (name.equals("Owner")) {
                    result.setOwner(currentOwner);
                    currentOwner = null;
                } else if (name.equals("Initiator")) {
                    result.setInitiator(currentOwner);
                    currentOwner = null;
                } else if (name.equals("StorageClass")) {
                    result.setStorageClass(getText());
                } else if (name.equals("PartNumberMarker")) {
                    result.setPartNumberMarker(parseInteger(getText()));
                } else if (name.equals("NextPartNumberMarker")) {
                    result.setNextPartNumberMarker(parseInteger(getText()));
                } else if (name.equals("MaxParts")) {
                    result.setMaxParts(parseInteger(getText()));
                } else if (name.equals("EncodingType")) {
                    result.setEncodingType(checkForEmptyString(getText()));
                } else if (name.equals("IsTruncated")) {
                    result.setTruncated(Boolean.parseBoolean(getText()));
                } else if (name.equals("Part")) {
                    result.getParts().add(currentPart);
                    currentPart = null;
                }
            }

            else if (in("ListPartsResult", "Part")) {
                if (name.equals("PartNumber")) {
                    currentPart.setPartNumber(Integer.parseInt(getText()));
                } else if (name.equals("LastModified")) {
                    currentPart.setLastModified(
                            ServiceUtils.parseIso8601Date(getText()));
                } else if (name.equals("ETag")) {
                    currentPart.setETag(ServiceUtils.removeQuotes(getText()));
                } else if (name.equals("Size")) {
                    currentPart.setSize(Long.parseLong(getText()));
                }
            }

            else if (in("ListPartsResult", "Owner")
                  || in("ListPartsResult", "Initiator")) {

                if (name.equals("ID")) {
                    currentOwner.setId(checkForEmptyString(getText()));
                } else if (name.equals("DisplayName")) {
                    currentOwner.setDisplayName(checkForEmptyString(getText()));
                }
            }
        }

        private Integer parseInteger(String text) {
            text = checkForEmptyString(getText());
            if (text == null) return null;
            return Integer.parseInt(text);
        }
    }

    /**
     * Handler for parsing the get replication configuration response from
     * Amazon S3. Sample HTTP response is given below.
     *
     * 
     * 
     * 	
     *   	replication-rule-1-1421862858808
     *   	testPrefix1
     *   	Enabled
     *   	
     *       	bucketARN
     *   	
     *	
     *	
     *   	replication-rule-2-1421862858808
     *   	testPrefix2
     *   	Disabled
     *   	
     *       	arn:aws:s3:::bucket-dest-replication-integ-test-1421862858808
     *   	
     *	
     * 
     * 
*/ public static class BucketReplicationConfigurationHandler extends AbstractHandler { private final BucketReplicationConfiguration bucketReplicationConfiguration = new BucketReplicationConfiguration(); private String currentRuleId; private ReplicationRule currentRule; private ReplicationFilter currentFilter; private List andOperandsList; private String currentTagKey; private String currentTagValue; private ExistingObjectReplication existingObjectReplication; private DeleteMarkerReplication deleteMarkerReplication; private ReplicationDestinationConfig destinationConfig; private AccessControlTranslation accessControlTranslation; private EncryptionConfiguration encryptionConfiguration; private ReplicationTime replicationTime; private Metrics metrics; private SourceSelectionCriteria sourceSelectionCriteria; private SseKmsEncryptedObjects sseKmsEncryptedObjects; private ReplicaModifications replicaModifications; private static final String REPLICATION_CONFIG = "ReplicationConfiguration"; private static final String ROLE = "Role"; private static final String RULE = "Rule"; private static final String DESTINATION = "Destination"; private static final String ID = "ID"; private static final String PREFIX = "Prefix"; private static final String FILTER = "Filter"; private static final String AND = "And"; private static final String TAG = "Tag"; private static final String TAG_KEY = "Key"; private static final String TAG_VALUE = "Value"; private static final String EXISTING_OBJECT_REPLICATION = "ExistingObjectReplication"; private static final String DELETE_MARKER_REPLICATION = "DeleteMarkerReplication"; private static final String PRIORITY = "Priority"; private static final String STATUS = "Status"; private static final String BUCKET = "Bucket"; private static final String STORAGECLASS = "StorageClass"; private static final String ACCOUNT = "Account"; private static final String ACCESS_CONTROL_TRANSLATION = "AccessControlTranslation"; private static final String OWNER = "Owner"; private static final String ENCRYPTION_CONFIGURATION = "EncryptionConfiguration"; private static final String REPLICATION_TIME = "ReplicationTime"; private static final String TIME = "Time"; private static final String MINUTES = "Minutes"; private static final String METRICS = "Metrics"; private static final String EVENT_THRESHOLD = "EventThreshold"; private static final String REPLICA_KMS_KEY_ID = "ReplicaKmsKeyID"; private static final String SOURCE_SELECTION_CRITERIA = "SourceSelectionCriteria"; private static final String SSE_KMS_ENCRYPTED_OBJECTS = "SseKmsEncryptedObjects"; private static final String REPLICA_MODIFICATIONS = "ReplicaModifications"; public BucketReplicationConfiguration getConfiguration() { return bucketReplicationConfiguration; } @Override protected void doStartElement(String uri, String name, String qName, Attributes attrs) { if (in(REPLICATION_CONFIG)) { if (name.equals(RULE)) { currentRule = new ReplicationRule(); } } else if (in(REPLICATION_CONFIG, RULE)) { if (name.equals(DESTINATION)) { destinationConfig = new ReplicationDestinationConfig(); } else if (name.equals(SOURCE_SELECTION_CRITERIA)) { sourceSelectionCriteria = new SourceSelectionCriteria(); } else if (name.equals(EXISTING_OBJECT_REPLICATION)) { existingObjectReplication = new ExistingObjectReplication(); } else if (name.equals(DELETE_MARKER_REPLICATION)) { deleteMarkerReplication = new DeleteMarkerReplication(); } else if (name.equals(FILTER)) { currentFilter = new ReplicationFilter(); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION)) { if (name.equals(ACCESS_CONTROL_TRANSLATION)) { accessControlTranslation = new AccessControlTranslation(); } else if (name.equals(ENCRYPTION_CONFIGURATION)) { encryptionConfiguration = new EncryptionConfiguration(); } else if (name.equals(REPLICATION_TIME)) { replicationTime = new ReplicationTime(); } else if (name.equals(METRICS)) { metrics = new Metrics(); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, REPLICATION_TIME)) { if (name.equals(TIME)) { replicationTime.setTime(new ReplicationTimeValue()); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, METRICS)) { if (name.equals(EVENT_THRESHOLD)) { metrics.setEventThreshold(new ReplicationTimeValue()); } } else if (in(REPLICATION_CONFIG, RULE, SOURCE_SELECTION_CRITERIA)) { if (name.equals(SSE_KMS_ENCRYPTED_OBJECTS)) { sseKmsEncryptedObjects = new SseKmsEncryptedObjects(); } else if (name.equals(REPLICA_MODIFICATIONS)) { replicaModifications = new ReplicaModifications(); } } else if (in(REPLICATION_CONFIG, RULE, FILTER)) { if (name.equals(AND)) { andOperandsList = new ArrayList(); } } } @Override protected void doEndElement(String uri, String name, String qName) { if (in(REPLICATION_CONFIG)) { if (name.equals(RULE)) { bucketReplicationConfiguration.addRule(currentRuleId, currentRule); currentRule = null; currentRuleId = null; existingObjectReplication = null; deleteMarkerReplication = null; destinationConfig = null; sseKmsEncryptedObjects = null; accessControlTranslation = null; encryptionConfiguration = null; replicaModifications = null; } else if (name.equals(ROLE)) { bucketReplicationConfiguration.setRoleARN(getText()); } } else if (in(REPLICATION_CONFIG, RULE)) { if (name.equals(ID)) { currentRuleId = getText(); } else if (name.equals(PREFIX)) { currentRule.setPrefix(getText()); } else if (name.equals(PRIORITY)) { currentRule.setPriority(Integer.valueOf(getText())); } else if (name.equals(EXISTING_OBJECT_REPLICATION)){ currentRule.setExistingObjectReplication(existingObjectReplication); } else if (name.equals(DELETE_MARKER_REPLICATION)) { currentRule.setDeleteMarkerReplication(deleteMarkerReplication); } else if (name.equals(SOURCE_SELECTION_CRITERIA)) { currentRule.setSourceSelectionCriteria(sourceSelectionCriteria); } else if (name.equals(FILTER)) { currentRule.setFilter(currentFilter); currentFilter = null; } else { if (name.equals(STATUS)) { currentRule.setStatus(getText()); } else if (name.equals(DESTINATION)) { currentRule.setDestinationConfig(destinationConfig); } } } else if (in(REPLICATION_CONFIG, RULE, FILTER)) { if (name.equals(PREFIX)) { currentFilter.setPredicate(new ReplicationPrefixPredicate(getText())); } else if (name.equals(TAG)) { currentFilter.setPredicate(new ReplicationTagPredicate(new Tag(currentTagKey, currentTagValue))); currentTagKey = null; currentTagValue = null; } else if (name.equals(AND)) { currentFilter.setPredicate(new ReplicationAndOperator(andOperandsList)); andOperandsList = null; } } else if (in(REPLICATION_CONFIG, RULE, FILTER, TAG)) { if (name.equals(TAG_KEY)) { currentTagKey = getText(); } else if (name.equals(TAG_VALUE)) { currentTagValue = getText(); } } else if (in(REPLICATION_CONFIG, RULE, FILTER, AND)) { if (name.equals(PREFIX)) { andOperandsList.add(new ReplicationPrefixPredicate(getText())); } else if (name.equals(TAG)) { andOperandsList.add(new ReplicationTagPredicate(new Tag(currentTagKey, currentTagValue))); currentTagKey = null; currentTagValue = null; } } else if (in(REPLICATION_CONFIG, RULE, FILTER, AND, TAG)) { if (name.equals(TAG_KEY)) { currentTagKey = getText(); } else if (name.equals(TAG_VALUE)) { currentTagValue = getText(); } } else if (in(REPLICATION_CONFIG, RULE, SOURCE_SELECTION_CRITERIA)) { if (name.equals(SSE_KMS_ENCRYPTED_OBJECTS)) { sourceSelectionCriteria.setSseKmsEncryptedObjects(sseKmsEncryptedObjects); } else if (name.equals(REPLICA_MODIFICATIONS)) { sourceSelectionCriteria.setReplicaModifications(replicaModifications); } } else if (in(REPLICATION_CONFIG, RULE, SOURCE_SELECTION_CRITERIA, SSE_KMS_ENCRYPTED_OBJECTS)) { if (name.equals(STATUS)) { sseKmsEncryptedObjects.setStatus(getText()); } } else if (in(REPLICATION_CONFIG, RULE, SOURCE_SELECTION_CRITERIA, REPLICA_MODIFICATIONS)) { if (name.equals(STATUS)) { replicaModifications.setStatus(getText()); } } else if (in(REPLICATION_CONFIG, RULE, EXISTING_OBJECT_REPLICATION)) { if (name.equals(STATUS)) { existingObjectReplication.setStatus(getText()); } } else if (in(REPLICATION_CONFIG, RULE, DELETE_MARKER_REPLICATION)) { if (name.equals(STATUS)) { deleteMarkerReplication.setStatus(getText()); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION)) { if (name.equals(BUCKET)) { destinationConfig.setBucketARN(getText()); } else if (name.equals(STORAGECLASS)) { destinationConfig.setStorageClass(getText()); } else if (name.equals(ACCOUNT)) { destinationConfig.setAccount(getText()); } else if (name.equals(ACCESS_CONTROL_TRANSLATION)) { destinationConfig.setAccessControlTranslation(accessControlTranslation); } else if (name.equals(ENCRYPTION_CONFIGURATION)) { destinationConfig.setEncryptionConfiguration(encryptionConfiguration); } else if (name.equals(REPLICATION_TIME)) { destinationConfig.setReplicationTime(replicationTime); } else if (name.equals(METRICS)) { destinationConfig.setMetrics(metrics); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, ACCESS_CONTROL_TRANSLATION)) { if (name.equals(OWNER)) { accessControlTranslation.setOwner(getText()); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, ENCRYPTION_CONFIGURATION)) { if (name.equals(REPLICA_KMS_KEY_ID)) { encryptionConfiguration.setReplicaKmsKeyID(getText()); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, REPLICATION_TIME)) { if (name.equals(STATUS)) { replicationTime.setStatus(getText()); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, REPLICATION_TIME, TIME)) { if (name.equals(MINUTES)) { replicationTime.getTime().setMinutes(Integer.parseInt(getText())); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, METRICS)) { if (name.equals(STATUS)) { metrics.setStatus(getText()); } } else if (in(REPLICATION_CONFIG, RULE, DESTINATION, METRICS, EVENT_THRESHOLD)) { if (name.equals(MINUTES)) { metrics.getEventThreshold().setMinutes(Integer.parseInt(getText())); } } } } public static class BucketTaggingConfigurationHandler extends AbstractHandler { private final BucketTaggingConfiguration configuration = new BucketTaggingConfiguration(); private Map currentTagSet; private String currentTagKey; private String currentTagValue; public BucketTaggingConfiguration getConfiguration() { return configuration; } @Override protected void doStartElement( String uri, String name, String qName, Attributes attrs) { if (in("Tagging")) { if (name.equals("TagSet")) { currentTagSet = new LinkedHashMap(); } } } @Override protected void doEndElement(String uri, String name, String qName) { if (in("Tagging")) { if (name.equals("TagSet")) { configuration.getAllTagSets() .add(new TagSet(currentTagSet)); currentTagSet = null; } } else if (in("Tagging", "TagSet")) { if (name.equals("Tag")) { if (currentTagKey != null && currentTagValue != null) { currentTagSet.put(currentTagKey, currentTagValue); } currentTagKey = null; currentTagValue = null; } } else if (in("Tagging", "TagSet", "Tag")) { if (name.equals("Key")) { currentTagKey = getText(); } else if (name.equals("Value")) { currentTagValue = getText(); } } } } /** * Handler for unmarshalling the response from GET Object Tagging. * * * * * Foo * 1 * * * Bar * 2 * * * Baz * 3 * * * */ public static class GetObjectTaggingHandler extends AbstractHandler { private GetObjectTaggingResult getObjectTaggingResult; private List tagSet; private String currentTagValue; private String currentTagKey; public GetObjectTaggingResult getResult() { return getObjectTaggingResult; } @Override protected void doStartElement(String uri, String name, String qName, Attributes attrs) { if (in("Tagging")) { if (name.equals("TagSet")) { tagSet = new ArrayList(); } } } @Override protected void doEndElement(String uri, String name, String qName) { if (in("Tagging")) { if (name.equals("TagSet")) { getObjectTaggingResult = new GetObjectTaggingResult(tagSet); tagSet = null; } } if (in("Tagging", "TagSet")) { if (name.equals("Tag")) { tagSet.add(new Tag(currentTagKey, currentTagValue)); currentTagKey = null; currentTagValue = null; } } else if (in("Tagging", "TagSet", "Tag")) { if (name.equals("Key")) { currentTagKey = getText(); } else if (name.equals("Value")) { currentTagValue = getText(); } } } } /* HTTP/1.1 200 OK x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 20 Sep 2012 20:34:56 GMT Content-Type: application/xml Transfer-Encoding: chunked Connection: keep-alive Server: AmazonS3 Key Version Key Version Code Message Key true Version */ public static class DeleteObjectsHandler extends AbstractHandler { private final DeleteObjectsResponse response = new DeleteObjectsResponse(); private DeletedObject currentDeletedObject = null; private DeleteError currentError = null; public DeleteObjectsResponse getDeleteObjectResult() { return response; } @Override protected void doStartElement( String uri, String name, String qName, Attributes attrs) { if (in("DeleteResult")) { if (name.equals("Deleted")) { currentDeletedObject = new DeletedObject(); } else if (name.equals("Error")) { currentError = new DeleteError(); } } } @Override protected void doEndElement(String uri, String name, String qName) { if (in("DeleteResult")) { if (name.equals("Deleted")) { response.getDeletedObjects().add(currentDeletedObject); currentDeletedObject = null; } else if (name.equals("Error")) { response.getErrors().add(currentError); currentError = null; } } else if (in("DeleteResult", "Deleted")) { if (name.equals("Key")) { currentDeletedObject.setKey(getText()); } else if (name.equals("VersionId")) { currentDeletedObject.setVersionId(getText()); } else if (name.equals("DeleteMarker")) { currentDeletedObject.setDeleteMarker( getText().equals("true")); } else if (name.equals("DeleteMarkerVersionId")) { currentDeletedObject.setDeleteMarkerVersionId(getText()); } } else if (in("DeleteResult", "Error")) { if (name.equals("Key")) { currentError.setKey(getText()); } else if (name.equals("VersionId")) { currentError.setVersionId(getText()); } else if (name.equals("Code")) { currentError.setCode(getText()); } else if (name.equals("Message")) { currentError.setMessage(getText()); } } else if (in("Error")) { if (name.equals("Code") && getText().equals("SlowDown")) { throw new MultiObjectDeleteSlowdownException(); } } } } /** * HTTP/1.1 200 OK x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 20 Sep 2012 20:34:56 GMT Content-Length: xxx Connection: keep-alive Server: AmazonS3 logs-rule logs/ Enabled logs/ key1 value1 logs/ key1 value1 key1 value1 30 STANDARD_IA 90 GLACIER 365 7 STANDARD_IA 14 GLACIER 365 image-rule image/ Enabled 2012-12-31T00:00:00.000Z GLACIER 2020-12-31T00:00:00.000Z 10 */ public static class BucketLifecycleConfigurationHandler extends AbstractHandler { private final BucketLifecycleConfiguration configuration = new BucketLifecycleConfiguration(new ArrayList()); private Rule currentRule; private Transition currentTransition; private NoncurrentVersionTransition currentNcvTransition; private NoncurrentVersionExpiration ncvExpiration; private AbortIncompleteMultipartUpload abortIncompleteMultipartUpload; private LifecycleFilter currentFilter; private List andOperandsList; private String currentTagKey; private String currentTagValue; public BucketLifecycleConfiguration getConfiguration() { return configuration; } @Override protected void doStartElement( String uri, String name, String qName, Attributes attrs) { if (in("LifecycleConfiguration")) { if (name.equals("Rule")) { currentRule = new Rule(); } } else if (in("LifecycleConfiguration", "Rule")) { if (name.equals("Transition")) { currentTransition = new Transition(); } else if (name.equals("NoncurrentVersionTransition")) { currentNcvTransition = new NoncurrentVersionTransition(); } else if (name.equals("NoncurrentVersionExpiration")) { ncvExpiration = new NoncurrentVersionExpiration(); } else if (name.equals("AbortIncompleteMultipartUpload")) { abortIncompleteMultipartUpload = new AbortIncompleteMultipartUpload(); } else if (name.equals("Filter")) { currentFilter = new LifecycleFilter(); } } else if (in("LifecycleConfiguration", "Rule", "Filter")) { if (name.equals("And")) { andOperandsList = new ArrayList(); } } } @Override protected void doEndElement(String uri, String name, String qName) { if (in("LifecycleConfiguration")) { if (name.equals("Rule")) { configuration.getRules().add(currentRule); currentRule = null; } } else if (in("LifecycleConfiguration", "Rule")) { if ( name.equals("ID") ) { currentRule.setId(getText()); } else if ( name.equals("Prefix") ) { currentRule.setPrefix(getText()); } else if ( name.equals("Status") ) { currentRule.setStatus(getText()); } else if (name.equals("Transition")) { currentRule.addTransition(currentTransition); currentTransition = null; } else if (name.equals("NoncurrentVersionTransition")) { currentRule.addNoncurrentVersionTransition( currentNcvTransition); currentNcvTransition = null; } else if (name.equals("NoncurrentVersionExpiration")) { currentRule.setNoncurrentVersionExpiration(ncvExpiration); ncvExpiration = null; } else if (name.equals("AbortIncompleteMultipartUpload")) { currentRule.setAbortIncompleteMultipartUpload(abortIncompleteMultipartUpload); abortIncompleteMultipartUpload = null; } else if (name.equals("Filter")) { currentRule.setFilter(currentFilter); currentFilter = null; } } else if (in("LifecycleConfiguration", "Rule", "Expiration")) { if (name.equals("Date")) { currentRule.setExpirationDate(ServiceUtils.parseIso8601Date(getText())); } else if (name.equals("Days")) { currentRule.setExpirationInDays(Integer.parseInt(getText())); } else if (name.equals("ExpiredObjectDeleteMarker")) { if ("true".equals(getText())) { currentRule.setExpiredObjectDeleteMarker(true); } } } else if (in("LifecycleConfiguration", "Rule", "Transition")) { if (name.equals("StorageClass")) { currentTransition.setStorageClass(getText()); } else if (name.equals("Date")) { currentTransition.setDate( ServiceUtils.parseIso8601Date(getText())); } else if (name.equals("Days")) { currentTransition.setDays(Integer.parseInt(getText())); } } else if (in("LifecycleConfiguration", "Rule", "NoncurrentVersionExpiration")) { if (name.equals("NoncurrentDays")) { ncvExpiration.setDays(Integer.parseInt(getText())); } else if (name.equals("NewerNoncurrentVersions")) { ncvExpiration.setNewerNoncurrentVersions(Integer.parseInt(getText())); } } else if (in("LifecycleConfiguration", "Rule", "NoncurrentVersionTransition")) { if (name.equals("StorageClass")) { currentNcvTransition.setStorageClass(getText()); } else if (name.equals("NoncurrentDays")) { currentNcvTransition.setDays(Integer.parseInt(getText())); } else if (name.equals("NewerNoncurrentVersions")) { currentNcvTransition.setNewerNoncurrentVersions(Integer.parseInt(getText())); } } else if (in("LifecycleConfiguration", "Rule", "AbortIncompleteMultipartUpload")) { if (name.equals("DaysAfterInitiation")) { abortIncompleteMultipartUpload.setDaysAfterInitiation (Integer.parseInt(getText())); } } else if (in("LifecycleConfiguration", "Rule", "Filter")) { if (name.equals("Prefix")) { currentFilter.setPredicate(new LifecyclePrefixPredicate(getText())); } else if (name.equals("Tag")) { currentFilter.setPredicate(new LifecycleTagPredicate(new Tag(currentTagKey, currentTagValue))); currentTagKey = null; currentTagValue = null; } else if (name.equals("ObjectSizeGreaterThan")) { currentFilter.setPredicate(new LifecycleObjectSizeGreaterThanPredicate(Long.parseLong(getText()))); } else if (name.equals("ObjectSizeLessThan")) { currentFilter.setPredicate(new LifecycleObjectSizeLessThanPredicate(Long.parseLong(getText()))); } else if (name.equals("And")) { currentFilter.setPredicate(new LifecycleAndOperator(andOperandsList)); andOperandsList = null; } } else if (in("LifecycleConfiguration", "Rule", "Filter", "Tag")) { if (name.equals("Key")) { currentTagKey = getText(); } else if (name.equals("Value")) { currentTagValue = getText(); } } else if (in("LifecycleConfiguration", "Rule", "Filter", "And")) { if (name.equals("Prefix")) { andOperandsList.add(new LifecyclePrefixPredicate(getText())); } else if (name.equals("Tag")) { andOperandsList.add(new LifecycleTagPredicate(new Tag(currentTagKey, currentTagValue))); currentTagKey = null; currentTagValue = null; } else if (name.equals("ObjectSizeGreaterThan")) { andOperandsList.add(new LifecycleObjectSizeGreaterThanPredicate(Long.parseLong(getText()))); } else if (name.equals("ObjectSizeLessThan")) { andOperandsList.add(new LifecycleObjectSizeLessThanPredicate(Long.parseLong(getText()))); } } else if (in("LifecycleConfiguration", "Rule", "Filter", "And", "Tag")) { if (name.equals("Key")) { currentTagKey = getText(); } else if (name.equals("Value")) { currentTagValue = getText(); } } } } /* HTTP/1.1 200 OK x-amz-id-2: Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== x-amz-request-id: 656c76696e6727732072657175657374 Date: Tue, 20 Sep 2011 20:34:56 GMT Content-Length: Some Length Connection: keep-alive Server: AmazonS3 http://www.foobar.com GET 3000 x-amz-server-side-encryption */ public static class BucketCrossOriginConfigurationHandler extends AbstractHandler { private final BucketCrossOriginConfiguration configuration = new BucketCrossOriginConfiguration(new ArrayList()); private CORSRule currentRule; private List allowedMethods = null; private List allowedOrigins = null; private List exposedHeaders = null; private List allowedHeaders = null; public BucketCrossOriginConfiguration getConfiguration() { return configuration; } @Override protected void doStartElement( String uri, String name, String qName, Attributes attrs) { if (in("CORSConfiguration")) { if (name.equals("CORSRule")) { currentRule = new CORSRule(); } } else if (in("CORSConfiguration", "CORSRule")) { if (name.equals("AllowedOrigin")) { if (allowedOrigins == null) { allowedOrigins = new ArrayList(); } } else if (name.equals("AllowedMethod")) { if (allowedMethods == null) { allowedMethods = new ArrayList(); } } else if (name.equals("ExposeHeader")) { if (exposedHeaders == null) { exposedHeaders = new ArrayList(); } } else if (name.equals("AllowedHeader")) { if (allowedHeaders == null) { allowedHeaders = new LinkedList(); } } } } @Override protected void doEndElement(String uri, String name, String qName) { if (in("CORSConfiguration")) { if (name.equals("CORSRule")) { currentRule.setAllowedHeaders(allowedHeaders); currentRule.setAllowedMethods(allowedMethods); currentRule.setAllowedOrigins(allowedOrigins); currentRule.setExposedHeaders(exposedHeaders); allowedHeaders = null; allowedMethods = null; allowedOrigins = null; exposedHeaders = null; configuration.getRules().add(currentRule); currentRule = null; } } else if (in("CORSConfiguration", "CORSRule")) { if (name.equals("ID")) { currentRule.setId(getText()); } else if (name.equals("AllowedOrigin")) { allowedOrigins.add(getText()); } else if (name.equals("AllowedMethod")) { allowedMethods.add(AllowedMethods.fromValue(getText())); } else if (name.equals("MaxAgeSeconds")) { currentRule.setMaxAgeSeconds(Integer.parseInt(getText())); } else if (name.equals("ExposeHeader")) { exposedHeaders.add(getText()); } else if (name.equals("AllowedHeader")) { allowedHeaders.add(getText()); } } } } /* HTTP/1.1 200 OK x-amz-id-2: ITnGT1y4RyTmXa3rPi4hklTXouTf0hccUjo0iCPjz6FnfIutBj3M7fPGlWO2SEWp x-amz-request-id: 51991C342C575321 Date: Wed, 14 May 2014 02:11:22 GMT Server: AmazonS3 Content-Length: ... metrics-id