}
}
public void testObjectManagement() throws Exception {
String bucketName = createBucketForTest("testObjectManagement").getName();
RestStorageService service = getStorageService(getCredentials());
try {
StorageObject object = new StorageObject("TestObject");
try {
service.putObject((String) null, null);
fail("Cannot create an object without a valid bucket");
} catch (ServiceException e) {
}
try {
service.putObject((String) null, object);
fail("Cannot create an object without a valid bucket");
} catch (ServiceException e) {
}
try {
service.putObject(bucketName, new StorageObject());
fail("Cannot create an object without a valid object");
} catch (ServiceException e) {
}
// Create basic object with no content type (use the default) and no data.
StorageObject basicObject = service.putObject(bucketName, object);
// Ensure Content-Type is set to binary by default
// TODO: Google Storage bug: Content type returned on initial PUT is always "text/html"
if (!TARGET_SERVICE_GS.equals(getTargetService())) {
assertTrue("Unexpected default content type",
Mimetypes.MIMETYPE_OCTET_STREAM.equals(basicObject.getContentType()));
}
// Re-retrieve object to ensure it was correctly created.
basicObject = service.getObject(bucketName, object.getKey());
assertEquals("Unexpected content type",
Mimetypes.MIMETYPE_OCTET_STREAM, basicObject.getContentType());
assertEquals("Unexpected size for 'empty' object", 0, basicObject.getContentLength());
basicObject.closeDataInputStream();
// Make sure bucket cannot be removed while it has contents.
try {
service.deleteBucket(bucketName);
fail("Should not be able to delete a bucket containing objects");
} catch (ServiceException e) {
}
// Update/overwrite object with real data content and some metadata.
String contentType = "text/plain";
String objectData = "Just some rubbish text to include as data";
String dataMd5HashAsHex = ServiceUtils.toHex(
ServiceUtils.computeMD5Hash(objectData.getBytes()));
HashMap<String, Object> metadata = new HashMap<String, Object>();
metadata.put("creator", "testObjectManagement");
metadata.put("purpose", "For testing purposes");
object.replaceAllMetadata(metadata);
object.setContentType(contentType);
object.setDataInputStream(new ByteArrayInputStream(objectData.getBytes()));
StorageObject dataObject = service.putObject(bucketName, object);
// TODO: Google Storage bug: Content type returned on initial PUT is always "text/html"
if (TARGET_SERVICE_GS.equals(getTargetService())) {
dataObject = service.getObject(bucketName, object.getKey());
}
assertEquals("Unexpected content type", contentType, dataObject.getContentType());
assertEquals("Mismatching MD5 hex hash", dataMd5HashAsHex, dataObject.getETag());
// Retrieve data object to ensure it was correctly created, the server-side hash matches
// what we expect, and we get our metadata back.
dataObject = service.getObject(bucketName, object.getKey());
assertEquals("Unexpected default content type", "text/plain", dataObject.getContentType());
// TODO: Google Storage doesn't reliably return Content-Length in a GET
if (!TARGET_SERVICE_GS.equals(getTargetService())) {
assertEquals("Unexpected content-length for object",
objectData.length(), dataObject.getContentLength());
}
assertEquals("Mismatching hash", dataMd5HashAsHex, dataObject.getETag());
// Check user's data are available in basic metadata map
assertEquals("Missing creator metadata", "testObjectManagement",
dataObject.getMetadata("creator"));
assertEquals("Missing purpose metadata", "For testing purposes",
dataObject.getMetadata("purpose"));
// Check data are available in user metadata map
assertEquals("Missing creator user metadata",
"testObjectManagement", dataObject.getUserMetadataMap().get("creator"));
assertEquals("Missing purpose user metadata",
"For testing purposes", dataObject.getUserMetadataMap().get("purpose"));
assertNotNull("Expected data input stream to be available", dataObject.getDataInputStream());
// Check data are available in service metadata map
assertNotNull(dataObject.getServiceMetadataMap().get("request-id"));
// Ensure we can get the data from S3.
StringBuffer sb = new StringBuffer();
int b = -1;
while ((b = dataObject.getDataInputStream().read()) != -1) {
sb.append((char) b);
}
dataObject.closeDataInputStream();
assertEquals("Mismatching data", objectData, sb.toString());
// Retrieve only HEAD of data object (all metadata is available, but not the object content
// data input stream)
dataObject = service.getObjectDetails(bucketName, object.getKey());
assertEquals("Unexpected default content type", "text/plain", dataObject.getContentType());
assertEquals("Mismatching hash", dataMd5HashAsHex, dataObject.getETag());
assertEquals("Missing creator metadata", "testObjectManagement",
dataObject.getMetadata("creator"));
assertEquals("Missing purpose metadata", "For testing purposes",
dataObject.getMetadata("purpose"));
assertNull("Expected data input stream to be unavailable", dataObject.getDataInputStream());
assertEquals("Unexpected size for object", objectData.length(), dataObject.getContentLength());
// Test object GET constraints.
Calendar objectCreationTimeCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.US);
objectCreationTimeCal.setTime(dataObject.getLastModifiedDate());
Calendar yesterday = (Calendar) objectCreationTimeCal.clone();
yesterday.add(Calendar.DAY_OF_YEAR, -1);
Calendar tomorrow = (Calendar) objectCreationTimeCal.clone();
tomorrow.add(Calendar.DAY_OF_YEAR, +2);
// Precondition: Modified since yesterday
service.getObjectDetails(bucketName, object.getKey(), yesterday, null, null, null);
// Precondition: Mot modified since after creation date.
try {
service.getObjectDetails(bucketName, object.getKey(), objectCreationTimeCal, null, null, null);
fail("Cannot have been modified since object was created");
} catch (ServiceException e) { }
// Precondition: Not modified since yesterday
try {
service.getObjectDetails(bucketName, object.getKey(), null, yesterday, null, null);
fail("Cannot be unmodified since yesterday");
} catch (ServiceException e) { }
// Precondition: Not modified since tomorrow
service.getObjectDetails(bucketName, object.getKey(), null, tomorrow, null, null);
// Precondition: matches correct hash
service.getObjectDetails(bucketName, object.getKey(), null, null, new String[] {dataMd5HashAsHex}, null);
// Precondition: doesn't match incorrect hash
try {
service.getObjectDetails(bucketName, object.getKey(), null, null,
new String[] {"__" + dataMd5HashAsHex.substring(2)}, null);
fail("Hash values should not match");
} catch (ServiceException e) {
}
// Precondition: doesn't match correct hash
try {
service.getObjectDetails(bucketName, object.getKey(), null, null, null, new String[] {dataMd5HashAsHex});
fail("Hash values should mis-match");
} catch (ServiceException e) {
}
// Precondition: doesn't match incorrect hash
service.getObjectDetails(bucketName, object.getKey(), null, null, null,
new String[] {"__" + dataMd5HashAsHex.substring(2)});
// Retrieve only a limited byte-range of the data, with a start and end.
Long byteRangeStart = new Long(3);
Long byteRangeEnd = new Long(12);
dataObject = service.getObject(bucketName, object.getKey(), null, null, null, null, byteRangeStart, byteRangeEnd);
String dataReceived = ServiceUtils.readInputStreamToString(
dataObject.getDataInputStream(), Constants.DEFAULT_ENCODING);
String dataExpected = objectData.substring(byteRangeStart.intValue(), byteRangeEnd.intValue() + 1);
assertEquals("Mismatching data from range precondition", dataExpected, dataReceived);
// Retrieve only a limited byte-range of the data, with a start range only.
byteRangeStart = new Long(7);
byteRangeEnd = null;
dataObject = service.getObject(bucketName, object.getKey(), null, null, null, null, byteRangeStart, byteRangeEnd);
dataReceived = ServiceUtils.readInputStreamToString(
dataObject.getDataInputStream(), Constants.DEFAULT_ENCODING);
dataExpected = objectData.substring(byteRangeStart.intValue());
assertEquals("Mismatching data from range precondition", dataExpected, dataReceived);
// Retrieve only a limited byte-range of the data, with an end range only.
byteRangeStart = null;
byteRangeEnd = new Long(13);
dataObject = service.getObject(bucketName, object.getKey(), null, null, null, null, byteRangeStart, byteRangeEnd);
dataReceived = ServiceUtils.readInputStreamToString(
dataObject.getDataInputStream(), Constants.DEFAULT_ENCODING);
dataExpected = objectData.substring(objectData.length() - byteRangeEnd.intValue());
assertEquals("Mismatching data from range precondition", dataExpected, dataReceived);
// Clean-up.
service.deleteObject(bucketName, object.getKey());
// Create object with tricky key.
String trickyKey = "http://example.site.com/some/path/document name.html?param1=a@b#c$d¶m2=(089)";
StorageObject trickyObject = service.putObject(bucketName,
new StorageObject(trickyKey, "Some test data"));
assertEquals("Tricky key name mistmatch", trickyKey, trickyObject.getKey());
// Make sure the tricky named object really exists with its full name.
StorageObject[] objects = service.listObjects(bucketName);
boolean trickyNamedObjectExists = false;
for (int i = 0; !trickyNamedObjectExists && i < objects.length; i++) {
if (trickyKey.equals(objects[i].getKey())) {
trickyNamedObjectExists = true;
}
}
assertTrue("Tricky key name object does not exist with its full name", trickyNamedObjectExists);
// Delete object with tricky key.
service.deleteObject(bucketName, trickyObject.getKey());
} finally {
cleanupBucketForTest("testObjectManagement");
}
}