Java tutorial
/* * JetS3t : Java S3 Toolkit * Project hosted at http://bitbucket.org/jmurty/jets3t/ * * Copyright 2006-2011 James Murty * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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 org.jets3t.service.impl.rest; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Locale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jets3t.service.Constants; import org.jets3t.service.Jets3tProperties; import org.jets3t.service.ServiceException; import org.jets3t.service.acl.EmailAddressGrantee; import org.jets3t.service.acl.S3CanonicalGrantee; import org.jets3t.service.acl.S3GrantAndPermission; import org.jets3t.service.acl.S3GranteeInterface; import org.jets3t.service.acl.S3GroupGrantee; import org.jets3t.service.acl.S3Permission; //import org.jets3t.service.acl.gs.GSAccessControlList; import org.jets3t.service.model.BaseVersionOrDeleteMarker; //import org.jets3t.service.model.GSBucket; //import org.jets3t.service.model.GSBucketLoggingStatus; //import org.jets3t.service.model.GSObject; //import org.jets3t.service.model.GSOwner; import org.jets3t.service.model.S3LifecycleConfiguration; import org.jets3t.service.model.S3LifecycleConfiguration.Expiration; import org.jets3t.service.model.S3LifecycleConfiguration.Rule; import org.jets3t.service.model.S3LifecycleConfiguration.TimeEvent; import org.jets3t.service.model.S3LifecycleConfiguration.Transition; import org.jets3t.service.model.MultipartCompleted; import org.jets3t.service.model.MultipartPart; import org.jets3t.service.model.MultipleDeleteResult; import org.jets3t.service.model.NotificationConfig; import org.jets3t.service.model.RedirectRule; import org.jets3t.service.model.RoutingRule; import org.jets3t.service.model.RoutingRuleCondition; import org.jets3t.service.model.S3BucketLoggingStatus; import org.jets3t.service.model.S3BucketVersioningStatus; import org.jets3t.service.model.S3DeleteMarker; import org.jets3t.service.model.S3MultipartUpload; import org.jets3t.service.model.S3Owner; import org.jets3t.service.model.S3Version; import org.jets3t.service.model.SS3Bucket; import org.jets3t.service.model.SS3Object; import org.jets3t.service.model.StorageBucket; import org.jets3t.service.model.StorageBucketLoggingStatus; import org.jets3t.service.model.StorageObject; import org.jets3t.service.model.StorageOwner; import org.jets3t.service.model.S3WebsiteConfiguration; import org.jets3t.service.utils.ServiceUtils; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * XML Sax parser to read XML documents returned by S3 via the REST interface, converting these * documents into JetS3t objects. * * @author James Murty */ public class XmlResponsesSaxParser { private static final Log log = LogFactory.getLog(XmlResponsesSaxParser.class); private XMLReader xr = null; private Jets3tProperties properties = null; private boolean isGoogleStorageMode = false; /** * Constructs the XML SAX parser. * * @param properties * the JetS3t properties that will be applied when parsing XML documents. * * @throws ServiceException */ public XmlResponsesSaxParser(Jets3tProperties properties, boolean returnGoogleStorageObjects) throws ServiceException { this.properties = properties; this.isGoogleStorageMode = returnGoogleStorageObjects; this.xr = ServiceUtils.loadXMLReader(); } protected StorageBucket newBucket() { // if (isGoogleStorageMode) // { // return new GSBucket(); // } // else { return new SS3Bucket(); } } protected StorageObject newObject() { // if (isGoogleStorageMode) // { // return new GSObject(); // } // else { return new SS3Object(); } } protected StorageOwner newOwner() { // if (isGoogleStorageMode) // { // return new GSOwner(); // } // else { return new S3Owner(); } } /** * 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 ServiceException * any parsing, IO or other exceptions are wrapped in an ServiceException. */ protected void parseXmlInputStream(DefaultHandler handler, InputStream inputStream) throws ServiceException { 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)); inputStream.close(); } 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 ServiceException("Failed to parse XML document with handler " + handler.getClass(), t); } } protected InputStream sanitizeXmlDocument(DefaultHandler handler, InputStream inputStream) throws ServiceException { if (!properties.getBoolProperty("xmlparser.sanitize-listings", true)) { // 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. String listingDoc = listingDocBuffer.toString().replaceAll("\r", "
"); sanitizedInputStream = new ByteArrayInputStream(listingDoc.getBytes(Constants.DEFAULT_ENCODING)); } 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 ServiceException( "Failed to sanitize XML document destined for handler " + handler.getClass(), t); } return sanitizedInputStream; } } /** * 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 ServiceException */ public ListBucketHandler parseListBucketResponse(InputStream inputStream) throws ServiceException { ListBucketHandler handler = new ListBucketHandler(); 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 ServiceException */ public ListAllMyBucketsHandler parseListMyBucketsResponse(InputStream inputStream) throws ServiceException { 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 ServiceException */ public AccessControlListHandler parseAccessControlListResponse(InputStream inputStream) throws ServiceException { AccessControlListHandler handler; handler = new AccessControlListHandler(); return parseAccessControlListResponse(inputStream, handler); } public StorageInfoHandler parseStorageInfoResponse(InputStream inputStream) throws ServiceException { StorageInfoHandler handler = new StorageInfoHandler(); return parseStorageInfoResponse(inputStream, handler); } public QuotaHandler parseQuotaResponse(InputStream inputStream) throws ServiceException { QuotaHandler handler = new QuotaHandler(); return parseQuotaResponse(inputStream, handler); } public StoragePolicyHandler parseStoragePolicyResponse(InputStream inputStream) throws ServiceException { StoragePolicyHandler handler = new StoragePolicyHandler(); return parseStoragePolicyResponse(inputStream, handler); } /** * Parses an AccessControlListHandler response XML document from an input stream. * * @param inputStream * XML data input stream. * @param handler * the instance of AccessControlListHandler to be used. * @return * the XML handler object populated with data parsed from the XML stream. * * @throws ServiceException */ public AccessControlListHandler parseAccessControlListResponse(InputStream inputStream, AccessControlListHandler handler) throws ServiceException { parseXmlInputStream(handler, inputStream); return handler; } public StorageInfoHandler parseStorageInfoResponse(InputStream inputStream, StorageInfoHandler handler) throws ServiceException { parseXmlInputStream(handler, inputStream); return handler; } public QuotaHandler parseQuotaResponse(InputStream inputStream, QuotaHandler handler) throws ServiceException { parseXmlInputStream(handler, inputStream); return handler; } public StoragePolicyHandler parseStoragePolicyResponse(InputStream inputStream, StoragePolicyHandler handler) throws ServiceException { 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 ServiceException */ public BucketLoggingStatusHandler parseLoggingStatusResponse(InputStream inputStream) throws ServiceException { BucketLoggingStatusHandler handler; // if (this.isGoogleStorageMode) // { // handler = new GSBucketLoggingStatusHandler(); // } // else { handler = new S3BucketLoggingStatusHandler(); } 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 ServiceException */ public BucketLoggingStatusHandler parseLoggingStatusResponse(InputStream inputStream, BucketLoggingStatusHandler handler) throws ServiceException { parseXmlInputStream(handler, inputStream); return handler; } public String parseBucketLocationResponse(InputStream inputStream) throws ServiceException { BucketLocationHandler handler = new BucketLocationHandler(); parseXmlInputStream(handler, inputStream); return handler.getLocation(); } public CopyObjectResultHandler parseCopyObjectResponse(InputStream inputStream) throws ServiceException { CopyObjectResultHandler handler = new CopyObjectResultHandler(); parseXmlInputStream(handler, inputStream); return handler; } /** * @param inputStream * * @return * true if the bucket is configured as Requester Pays, false if it is * configured as Owner pays. * * @throws ServiceException */ public boolean parseRequestPaymentConfigurationResponse(InputStream inputStream) throws ServiceException { RequestPaymentConfigurationHandler handler = new RequestPaymentConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler.isRequesterPays(); } /** * @param inputStream * * @return * true if the bucket has versioning enabled, false otherwise. * * @throws ServiceException */ public S3BucketVersioningStatus parseVersioningConfigurationResponse(InputStream inputStream) throws ServiceException { VersioningConfigurationHandler handler = new VersioningConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler.getVersioningStatus(); } public ListVersionsResultsHandler parseListVersionsResponse(InputStream inputStream) throws ServiceException { ListVersionsResultsHandler handler = new ListVersionsResultsHandler(); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } public S3MultipartUpload parseInitiateMultipartUploadResult(InputStream inputStream) throws ServiceException { MultipartUploadResultHandler handler = new MultipartUploadResultHandler(xr); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler.getMultipartUpload(); } public MultipartPart parseMultipartUploadPartCopyResult(InputStream inputStream) throws ServiceException { MultipartPartResultHandler handler = new MultipartPartResultHandler(xr); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler.getMultipartPart(); } public ListMultipartUploadsResultHandler parseListMultipartUploadsResult(InputStream inputStream) throws ServiceException { ListMultipartUploadsResultHandler handler = new ListMultipartUploadsResultHandler(xr); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } public ListMultipartPartsResultHandler parseListMultipartPartsResult(InputStream inputStream) throws ServiceException { ListMultipartPartsResultHandler handler = new ListMultipartPartsResultHandler(xr); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } public CompleteMultipartUploadResultHandler parseCompleteMultipartUploadResult(InputStream inputStream) throws ServiceException { CompleteMultipartUploadResultHandler handler = new CompleteMultipartUploadResultHandler(xr); parseXmlInputStream(handler, sanitizeXmlDocument(handler, inputStream)); return handler; } public S3WebsiteConfiguration parseWebsiteConfigurationResponse(InputStream inputStream) throws ServiceException { WebsiteConfigurationHandler handler = new WebsiteConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler.getWebsiteConfig(); } public NotificationConfig parseNotificationConfigurationResponse(InputStream inputStream) throws ServiceException { NotificationConfigurationHandler handler = new NotificationConfigurationHandler(); parseXmlInputStream(handler, inputStream); return handler.getNotificationConfig(); } public MultipleDeleteResult parseMultipleDeleteResponse(InputStream inputStream) throws ServiceException { MultipleDeleteResultHandler handler = new MultipleDeleteResultHandler(); parseXmlInputStream(handler, inputStream); return handler.getMultipleDeleteResult(); } public S3LifecycleConfiguration parseLifecycleConfigurationResponse(InputStream inputStream) throws ServiceException { LifecycleConfigurationHandler handler = new LifecycleConfigurationHandler(xr); parseXmlInputStream(handler, inputStream); return handler.getLifecycleConfig(); } ////////////// // Handlers // ////////////// /** * Handler for ListBucket response XML documents. * The document is parsed into {@link SS3Object}s available via the {@link #getObjects()} method. */ public class ListBucketHandler extends DefaultXmlHandler { private StorageObject currentObject = null; private StorageOwner currentOwner = null; private boolean insideCommonPrefixes = false; private final List<StorageObject> objects = new ArrayList<StorageObject>(); private final List<String> commonPrefixes = new ArrayList<String>(); // Listing properties. private String bucketName = null; private String requestPrefix = null; private String requestMarker = null; private long requestMaxKeys = 0; private boolean listingTruncated = false; private String lastKey = null; private String nextMarker = null; /** * If the listing is truncated this method will return the marker that should be used * in subsequent bucket list calls to complete the listing. * * @return * null if the listing is not truncated, otherwise the next marker if it's available or * the last object key seen if the next marker isn't available. */ public String getMarkerForNextListing() { if (listingTruncated) { if (nextMarker != null) { return nextMarker; } else if (lastKey != null) { return lastKey; } else { if (log.isWarnEnabled()) { log.warn("Unable to find Next Marker or Last Key for truncated listing"); } return null; } } else { return null; } } /** * @return * true if the listing document was truncated, and therefore only contained a subset of the * available S3 objects. */ public boolean isListingTruncated() { return listingTruncated; } /** * @return * the S3 objects contained in the listing. */ public StorageObject[] getObjects() { return objects.toArray(new StorageObject[objects.size()]); } public String[] getCommonPrefixes() { return commonPrefixes.toArray(new String[commonPrefixes.size()]); } public String getRequestPrefix() { return requestPrefix; } public String getRequestMarker() { return requestMarker; } public String getNextMarker() { return nextMarker; } public long getRequestMaxKeys() { return requestMaxKeys; } @Override public void startElement(String name) { if (name.equals("Contents")) { currentObject = newObject(); if (currentObject instanceof SS3Object) { ((SS3Object) currentObject).setBucketName(bucketName); } } else if (name.equals("Owner")) { currentOwner = newOwner(); currentObject.setOwner(currentOwner); } else if (name.equals("CommonPrefixes")) { insideCommonPrefixes = true; } } @Override public void endElement(String name, String elementText) { // Listing details if (name.equals("Name")) { bucketName = elementText; if (log.isDebugEnabled()) { log.debug("Examining listing for bucket: " + bucketName); } } else if (!insideCommonPrefixes && name.equals("Prefix")) { requestPrefix = elementText; } else if (name.equals("Marker")) { requestMarker = elementText; } else if (name.equals("NextMarker")) { nextMarker = elementText; } else if (name.equals("MaxKeys")) { requestMaxKeys = Long.parseLong(elementText); } else if (name.equals("IsTruncated")) { String isTruncatedStr = elementText.toLowerCase(Locale.getDefault()); if (isTruncatedStr.startsWith("false")) { listingTruncated = false; } else if (isTruncatedStr.startsWith("true")) { listingTruncated = true; } else { throw new RuntimeException("Invalid value for IsTruncated field: " + isTruncatedStr); } } // Object details. else if (name.equals("Contents")) { objects.add(currentObject); if (log.isDebugEnabled()) { log.debug("Created new object from listing: " + currentObject); } } else if (name.equals("Key")) { currentObject.setKey(elementText); lastKey = elementText; } else if (name.equals("LastModified")) { try { currentObject.setLastModifiedDate(ServiceUtils.parseIso8601Date(elementText)); } catch (ParseException e) { throw new RuntimeException( "Non-ISO8601 date for LastModified in bucket's object listing output: " + elementText, e); } } else if (name.equals("ETag")) { currentObject.setETag(elementText); } else if (name.equals("Size")) { currentObject.setContentLength(Long.parseLong(elementText)); } else if (name.equals("StorageClass")) { currentObject.setStorageClass(elementText); } // Owner details. else if (name.equals("ID")) { // Work-around to support Eucalyptus responses, which do not // contain Owner elements. if (currentOwner == null) { currentOwner = newOwner(); currentObject.setOwner(currentOwner); } currentOwner.setId(elementText); } else if (name.equals("DisplayName")) { currentOwner.setDisplayName(elementText); } // Common prefixes. else if (insideCommonPrefixes && name.equals("Prefix")) { commonPrefixes.add(elementText); } else if (name.equals("CommonPrefixes")) { insideCommonPrefixes = false; } } } /** * Handler for ListAllMyBuckets response XML documents. The document is parsed into * {@link StorageBucket}s available via the {@link #getBuckets()} method. * * @author James Murty * */ public class ListAllMyBucketsHandler extends DefaultXmlHandler { private StorageOwner bucketsOwner = null; private StorageBucket currentBucket = null; private final List<StorageBucket> buckets = new ArrayList<StorageBucket>(); /** * @return * the buckets listed in the document. */ public StorageBucket[] getBuckets() { return buckets.toArray(new StorageBucket[buckets.size()]); } /** * @return * the owner of the buckets. */ public StorageOwner getOwner() { return bucketsOwner; } @Override public void startElement(String name) { if (name.equals("Bucket")) { currentBucket = newBucket(); } else if (name.equals("Owner")) { bucketsOwner = newOwner(); } } @Override public void endElement(String name, String elementText) { // Listing details. if (name.equals("ID")) { bucketsOwner.setId(elementText); } else if (name.equals("DisplayName")) { bucketsOwner.setDisplayName(elementText); } // Bucket item details. else if (name.equals("Bucket")) { if (log.isDebugEnabled()) { log.debug("Created new bucket from listing: " + currentBucket); } currentBucket.setOwner(bucketsOwner); buckets.add(currentBucket); } else if (name.equals("Name")) { currentBucket.setName(elementText); } else if (name.equals("CreationDate")) { elementText += ".000Z"; try { currentBucket.setCreationDate(ServiceUtils.parseIso8601Date(elementText)); } catch (ParseException e) { throw new RuntimeException( "Non-ISO8601 date for CreationDate in list buckets output: " + elementText, e); } } } } public class BucketLoggingStatusHandler extends DefaultXmlHandler { protected StorageBucketLoggingStatus bucketLoggingStatus; /** * @return * an object representing the bucket's LoggingStatus document. */ public StorageBucketLoggingStatus getBucketLoggingStatus() { return bucketLoggingStatus; } } /** * Handler for LoggingStatus response XML documents for a bucket. * The document is parsed into an {@link S3BucketLoggingStatus} object available via the * {@link #getBucketLoggingStatus()} method. * * @author James Murty * */ public class S3BucketLoggingStatusHandler extends BucketLoggingStatusHandler { private String targetBucket = null; private String targetPrefix = null; private S3GranteeInterface currentGrantee = null; private S3Permission currentPermission = null; @Override public void startElement(String name) { if (name.equals("BucketLoggingStatus")) { bucketLoggingStatus = new S3BucketLoggingStatus(); } } @Override public void endElement(String name, String elementText) { if (name.equals("TargetBucket")) { targetBucket = elementText; } else if (name.equals("TargetPrefix")) { targetPrefix = elementText; } else if (name.equals("LoggingEnabled")) { bucketLoggingStatus.setTargetBucketName(targetBucket); bucketLoggingStatus.setLogfilePrefix(targetPrefix); } // Handle TargetGrants ACLs else if (name.equals("ID")) { currentGrantee = new S3CanonicalGrantee(); currentGrantee.setIdentifier(elementText); } else if (name.equals("EmailAddress")) { currentGrantee = new EmailAddressGrantee(); currentGrantee.setIdentifier(elementText); } else if (name.equals("URI")) { currentGrantee = new S3GroupGrantee(); currentGrantee.setIdentifier(elementText); } else if (name.equals("DisplayName")) { ((S3CanonicalGrantee) currentGrantee).setDisplayName(elementText); } else if (name.equals("Permission")) { currentPermission = S3Permission.parsePermission(elementText); } else if (name.equals("Grant")) { S3GrantAndPermission grantAndPermission = new S3GrantAndPermission(currentGrantee, currentPermission); ((S3BucketLoggingStatus) bucketLoggingStatus).addTargetGrant(grantAndPermission); } } } /** * Handler for Logging response XML documents for a bucket. * The document is parsed into an {@link GSBucketLoggingStatus} object available via the * {@link #getBucketLoggingStatus()} method. * * @author David Kocher * */ // public class GSBucketLoggingStatusHandler extends BucketLoggingStatusHandler // { // @Override // public void startElement(String name) // { // if (name.equals("Logging")) // { // bucketLoggingStatus = new GSBucketLoggingStatus(); // } // } // // @Override // public void endElement(String name, String elementText) // { // if (name.equals("LogBucket")) // { // bucketLoggingStatus.setTargetBucketName(elementText); // } // else if (name.equals("LogObjectPrefix")) // { // bucketLoggingStatus.setLogfilePrefix(elementText); // } // else if (name.equals("PredefinedAcl")) // { // if (elementText.equals(GSAccessControlList.REST_CANNED_PRIVATE.getValueForRESTHeaderACL())) // { // ((GSBucketLoggingStatus) bucketLoggingStatus) // .setPredefinedAcl(GSAccessControlList.REST_CANNED_PRIVATE); // } // else if (elementText.equals(GSAccessControlList.REST_CANNED_PUBLIC_READ.getValueForRESTHeaderACL())) // { // ((GSBucketLoggingStatus) bucketLoggingStatus) // .setPredefinedAcl(GSAccessControlList.REST_CANNED_PUBLIC_READ); // } // else if (elementText.equals(GSAccessControlList.REST_CANNED_PUBLIC_READ_WRITE // .getValueForRESTHeaderACL())) // { // ((GSBucketLoggingStatus) bucketLoggingStatus) // .setPredefinedAcl(GSAccessControlList.REST_CANNED_PUBLIC_READ_WRITE); // } // else if (elementText.equals(GSAccessControlList.REST_CANNED_AUTHENTICATED_READ // .getValueForRESTHeaderACL())) // { // ((GSBucketLoggingStatus) bucketLoggingStatus) // .setPredefinedAcl(GSAccessControlList.REST_CANNED_AUTHENTICATED_READ); // } // else if (elementText.equals(GSAccessControlList.REST_CANNED_BUCKET_OWNER_READ // .getValueForRESTHeaderACL())) // { // ((GSBucketLoggingStatus) bucketLoggingStatus) // .setPredefinedAcl(GSAccessControlList.REST_CANNED_BUCKET_OWNER_READ); // } // else if (elementText.equals(GSAccessControlList.REST_CANNED_BUCKET_OWNER_FULL_CONTROL // .getValueForRESTHeaderACL())) // { // ((GSBucketLoggingStatus) bucketLoggingStatus) // .setPredefinedAcl(GSAccessControlList.REST_CANNED_BUCKET_OWNER_FULL_CONTROL); // } // } // } // } /** * 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. * * @author James Murty * */ public class BucketLocationHandler extends DefaultXmlHandler { private String location = null; /** * @return * the bucket's location. */ public String getLocation() { return location; } @Override public void endElement(String name, String elementText) { if (name.equals("LocationConstraint")) { if (elementText.length() == 0) { location = null; } else { location = elementText; } } } } public class CopyObjectResultHandler extends DefaultXmlHandler { // Data items for successful copy private String etag = null; private Date lastModified = null; // 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; public Date getLastModified() { return lastModified; } public String getETag() { return etag; } 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; } @Override public void startElement(String name) { if (name.equals("CopyObjectResult")) { receivedErrorResponse = false; } else if (name.equals("Error")) { receivedErrorResponse = true; } } @Override public void endElement(String name, String elementText) { if (name.equals("LastModified")) { try { lastModified = ServiceUtils.parseIso8601Date(elementText); } catch (ParseException e) { throw new RuntimeException( "Non-ISO8601 date for LastModified in copy object output: " + elementText, e); } } else if (name.equals("ETag")) { etag = elementText; } else if (name.equals("Code")) { errorCode = elementText; } else if (name.equals("Message")) { errorMessage = elementText; } else if (name.equals("RequestId")) { errorRequestId = elementText; } else if (name.equals("HostId")) { errorHostId = elementText; } } } /** * Handler for RequestPaymentConfiguration response XML documents for a bucket. * The document is parsed into a boolean value: true if the bucket is configured * as Requester Pays, false if it is configured as Owner pays. This boolean value * is available via the {@link #isRequesterPays()} method. * * @author James Murty */ public class RequestPaymentConfigurationHandler extends DefaultXmlHandler { private String payer = null; /** * @return * true if the bucket is configured as Requester Pays, false if it is * configured as Owner pays. */ public boolean isRequesterPays() { return "Requester".equals(payer); } @Override public void endElement(String name, String elementText) { if (name.equals("Payer")) { payer = elementText; } } } public class VersioningConfigurationHandler extends DefaultXmlHandler { private S3BucketVersioningStatus versioningStatus = null; private String status = null; private String mfaStatus = null; public S3BucketVersioningStatus getVersioningStatus() { return this.versioningStatus; } @Override public void endElement(String name, String elementText) { if (name.equals("Status")) { this.status = elementText; } else if (name.equals("MfaDelete")) { this.mfaStatus = elementText; } else if (name.equals("VersioningConfiguration")) { if (!"Enabled".equals(status) && !"Suspended".equals(status)) { this.versioningStatus = new S3BucketVersioningStatus("Off", "Enabled".equals(mfaStatus)); } else { this.versioningStatus = new S3BucketVersioningStatus(this.status, "Enabled".equals(mfaStatus)); } } } } public class ListVersionsResultsHandler extends DefaultXmlHandler { private final List<BaseVersionOrDeleteMarker> items = new ArrayList<BaseVersionOrDeleteMarker>(); private final List<String> commonPrefixes = new ArrayList<String>(); private String key = null; private String versionId = null; private boolean isLatest = false; private Date lastModified = null; private StorageOwner owner = null; private String etag = null; private long size = 0; private String storageClass = null; private boolean insideCommonPrefixes = false; // Listing properties. private String bucketName = null; private String requestPrefix = null; private String keyMarker = null; private String versionIdMarker = null; private long requestMaxKeys = 0; private boolean listingTruncated = false; private String nextMarker = null; private String nextVersionIdMarker = null; public String getBucketName() { return this.bucketName; } /** * @return * true if the listing document was truncated, and therefore only contained a subset of the * available S3 objects. */ public boolean isListingTruncated() { return listingTruncated; } /** * @return * the S3 objects contained in the listing. */ public BaseVersionOrDeleteMarker[] getItems() { return items.toArray(new BaseVersionOrDeleteMarker[items.size()]); } public String[] getCommonPrefixes() { return commonPrefixes.toArray(new String[commonPrefixes.size()]); } public String getRequestPrefix() { return requestPrefix; } public String getKeyMarker() { return keyMarker; } public String getVersionIdMarker() { return versionIdMarker; } public String getNextKeyMarker() { return nextMarker; } public String getNextVersionIdMarker() { return nextVersionIdMarker; } public long getRequestMaxKeys() { return requestMaxKeys; } @Override public void startElement(String name) { if (name.equals("Owner")) { owner = null; } else if (name.equals("CommonPrefixes")) { insideCommonPrefixes = true; } } @Override public void endElement(String name, String elementText) { // Listing details if (name.equals("Name")) { bucketName = elementText; if (log.isDebugEnabled()) { log.debug("Examining listing for bucket: " + bucketName); } } else if (!insideCommonPrefixes && name.equals("Prefix")) { requestPrefix = elementText; } else if (name.equals("KeyMarker")) { keyMarker = elementText; } else if (name.equals("NextKeyMarker")) { nextMarker = elementText; } else if (name.equals("VersionIdMarker")) { versionIdMarker = elementText; } else if (name.equals("NextVersionIdMarker")) { nextVersionIdMarker = elementText; } else if (name.equals("MaxKeys")) { requestMaxKeys = Long.parseLong(elementText); } else if (name.equals("IsTruncated")) { String isTruncatedStr = elementText.toLowerCase(Locale.getDefault()); if (isTruncatedStr.startsWith("false")) { listingTruncated = false; } else if (isTruncatedStr.startsWith("true")) { listingTruncated = true; } else { throw new RuntimeException("Invalid value for IsTruncated field: " + isTruncatedStr); } } // Version/DeleteMarker finished. else if (name.equals("Version")) { BaseVersionOrDeleteMarker item = new S3Version(key, versionId, isLatest, lastModified, (S3Owner) owner, etag, size, storageClass); items.add(item); } else if (name.equals("DeleteMarker")) { BaseVersionOrDeleteMarker item = new S3DeleteMarker(key, versionId, isLatest, lastModified, (S3Owner) owner); items.add(item); // Version/DeleteMarker details } else if (name.equals("Key")) { key = elementText; } else if (name.equals("VersionId")) { versionId = elementText; } else if (name.equals("IsLatest")) { isLatest = "true".equals(elementText); } else if (name.equals("LastModified")) { try { lastModified = ServiceUtils.parseIso8601Date(elementText); } catch (ParseException e) { throw new RuntimeException( "Non-ISO8601 date for LastModified in bucket's versions listing output: " + elementText, e); } } else if (name.equals("ETag")) { etag = elementText; } else if (name.equals("Size")) { size = Long.parseLong(elementText); } else if (name.equals("StorageClass")) { storageClass = elementText; } // Owner details. else if (name.equals("ID")) { owner = newOwner(); owner.setId(elementText); } else if (name.equals("DisplayName")) { owner.setDisplayName(elementText); } // Common prefixes. else if (insideCommonPrefixes && name.equals("Prefix")) { commonPrefixes.add(elementText); } else if (name.equals("CommonPrefixes")) { insideCommonPrefixes = false; } } } public class OwnerHandler extends SimpleHandler { private String id; private String displayName; public OwnerHandler(XMLReader xr) { super(xr); } public StorageOwner getOwner() { StorageOwner owner = newOwner(); owner.setId(id); owner.setDisplayName(displayName); return owner; } public void endID(String text) { this.id = text; } public void endDisplayName(String text) { this.displayName = text; } public void endOwner(String text) { returnControlToParentHandler(); } // </Initiator> represents end of an owner item in ListMultipartUploadsResult/Upload public void endInitiator(String text) { returnControlToParentHandler(); } } public class MultipartUploadResultHandler extends SimpleHandler { private String uploadId; private String bucketName; private String objectKey; private String storageClass; private S3Owner owner; private S3Owner initiator; private Date initiatedDate; private boolean inInitiator = false; public MultipartUploadResultHandler(XMLReader xr) { super(xr); } public S3MultipartUpload getMultipartUpload() { if (initiatedDate != null) { // Return the contents from a ListMultipartUploadsResult response return new S3MultipartUpload(uploadId, objectKey, storageClass, initiator, owner, initiatedDate); } else { // Return the contents from an InitiateMultipartUploadsResult response return new S3MultipartUpload(uploadId, bucketName, objectKey); } } public void endUploadId(String text) { this.uploadId = text; } public void endBucket(String text) { this.bucketName = text; } public void endKey(String text) { this.objectKey = text; } public void endStorageClass(String text) { this.storageClass = text; } public void endInitiated(String text) throws ParseException { this.initiatedDate = ServiceUtils.parseIso8601Date(text); } public void startOwner() { inInitiator = false; transferControlToHandler(new OwnerHandler(xr)); } public void startInitiator() { inInitiator = true; transferControlToHandler(new OwnerHandler(xr)); } @Override public void controlReturned(SimpleHandler childHandler) { if (inInitiator) { this.owner = (S3Owner) ((OwnerHandler) childHandler).getOwner(); } else { this.initiator = (S3Owner) ((OwnerHandler) childHandler).getOwner(); } } // </Upload> represents end of a MultipartUpload item in ListMultipartUploadsResult public void endUpload(String text) { returnControlToParentHandler(); } } public class ListMultipartUploadsResultHandler extends SimpleHandler { private final List<S3MultipartUpload> uploads = new ArrayList<S3MultipartUpload>(); private final List<String> commonPrefixes = new ArrayList<String>(); private boolean insideCommonPrefixes; private String bucketName = null; private String keyMarker = null; private String uploadIdMarker = null; private String nextKeyMarker = null; private String nextUploadIdMarker = null; private int maxUploads = 1000; private boolean isTruncated = false; public ListMultipartUploadsResultHandler(XMLReader xr) { super(xr); } public List<S3MultipartUpload> getMultipartUploadList() { // Update multipart upload objects with overall bucket name for (S3MultipartUpload upload : uploads) { upload.setBucketName(bucketName); } return uploads; } public boolean isTruncated() { return isTruncated; } public String getKeyMarker() { return keyMarker; } public String getUploadIdMarker() { return uploadIdMarker; } public String getNextKeyMarker() { return nextKeyMarker; } public String getNextUploadIdMarker() { return nextUploadIdMarker; } public int getMaxUploads() { return maxUploads; } public String[] getCommonPrefixes() { return commonPrefixes.toArray(new String[commonPrefixes.size()]); } public void startUpload() { transferControlToHandler(new MultipartUploadResultHandler(xr)); } public void startCommonPrefixes() { insideCommonPrefixes = true; } @Override public void controlReturned(SimpleHandler childHandler) { uploads.add(((MultipartUploadResultHandler) childHandler).getMultipartUpload()); } public void endBucket(String text) { this.bucketName = text; } public void endKeyMarker(String text) { this.keyMarker = text; } public void endUploadIdMarker(String text) { this.uploadIdMarker = text; } public void endNextKeyMarker(String text) { this.nextKeyMarker = text; } public void endNextUploadIdMarker(String text) { this.nextUploadIdMarker = text; } public void endMaxUploads(String text) { this.maxUploads = Integer.parseInt(text); } public void endIsTruncated(String text) { this.isTruncated = "true".equalsIgnoreCase(text); } public void endPrefix(String text) { if (insideCommonPrefixes) { commonPrefixes.add(text); } } public void endCommonPrefixes() { insideCommonPrefixes = false; } } public class MultipartPartResultHandler extends SimpleHandler { private Integer partNumber = -1; // CopyPartResult doesn't include part number, use clearly invalid default private Date lastModified; private String etag; private Long size = -1l; // CopyPartResult doesn't include size, use clearly invalid default public MultipartPartResultHandler(XMLReader xr) { super(xr); } public MultipartPart getMultipartPart() { return new MultipartPart(partNumber, lastModified, etag, size); } public void endPartNumber(String text) { this.partNumber = Integer.parseInt(text); } public void endLastModified(String text) throws ParseException { this.lastModified = ServiceUtils.parseIso8601Date(text); } public void endETag(String text) { this.etag = text; } public void endSize(String text) { this.size = Long.parseLong(text); } // </Part> represents end of a Part item in ListPartsResultHandler/Part public void endPart(String text) { returnControlToParentHandler(); } } public class ListMultipartPartsResultHandler extends SimpleHandler { private final List<MultipartPart> parts = new ArrayList<MultipartPart>(); private String bucketName = null; private String objectKey = null; private String uploadId = null; private S3Owner initiator = null; private S3Owner owner = null; private String storageClass = null; private String partNumberMarker = null; private String nextPartNumberMarker = null; private int maxParts = 1000; private boolean isTruncated = false; private boolean inInitiator = false; public ListMultipartPartsResultHandler(XMLReader xr) { super(xr); } public List<MultipartPart> getMultipartPartList() { return parts; } public boolean isTruncated() { return isTruncated; } public String getBucketName() { return bucketName; } public String getObjectKey() { return objectKey; } public String getUploadId() { return uploadId; } public S3Owner getInitiator() { return initiator; } public S3Owner getOwner() { return owner; } public String getStorageClass() { return storageClass; } public String getPartNumberMarker() { return partNumberMarker; } public String getNextPartNumberMarker() { return nextPartNumberMarker; } public int getMaxParts() { return maxParts; } public void startPart() { transferControlToHandler(new MultipartPartResultHandler(xr)); } @Override public void controlReturned(SimpleHandler childHandler) { if (childHandler instanceof MultipartPartResultHandler) { parts.add(((MultipartPartResultHandler) childHandler).getMultipartPart()); } else { if (inInitiator) { initiator = (S3Owner) ((OwnerHandler) childHandler).getOwner(); } else { owner = (S3Owner) ((OwnerHandler) childHandler).getOwner(); } } } public void startInitiator() { inInitiator = true; transferControlToHandler(new OwnerHandler(xr)); } public void startOwner() { inInitiator = false; transferControlToHandler(new OwnerHandler(xr)); } public void endBucket(String text) { this.bucketName = text; } public void endKey(String text) { this.objectKey = text; } public void endStorageClass(String text) { this.storageClass = text; } public void endUploadId(String text) { this.uploadId = text; } public void endPartNumberMarker(String text) { this.partNumberMarker = text; } public void endNextPartNumberMarker(String text) { this.nextPartNumberMarker = text; } public void endMaxParts(String text) { this.maxParts = Integer.parseInt(text); } public void endIsTruncated(String text) { this.isTruncated = "true".equalsIgnoreCase(text); } } public class CompleteMultipartUploadResultHandler extends SimpleHandler { private String location; private String bucketName; private String objectKey; private String etag; private ServiceException serviceException = null; public CompleteMultipartUploadResultHandler(XMLReader xr) { super(xr); } public MultipartCompleted getMultipartCompleted() { return new MultipartCompleted(location, bucketName, objectKey, etag); } public ServiceException getServiceException() { return serviceException; } public void endLocation(String text) { this.location = text; } public void endBucket(String text) { this.bucketName = text; } public void endKey(String text) { this.objectKey = text; } public void endETag(String text) { this.etag = text; } public void startError() { transferControlToHandler(new CompleteMultipartUploadErrorHandler(xr)); } @Override public void controlReturned(SimpleHandler childHandler) { this.serviceException = ((CompleteMultipartUploadErrorHandler) childHandler).getServiceException(); } } public class CompleteMultipartUploadErrorHandler extends SimpleHandler { private String code = null; private String message = null; private String etag = null; private Long minSizeAllowed = null; private Long proposedSize = null; private String hostId = null; private Integer partNumber = null; private String requestId = null; public CompleteMultipartUploadErrorHandler(XMLReader xr) { super(xr); } public ServiceException getServiceException() { String fullMessage = message + ": PartNumber=" + partNumber + ", MinSizeAllowed=" + minSizeAllowed + ", ProposedSize=" + proposedSize + ", ETag=" + etag; ServiceException e = new ServiceException(fullMessage); e.setErrorCode(code); e.setErrorMessage(message); e.setErrorHostId(hostId); e.setErrorRequestId(requestId); return e; } public void endCode(String text) { this.code = text; } public void endMessage(String text) { this.message = text; } public void endETag(String text) { this.etag = text; } public void endMinSizeAllowed(String text) { this.minSizeAllowed = Long.parseLong(text); } public void endProposedSize(String text) { this.proposedSize = Long.parseLong(text); } public void endHostId(String text) { this.hostId = text; } public void endPartNumber(String text) { this.partNumber = Integer.parseInt(text); } public void endRequestId(String text) { this.requestId = text; } public void endError(String text) { returnControlToParentHandler(); } } public class WebsiteConfigurationHandler extends DefaultXmlHandler { private S3WebsiteConfiguration config = new S3WebsiteConfiguration(); private RedirectRule currentRedirectRule = null; private RoutingRule currentRoutingRule = null; private RoutingRuleCondition currentCondition = null; private String indexDocumentSuffix = null; private String errorDocumentKey = null; public S3WebsiteConfiguration getWebsiteConfig() { return config; } @Override public void startElement(String name) { if (name.equals("RedirectAllRequestsTo")) { currentRedirectRule = new RedirectRule(); this.config.setRedirectAllRequestsTo(currentRedirectRule); } if ("RoutingRules".equals(name)) { List<RoutingRule> routingRules = new LinkedList<RoutingRule>(); this.config.setRoutingRules(routingRules); } if (name.equals("RoutingRule")) { currentRoutingRule = new RoutingRule(); this.config.getRoutingRules().add(currentRoutingRule); } if (name.equals("Condition")) { currentCondition = new RoutingRuleCondition(); currentRoutingRule.setCondition(currentCondition); } else if (name.equals("Redirect")) { currentRedirectRule = new RedirectRule(); currentRoutingRule.setRedirect(currentRedirectRule); } } @Override public void endElement(String name, String elementText) { if (name.equals("Suffix")) { config.setIndexDocumentSuffix(elementText); } if (name.equals("Key")) { config.setErrorDocumentKey(elementText); } if (name.equals("KeyPrefixEquals")) { currentCondition.setKeyPrefixEquals(elementText); } else if (name.equals("HttpErrorCodeReturnedEquals")) { currentCondition.setHttpErrorCodeReturnedEquals(elementText); } if (name.equals("Protocol")) { currentRedirectRule.setProtocol(elementText); } else if (name.equals("HostName")) { currentRedirectRule.setHostName(elementText); } else if (name.equals("ReplaceKeyPrefixWith")) { currentRedirectRule.setReplaceKeyPrefixWith(elementText); } else if (name.equals("ReplaceKeyWith")) { currentRedirectRule.setReplaceKeyWith(elementText); } else if (name.equals("HttpRedirectCode")) { currentRedirectRule.setHttpRedirectCode(elementText); } } } public class NotificationConfigurationHandler extends DefaultXmlHandler { private NotificationConfig config = new NotificationConfig(); private String lastTopic = null; private String lastEvent = null; public NotificationConfig getNotificationConfig() { return config; } @Override public void endElement(String name, String elementText) { if (name.equals("Topic")) { this.lastTopic = elementText; } else if (name.equals("Event")) { this.lastEvent = elementText; config.addTopicConfig(config.new TopicConfig(this.lastTopic, this.lastEvent)); } else if (name.equals("NotificationConfiguration")) { } } } public class MultipleDeleteResultHandler extends DefaultXmlHandler { private MultipleDeleteResult result = new MultipleDeleteResult(); private List<MultipleDeleteResult.DeletedObjectResult> deletedObjectResults = new ArrayList<MultipleDeleteResult.DeletedObjectResult>(); private List<MultipleDeleteResult.ErrorResult> errorResults = new ArrayList<MultipleDeleteResult.ErrorResult>(); private boolean inDeleted, inError; private String key, version, deleteMarkerVersion, errorCode, message; private Boolean withDeleteMarker; public MultipleDeleteResult getMultipleDeleteResult() { return result; } @Override public void startElement(String name) { if ("Deleted".equals(name)) { inDeleted = true; } else if ("Error".equals(name)) { inError = true; } } @Override public void endElement(String name, String elementText) { if ("Key".equals(name)) { key = elementText; } else if ("VersionId".equals(name)) { version = elementText; } else if ("DeleteMarker".equals(name)) { withDeleteMarker = Boolean.valueOf(elementText); } else if ("DeleteMarkerVersionId".equals(name)) { deleteMarkerVersion = elementText; } else if ("Code".equals(name)) { errorCode = elementText; } else if ("Message".equals(name)) { message = elementText; } else if ("Deleted".equals(name)) { deletedObjectResults .add(result.new DeletedObjectResult(key, version, withDeleteMarker, deleteMarkerVersion)); inDeleted = false; key = version = deleteMarkerVersion = errorCode = message = null; withDeleteMarker = null; } else if ("Error".equals(name)) { errorResults.add(result.new ErrorResult(key, version, errorCode, message)); inError = false; key = version = deleteMarkerVersion = errorCode = message = null; withDeleteMarker = null; } else if (name.equals("DeleteResult")) { result.setDeletedObjectResults(deletedObjectResults); result.setErrorResults(errorResults); } } } public class LifecycleConfigurationHandler extends SimpleHandler { private S3LifecycleConfiguration config = new S3LifecycleConfiguration(); private Rule latestRule = null; private TimeEvent latestTimeEvent = null; public LifecycleConfigurationHandler(XMLReader xr) { super(xr); } public S3LifecycleConfiguration getLifecycleConfig() { return config; } // Transition/Expiration section public void startTransition() { latestTimeEvent = config.new Transition(); latestRule.setTransition(((Transition) latestTimeEvent)); } public void startExpiration() { latestTimeEvent = config.new Expiration(); latestRule.setExpiration(((Expiration) latestTimeEvent)); } public void endDate(String text) throws ParseException { this.latestTimeEvent.setDate(ServiceUtils.parseIso8601Date(text)); } public void endDays(String text) { this.latestTimeEvent.setDays(Integer.parseInt(text)); } public void endStorageClass(String text) { ((Transition) this.latestTimeEvent).setStorageClass(text); } // Rule section public void startRule() { latestRule = config.new Rule(); } public void endID(String text) { latestRule.setId(text); } public void endPrefix(String text) { latestRule.setPrefix(text); } public void endStatus(String text) { latestRule.setEnabled(text.equals("Enabled")); } public void endRule(String text) { config.addRule(latestRule); } } }