From 4ad59a45d7816f3114474c77ac978aa89927ed4d Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 25 Jul 2012 16:46:58 -0700 Subject: [PATCH 01/11] issure #77, let get blob properties throws an exception when the if modified since condition is not met. --- .../blob/implementation/BlobRestProxy.java | 2 +- .../blob/BlobServiceIntegrationTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java index 00aea0a3efbd..ec291f845b31 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java @@ -546,7 +546,7 @@ public GetBlobPropertiesResult getBlobProperties(String container, String blob, builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.method("HEAD", ClientResponse.class); - ThrowIfError(response); + ThrowIfNotSuccess(response); return getBlobPropertiesResultFromResponse(response); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java index 76146812ed98..a72111d7853b 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java @@ -1259,6 +1259,24 @@ public void getBlobPropertiesWorks() throws Exception { assertEquals(0, props.getSequenceNumber()); } + @Test(expected = ServiceException.class) + public void getBlobPropertiesIfNotModified() throws Exception { + // Arrange + Configuration config = createConfiguration(); + BlobContract service = BlobService.create(config); + Date currentLastModifiedDate = new Date(); + + // Act + String container = TEST_CONTAINER_FOR_BLOBS; + String blob = "test"; + service.createPageBlob(container, blob, 4096); + GetBlobPropertiesResult result = service.getBlobProperties(container, blob, new GetBlobPropertiesOptions() + .setAccessCondition(AccessCondition.ifModifiedSince(currentLastModifiedDate))); + + // Assert + assertTrue(false); + } + @Test public void getBlobMetadataWorks() throws Exception { // Arrange From 8f8c6e3d168b43539bb5a503263961b4d46b3c87 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 25 Jul 2012 17:51:32 -0700 Subject: [PATCH 02/11] Support range MD5 for get Blob request. --- .../blob/implementation/BlobRestProxy.java | 3 +++ .../blob/BlobServiceIntegrationTest.java | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java index ec291f845b31..93d219bb03ea 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java @@ -651,6 +651,9 @@ public GetBlobResult getBlob(String container, String blob, GetBlobOptions optio builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); builder = addOptionalRangeHeader(builder, options.getRangeStart(), options.getRangeEnd()); builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + if (options.isComputeRangeMD5()) { + builder = addOptionalHeader(builder, "x-ms-range-get-content-md5", "true"); + } ClientResponse response = builder.get(ClientResponse.class); ThrowIfNotSuccess(response); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java index a72111d7853b..062bd8cf4e00 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java @@ -1226,6 +1226,32 @@ public void getBlobWithIfNotModifiedSinceAccessConditionWorks() throws Exception // Assert } + @Test + public void getBlobWithMD5Range() throws Exception { + // Arrange + Configuration config = createConfiguration(); + BlobContract service = BlobService.create(config); + String expectedMd5 = "+zxkkqBt6HehE3r5suhS1w=="; + + // Act + String container = TEST_CONTAINER_FOR_BLOBS; + String blob = "test"; + service.createPageBlob(container, blob, 4096); + + GetBlobOptions options = new GetBlobOptions(); + options = options.setRangeStart(50L); + options = options.setRangeEnd(200L); + options = options.setComputeRangeMD5(true); + GetBlobResult getBlobResult = service.getBlob(container, blob, options); + + // Assert + assertNotNull(getBlobResult); + BlobProperties blobProperties = getBlobResult.getProperties(); + String actualMd5 = blobProperties.getContentMD5(); + assertEquals(expectedMd5, actualMd5); + + } + @Test public void getBlobPropertiesWorks() throws Exception { // Arrange From 1b7622568ea99dcccc62e3a703d93660c7e0a767 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Thu, 26 Jul 2012 20:04:50 -0700 Subject: [PATCH 03/11] Return Etag for create*Blob and copyBlob --- .../services/blob/BlobContract.java | 19 +++-- .../BlobExceptionProcessor.java | 31 +++---- .../blob/implementation/BlobRestProxy.java | 52 ++++++++---- .../services/blob/models/CopyBlobResult.java | 80 +++++++++++++++++++ .../blob/models/CreateBlobResult.java | 80 +++++++++++++++++++ .../blob/BlobServiceIntegrationTest.java | 54 +++++++++++++ 6 files changed, 280 insertions(+), 36 deletions(-) create mode 100644 microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java create mode 100644 microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CreateBlobResult.java diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/BlobContract.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/BlobContract.java index b3dbc962f0f5..367d3b311d93 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/BlobContract.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/BlobContract.java @@ -24,10 +24,12 @@ import com.microsoft.windowsazure.services.blob.models.CommitBlobBlocksOptions; import com.microsoft.windowsazure.services.blob.models.ContainerACL; import com.microsoft.windowsazure.services.blob.models.CopyBlobOptions; +import com.microsoft.windowsazure.services.blob.models.CopyBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobBlockOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesResult; +import com.microsoft.windowsazure.services.blob.models.CreateBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotResult; import com.microsoft.windowsazure.services.blob.models.CreateContainerOptions; @@ -429,7 +431,7 @@ void setContainerMetadata(String container, HashMap metadata, Se * @throws ServiceException * if an error occurs accessing the storage service. */ - void createPageBlob(String container, String blob, long length) throws ServiceException; + CreateBlobResult createPageBlob(String container, String blob, long length) throws ServiceException; /** * Creates a page blob of the specified maximum length, using the specified options. @@ -457,7 +459,8 @@ void setContainerMetadata(String container, HashMap metadata, Se * @throws ServiceException * if an error occurs accessing the storage service. */ - void createPageBlob(String container, String blob, long length, CreateBlobOptions options) throws ServiceException; + CreateBlobResult createPageBlob(String container, String blob, long length, CreateBlobOptions options) + throws ServiceException; /** * Creates a block blob from a content stream. @@ -473,7 +476,7 @@ void setContainerMetadata(String container, HashMap metadata, Se * @throws ServiceException * if an error occurs accessing the storage service. */ - void createBlockBlob(String container, String blob, InputStream contentStream) throws ServiceException; + CreateBlobResult createBlockBlob(String container, String blob, InputStream contentStream) throws ServiceException; /** * Creates a block blob from a content stream, using the specified options. @@ -495,7 +498,7 @@ void setContainerMetadata(String container, HashMap metadata, Se * @throws ServiceException * if an error occurs accessing the storage service. */ - void createBlockBlob(String container, String blob, InputStream contentStream, CreateBlobOptions options) + CreateBlobResult createBlockBlob(String container, String blob, InputStream contentStream, CreateBlobOptions options) throws ServiceException; /** @@ -1214,8 +1217,8 @@ CreateBlobSnapshotResult createBlobSnapshot(String container, String blob, Creat * @throws ServiceException * if an error occurs accessing the storage service. */ - void copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, String sourceBlob) - throws ServiceException; + CopyBlobResult copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, + String sourceBlob) throws ServiceException; /** * Copies a source blob to a destination within the storage account, using the specified options. @@ -1293,8 +1296,8 @@ void copyBlob(String destinationContainer, String destinationBlob, String source * @throws ServiceException * if an error occurs accessing the storage service. */ - void copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, String sourceBlob, - CopyBlobOptions options) throws ServiceException; + CopyBlobResult copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, + String sourceBlob, CopyBlobOptions options) throws ServiceException; /** * Gets a new lease on a blob. diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobExceptionProcessor.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobExceptionProcessor.java index fea8798af109..157dbd9737cb 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobExceptionProcessor.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobExceptionProcessor.java @@ -30,10 +30,12 @@ import com.microsoft.windowsazure.services.blob.models.CommitBlobBlocksOptions; import com.microsoft.windowsazure.services.blob.models.ContainerACL; import com.microsoft.windowsazure.services.blob.models.CopyBlobOptions; +import com.microsoft.windowsazure.services.blob.models.CopyBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobBlockOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesResult; +import com.microsoft.windowsazure.services.blob.models.CreateBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotResult; import com.microsoft.windowsazure.services.blob.models.CreateContainerOptions; @@ -383,9 +385,9 @@ public ListBlobsResult listBlobs(String container, ListBlobsOptions options) thr } @Override - public void createPageBlob(String container, String blob, long length) throws ServiceException { + public CreateBlobResult createPageBlob(String container, String blob, long length) throws ServiceException { try { - service.createPageBlob(container, blob, length); + return service.createPageBlob(container, blob, length); } catch (UniformInterfaceException e) { throw processCatch(new ServiceException(e)); @@ -396,10 +398,10 @@ public void createPageBlob(String container, String blob, long length) throws Se } @Override - public void createPageBlob(String container, String blob, long length, CreateBlobOptions options) + public CreateBlobResult createPageBlob(String container, String blob, long length, CreateBlobOptions options) throws ServiceException { try { - service.createPageBlob(container, blob, length, options); + return service.createPageBlob(container, blob, length, options); } catch (UniformInterfaceException e) { throw processCatch(new ServiceException(e)); @@ -410,9 +412,10 @@ public void createPageBlob(String container, String blob, long length, CreateBlo } @Override - public void createBlockBlob(String container, String blob, InputStream contentStream) throws ServiceException { + public CreateBlobResult createBlockBlob(String container, String blob, InputStream contentStream) + throws ServiceException { try { - service.createBlockBlob(container, blob, contentStream); + return service.createBlockBlob(container, blob, contentStream); } catch (UniformInterfaceException e) { throw processCatch(new ServiceException(e)); @@ -423,10 +426,10 @@ public void createBlockBlob(String container, String blob, InputStream contentSt } @Override - public void createBlockBlob(String container, String blob, InputStream contentStream, CreateBlobOptions options) - throws ServiceException { + public CreateBlobResult createBlockBlob(String container, String blob, InputStream contentStream, + CreateBlobOptions options) throws ServiceException { try { - service.createBlockBlob(container, blob, contentStream, options); + return service.createBlockBlob(container, blob, contentStream, options); } catch (UniformInterfaceException e) { throw processCatch(new ServiceException(e)); @@ -776,10 +779,10 @@ public CreateBlobSnapshotResult createBlobSnapshot(String container, String blob } @Override - public void copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, String sourceBlob) - throws ServiceException { + public CopyBlobResult copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, + String sourceBlob) throws ServiceException { try { - service.copyBlob(destinationContainer, destinationBlob, sourceContainer, sourceBlob); + return service.copyBlob(destinationContainer, destinationBlob, sourceContainer, sourceBlob); } catch (UniformInterfaceException e) { throw processCatch(new ServiceException(e)); @@ -790,10 +793,10 @@ public void copyBlob(String destinationContainer, String destinationBlob, String } @Override - public void copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, + public CopyBlobResult copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, String sourceBlob, CopyBlobOptions options) throws ServiceException { try { - service.copyBlob(destinationContainer, destinationBlob, sourceContainer, sourceBlob, options); + return service.copyBlob(destinationContainer, destinationBlob, sourceContainer, sourceBlob, options); } catch (UniformInterfaceException e) { throw processCatch(new ServiceException(e)); diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java index 00aea0a3efbd..695ff56b40df 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java @@ -34,10 +34,12 @@ import com.microsoft.windowsazure.services.blob.models.ContainerACL; import com.microsoft.windowsazure.services.blob.models.ContainerACL.PublicAccessType; import com.microsoft.windowsazure.services.blob.models.CopyBlobOptions; +import com.microsoft.windowsazure.services.blob.models.CopyBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobBlockOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesResult; +import com.microsoft.windowsazure.services.blob.models.CreateBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotResult; import com.microsoft.windowsazure.services.blob.models.CreateContainerOptions; @@ -488,12 +490,12 @@ public ListBlobsResult listBlobs(String container, ListBlobsOptions options) thr } @Override - public void createPageBlob(String container, String blob, long length) throws ServiceException { - createPageBlob(container, blob, length, new CreateBlobOptions()); + public CreateBlobResult createPageBlob(String container, String blob, long length) throws ServiceException { + return createPageBlob(container, blob, length, new CreateBlobOptions()); } @Override - public void createPageBlob(String container, String blob, long length, CreateBlobOptions options) + public CreateBlobResult createPageBlob(String container, String blob, long length, CreateBlobOptions options) throws ServiceException { String path = createPathFromContainer(container); WebResource webResource = getResource(options).path(path).path(blob); @@ -505,17 +507,25 @@ public void createPageBlob(String container, String blob, long length, CreateBlo builder = addOptionalHeader(builder, "x-ms-blob-sequence-number", options.getSequenceNumber()); builder = addPutBlobHeaders(options, builder); - builder.put(); + ClientResponse clientResponse = builder.put(ClientResponse.class); + ThrowIfError(clientResponse); + + CreateBlobResult createBlobResult = new CreateBlobResult(); + createBlobResult.setEtag(clientResponse.getHeaders().getFirst("ETag")); + createBlobResult.setLastModified(dateMapper.parse(clientResponse.getHeaders().getFirst("Last-Modified"))); + + return createBlobResult; } @Override - public void createBlockBlob(String container, String blob, InputStream contentStream) throws ServiceException { - createBlockBlob(container, blob, contentStream, new CreateBlobOptions()); + public CreateBlobResult createBlockBlob(String container, String blob, InputStream contentStream) + throws ServiceException { + return createBlockBlob(container, blob, contentStream, new CreateBlobOptions()); } @Override - public void createBlockBlob(String container, String blob, InputStream contentStream, CreateBlobOptions options) - throws ServiceException { + public CreateBlobResult createBlockBlob(String container, String blob, InputStream contentStream, + CreateBlobOptions options) throws ServiceException { String path = createPathFromContainer(container); System.out.println(path); WebResource webResource = getResource(options).path(path).path(blob); @@ -526,7 +536,14 @@ public void createBlockBlob(String container, String blob, InputStream contentSt builder = addPutBlobHeaders(options, builder); Object contentObject = (contentStream == null ? new byte[0] : contentStream); - builder.put(contentObject); + ClientResponse clientResponse = builder.put(ClientResponse.class, contentObject); + ThrowIfError(clientResponse); + + CreateBlobResult createBlobResult = new CreateBlobResult(); + createBlobResult.setEtag(clientResponse.getHeaders().getFirst("ETag")); + createBlobResult.setLastModified(dateMapper.parse(clientResponse.getHeaders().getFirst("Last-Modified"))); + + return createBlobResult; } @Override @@ -713,13 +730,13 @@ public CreateBlobSnapshotResult createBlobSnapshot(String container, String blob } @Override - public void copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, String sourceBlob) - throws ServiceException { - copyBlob(destinationContainer, destinationBlob, sourceContainer, sourceBlob, new CopyBlobOptions()); + public CopyBlobResult copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, + String sourceBlob) throws ServiceException { + return copyBlob(destinationContainer, destinationBlob, sourceContainer, sourceBlob, new CopyBlobOptions()); } @Override - public void copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, + public CopyBlobResult copyBlob(String destinationContainer, String destinationBlob, String sourceContainer, String sourceBlob, CopyBlobOptions options) { String path = createPathFromContainer(destinationContainer); WebResource webResource = getResource(options).path(path).path(destinationBlob); @@ -733,7 +750,14 @@ public void copyBlob(String destinationContainer, String destinationBlob, String builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); builder = addOptionalSourceAccessContitionHeader(builder, options.getSourceAccessCondition()); - builder.put(); + ClientResponse clientResponse = builder.put(ClientResponse.class); + ThrowIfError(clientResponse); + + CopyBlobResult copyBlobResult = new CopyBlobResult(); + copyBlobResult.setEtag(clientResponse.getHeaders().getFirst("ETag")); + copyBlobResult.setLastModified(dateMapper.parse(clientResponse.getHeaders().getFirst("Last-Modified"))); + + return copyBlobResult; } @Override diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java new file mode 100644 index 000000000000..7ab55ece6f01 --- /dev/null +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java @@ -0,0 +1,80 @@ +/** + * Copyright 2011 Microsoft Corporation + * + * 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 com.microsoft.windowsazure.services.blob.models; + +import java.util.Date; + +import com.microsoft.windowsazure.services.blob.BlobContract; + +/** + * A wrapper class for the response returned from a Blob Service REST API Copy Blob operation. This is returned by calls + * to implementations of {@link BlobContract#copyBlob(String, String, String, String, CopyBlobOptions)}. + *

+ * See the Copy Blob documentation on + * MSDN for details of the underlying Blob Service REST API operation. + */ +public class CopyBlobResult { + private String etag; + private Date lastModified; + + /** + * Gets the ETag of the blob. + * + * @return + * A {@link String} containing the server-assigned ETag value for the snapshot. + */ + public String getEtag() { + return etag; + } + + /** + * Sets the ETag of the blob from the ETag header returned in the + * response. + *

+ * This method is invoked by the API to set the value from the Blob Service REST API operation response returned by + * the server. + * + * @param etag + * A {@link String} containing the server-assigned ETag value for the blob. + */ + public void setEtag(String etag) { + this.etag = etag; + } + + /** + * Gets the last modified time of the blob. + *

+ * + * @return + * A {@link java.util.Date} containing the last modified time of the blob. + */ + public Date getLastModified() { + return lastModified; + } + + /** + * Sets the last modified time of the blob from the Last-Modified header + * returned in the response. + *

+ * This method is invoked by the API to set the value from the Blob Service REST API operation response returned by + * the server. + * + * @param lastModified + * A {@link java.util.Date} containing the last modified time of the snapshot. + */ + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } +} diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CreateBlobResult.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CreateBlobResult.java new file mode 100644 index 000000000000..4ab963e26900 --- /dev/null +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CreateBlobResult.java @@ -0,0 +1,80 @@ +/** + * Copyright 2011 Microsoft Corporation + * + * 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 com.microsoft.windowsazure.services.blob.models; + +import java.util.Date; + +import com.microsoft.windowsazure.services.blob.BlobContract; + +/** + * A wrapper class for the response returned from a Blob Service REST API Create Blob operation. This is returned by + * calls to implementations of {@link BlobContract#createPageBlob(String, String, long, CreateBlobOptions)} and + * {@link BlobContract#createBlockBlob(String, String, long, CreateBlobOptions)}. + *

+ * See the Put Blob documentation on + * MSDN for details of the underlying Blob Service REST API operation. + */ +public class CreateBlobResult { + private String etag; + private Date lastModified; + + /** + * Gets the ETag of the blob. + * + * @return + * A {@link String} containing the server-assigned ETag value for the snapshot. + */ + public String getEtag() { + return etag; + } + + /** + * Sets the ETag of the snapshot from the ETag header returned in the + * response. + *

+ * This method is invoked by the API to set the value from the Blob Service REST API operation response returned by + * the server. + * + * @param etag + * A {@link String} containing the server-assigned ETag value for the blob. + */ + public void setEtag(String etag) { + this.etag = etag; + } + + /** + * Gets the last modified time of the snapshot. + * + * @return + * A {@link java.util.Date} containing the last modified time of the blob. + */ + public Date getLastModified() { + return lastModified; + } + + /** + * Reserved for internal use. Sets the last modified time of the snapshot from the Last-Modified header + * returned in the response. + *

+ * This method is invoked by the API to set the value from the Blob Service REST API operation response returned by + * the server. + * + * @param lastModified + * A {@link java.util.Date} containing the last modified time of the snapshot. + */ + public void setLastModified(Date lastModified) { + this.lastModified = lastModified; + } +} diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java index 76146812ed98..862c6aa3c654 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/BlobServiceIntegrationTest.java @@ -44,8 +44,10 @@ import com.microsoft.windowsazure.services.blob.models.BlockList; import com.microsoft.windowsazure.services.blob.models.ContainerACL; import com.microsoft.windowsazure.services.blob.models.ContainerACL.PublicAccessType; +import com.microsoft.windowsazure.services.blob.models.CopyBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobPagesResult; +import com.microsoft.windowsazure.services.blob.models.CreateBlobResult; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotOptions; import com.microsoft.windowsazure.services.blob.models.CreateBlobSnapshotResult; import com.microsoft.windowsazure.services.blob.models.CreateContainerOptions; @@ -660,6 +662,20 @@ public void createPageBlobWorks() throws Exception { // Assert } + @Test + public void createPageBlobWithETagSuccess() throws Exception { + // Arrange + Configuration config = createConfiguration(); + BlobContract service = BlobService.create(config); + + // Act + CreateBlobResult createBlobResult = service.createPageBlob(TEST_CONTAINER_FOR_BLOBS, "test", 512); + + // Assert + assertNotNull(createBlobResult); + assertNotNull(createBlobResult.getEtag()); + } + @Test public void createPageBlobWithOptionsWorks() throws Exception { // Arrange @@ -971,6 +987,21 @@ public void createBlockBlobWorks() throws Exception { // Assert } + @Test + public void createBlockBlobWithValidEtag() throws Exception { + // Arrange + Configuration config = createConfiguration(); + BlobContract service = BlobService.create(config); + + // Act + CreateBlobResult createBlobResult = service.createBlockBlob(TEST_CONTAINER_FOR_BLOBS, "test2", + new ByteArrayInputStream("some content".getBytes())); + + // Assert + assertNotNull(createBlobResult); + assertNotNull(createBlobResult.getEtag()); + } + @Test public void createBlockBlobWithOptionsWorks() throws Exception { // Arrange @@ -1427,6 +1458,29 @@ public void copyBlobWorks() throws Exception { assertEquals(content, inputStreamToString(result.getContentStream(), "UTF-8")); } + @Test + public void copyBlobGetEtagSuccess() throws Exception { + // Arrange + Configuration config = createConfiguration(); + BlobContract service = BlobService.create(config); + String sourceBlobName = "copyblobgetetagsuccesssource"; + String targetBlobName = "copyblobgetetagsuccesstarget"; + + //Act + String content = "some content2"; + service.createBlockBlob(TEST_CONTAINER_FOR_BLOBS, sourceBlobName, + new ByteArrayInputStream(content.getBytes("UTF-8"))); + CopyBlobResult copyBlobResult = service.copyBlob(TEST_CONTAINER_FOR_BLOBS_2, targetBlobName, + TEST_CONTAINER_FOR_BLOBS, sourceBlobName); + + GetBlobResult result = service.getBlob(TEST_CONTAINER_FOR_BLOBS_2, targetBlobName); + + // Assert + assertNotNull(result); + assertEquals(copyBlobResult.getEtag(), result.getProperties().getEtag()); + + } + @Test public void acquireLeaseWorks() throws Exception { // Arrange From 26ee763254cb1813d71b69dcd47f7529743622ad Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Sun, 29 Jul 2012 11:36:55 -0700 Subject: [PATCH 04/11] add breaks in PipelineHelper/addOptionalSourceAccessContitionHeader --- .../services/core/utils/pipeline/PipelineHelpers.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java index 21b95538f6e1..19fcac1f3c6d 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java @@ -96,12 +96,16 @@ public static Builder addOptionalSourceAccessContitionHeader(Builder builder, Ac switch (accessCondition.getHeader()) { case IF_MATCH: headerName = "x-ms-source-if-match"; + break; case IF_UNMODIFIED_SINCE: headerName = "x-ms-source-if-unmodified-since"; + break; case IF_MODIFIED_SINCE: headerName = "x-ms-source-if-modified-since"; + break; case IF_NONE_MATCH: headerName = "x-ms-source-if-none-match"; + break; default: headerName = ""; } From 3c9be11340084fe2acb5b6370f2da7fa2fba2c87 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Sun, 29 Jul 2012 11:40:32 -0700 Subject: [PATCH 05/11] add PipelineHelpersTest unit test. --- .../utils/pipeline/PipelineHelpersTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java new file mode 100644 index 000000000000..cce21d51fbc9 --- /dev/null +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java @@ -0,0 +1,42 @@ +/** + * Copyright 2011 Microsoft Corporation + * + * 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 com.microsoft.windowsazure.services.core.utils.pipeline; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.microsoft.windowsazure.services.blob.models.AccessCondition; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.WebResource.Builder; + +public class PipelineHelpersTest { + @Test + public void addOptionalSourceAccessContitionHeaderSuccess() throws Exception { + // Arrange + Client client = Client.create(); + WebResource webResource = client.resource("http://www.microsoft.com"); + Builder builder = webResource.header("ms-version", "1.0"); + AccessCondition accessCondition = AccessCondition.ifMatch("thisIsAETag"); + + // Act + Builder resultBuilder = PipelineHelpers.addOptionalSourceAccessContitionHeader(builder, accessCondition); + + // Assert + assertNotNull(resultBuilder); + + } +} From a118bfb4d040eb80591eeaebbdb945028b71322a Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Sun, 29 Jul 2012 18:49:58 -0700 Subject: [PATCH 06/11] encode special characters when serializing entity to XML --- .../implementation/AtomReaderWriter.java | 20 +++++++++- .../table/TableServiceIntegrationTest.java | 38 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java index ddff3f9aa889..9bbabddbffe7 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java @@ -76,7 +76,8 @@ public void write(XMLStreamWriter writer) throws XMLStreamException { writer.writeAttribute("m:type", edmType); } - String value = edmValueConverter.serialize(edmType, entry.getValue().getValue()); + String value = encodeNumericCharacterReference(edmValueConverter.serialize(edmType, entry + .getValue().getValue())); if (value != null) { writer.writeCharacters(value); } @@ -327,4 +328,21 @@ private void expect(XMLStreamReader xmlr, int eventType, String localName) throw xmlr.require(eventType, null, localName); nextSignificant(xmlr); } + + private String encodeNumericCharacterReference(String value) { + if (value == null) { + return null; + } + else { + char[] charArray = value.toCharArray(); + StringBuffer stringBuffer = new StringBuffer(); + for (int index = 0; index < charArray.length; index++) { + if (charArray[index] < 0x20 || charArray[index] > 0x7f) + stringBuffer.append("&#").append(Integer.toHexString(charArray[index])).append(";"); + else + stringBuffer.append(charArray[index]); + } + return stringBuffer.toString(); + } + } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java index 07b25300bc20..d63acd2cfcc2 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java @@ -348,6 +348,44 @@ public void insertEntityWorks() throws Exception { assertEquals(uuid.toString(), result.getEntity().getProperty("test7").getValue().toString()); } + @Test + public void insertEntityEscapeCharactersWorks() throws Exception { + // Arrange + Configuration config = createConfiguration(); + TableContract service = TableService.create(config); + //Entity entity = new Entity().setPartitionKey("001").setRowKey("insertEntityEscapeCharactersWorks") + // .setProperty("test", EdmType.STRING, "t1").setProperty("test2", EdmType.STRING, "t2") + // .setProperty("test3", EdmType.STRING, "t3"); + Entity entity = new Entity().setPartitionKey("001").setRowKey("insertEntityEscapeCharactersWorks") + .setProperty("test", EdmType.STRING, "\u0005").setProperty("test2", EdmType.STRING, "\u0011") + .setProperty("test3", EdmType.STRING, "\u0025"); + + // Act + InsertEntityResult result = service.insertEntity(TEST_TABLE_2, entity); + + // Assert + assertNotNull(result); + assertNotNull(result.getEntity()); + + assertEquals("001", result.getEntity().getPartitionKey()); + assertEquals("insertEntityEscapeCharactersWorks", result.getEntity().getRowKey()); + assertNotNull(result.getEntity().getTimestamp()); + assertNotNull(result.getEntity().getEtag()); + + assertNotNull(result.getEntity().getProperty("test")); + String actualTest1 = (String) result.getEntity().getProperty("test").getValue(); + assertEquals("", actualTest1); + + assertNotNull(result.getEntity().getProperty("test2")); + String actualTest2 = (String) result.getEntity().getProperty("test2").getValue(); + assertEquals(" ", actualTest2); + + assertNotNull(result.getEntity().getProperty("test3")); + String actualTest3 = (String) result.getEntity().getProperty("test3").getValue(); + assertEquals("%", actualTest3); + + } + @Test public void updateEntityWorks() throws Exception { System.out.println("updateEntityWorks()"); From b18bd32e82a8b9a51f033c3e49bec4535c432401 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 30 Jul 2012 14:52:37 -0700 Subject: [PATCH 07/11] address code review feedback for returning ETag for create*Blob and copyBlob. --- .../windowsazure/services/blob/models/CopyBlobResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java index 7ab55ece6f01..d8dd1b665847 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/models/CopyBlobResult.java @@ -33,7 +33,7 @@ public class CopyBlobResult { * Gets the ETag of the blob. * * @return - * A {@link String} containing the server-assigned ETag value for the snapshot. + * A {@link String} containing the server-assigned ETag value for the copy blob. */ public String getEtag() { return etag; @@ -72,7 +72,7 @@ public Date getLastModified() { * the server. * * @param lastModified - * A {@link java.util.Date} containing the last modified time of the snapshot. + * A {@link java.util.Date} containing the last modified time of the blob. */ public void setLastModified(Date lastModified) { this.lastModified = lastModified; From 5966f3b27aa2715db1a508a07609309a3b91852b Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 30 Jul 2012 15:57:10 -0700 Subject: [PATCH 08/11] Address code review feedback to add breaks in PipelineHelper/addOptionalSourceAccessConditionHeader --- .../blob/implementation/BlobRestProxy.java | 40 +++++++++---------- .../core/utils/pipeline/PipelineHelpers.java | 4 +- .../implementation/ServiceBusRestProxy.java | 1 - .../utils/pipeline/PipelineHelpersTest.java | 4 +- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java index ec291f845b31..acfd07282a96 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/implementation/BlobRestProxy.java @@ -146,12 +146,12 @@ private Builder addOptionalRangeHeader(Builder builder, Long rangeStart, Long ra return PipelineHelpers.addOptionalRangeHeader(builder, rangeStart, rangeEnd); } - private Builder addOptionalAccessContitionHeader(Builder builder, AccessCondition accessCondition) { - return PipelineHelpers.addOptionalAccessContitionHeader(builder, accessCondition); + private Builder addOptionalAccessConditionHeader(Builder builder, AccessCondition accessCondition) { + return PipelineHelpers.addOptionalAccessConditionHeader(builder, accessCondition); } - private Builder addOptionalSourceAccessContitionHeader(Builder builder, AccessCondition accessCondition) { - return PipelineHelpers.addOptionalSourceAccessContitionHeader(builder, accessCondition); + private Builder addOptionalSourceAccessConditionHeader(Builder builder, AccessCondition accessCondition) { + return PipelineHelpers.addOptionalSourceAccessConditionHeader(builder, accessCondition); } private HashMap getMetadataFromHeaders(ClientResponse response) { @@ -191,7 +191,7 @@ private Builder addPutBlobHeaders(CreateBlobOptions options, Builder builder) { builder = addOptionalHeader(builder, "x-ms-blob-cache-control", options.getBlobCacheControl()); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); builder = addOptionalMetadataHeader(builder, options.getMetadata()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); return builder; } @@ -322,7 +322,7 @@ public void deleteContainer(String container, DeleteContainerOptions options) th WebResource webResource = getResource(options).path(container).queryParam("resType", "container"); WebResource.Builder builder = webResource.header("x-ms-version", API_VERSION); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); builder.delete(); } @@ -443,7 +443,7 @@ public void setContainerMetadata(String container, HashMap metad WebResource.Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalMetadataHeader(builder, metadata); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); builder.put(); } @@ -543,7 +543,7 @@ public GetBlobPropertiesResult getBlobProperties(String container, String blob, Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.method("HEAD", ClientResponse.class); ThrowIfNotSuccess(response); @@ -565,7 +565,7 @@ public GetBlobMetadataResult getBlobMetadata(String container, String blob, GetB Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.get(ClientResponse.class); ThrowIfError(response); @@ -594,7 +594,7 @@ public SetBlobPropertiesResult setBlobProperties(String container, String blob, builder = addOptionalHeader(builder, "x-ms-sequence-number-action", options.getSequenceNumberAction()); builder = addOptionalHeader(builder, "x-ms-blob-sequence-number", options.getSequenceNumber()); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.put(ClientResponse.class); ThrowIfError(response); @@ -625,7 +625,7 @@ public SetBlobMetadataResult setBlobMetadata(String container, String blob, Hash WebResource.Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); builder = addOptionalMetadataHeader(builder, metadata); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.put(ClientResponse.class); ThrowIfError(response); @@ -650,7 +650,7 @@ public GetBlobResult getBlob(String container, String blob, GetBlobOptions optio Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); builder = addOptionalRangeHeader(builder, options.getRangeStart(), options.getRangeEnd()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.get(ClientResponse.class); ThrowIfNotSuccess(response); @@ -680,7 +680,7 @@ public void deleteBlob(String container, String blob, DeleteBlobOptions options) builder = addOptionalHeader(builder, "x-ms-delete-snapshots", options.getDeleteSnaphotsOnly() ? "only" : "include"); } - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); builder.delete(); } @@ -699,7 +699,7 @@ public CreateBlobSnapshotResult createBlobSnapshot(String container, String blob Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); builder = addOptionalMetadataHeader(builder, options.getMetadata()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.put(ClientResponse.class); ThrowIfError(response); @@ -730,8 +730,8 @@ public void copyBlob(String destinationContainer, String destinationBlob, String builder = addOptionalHeader(builder, "x-ms-copy-source", getCopyBlobSourceName(sourceContainer, sourceBlob, options)); builder = addOptionalMetadataHeader(builder, options.getMetadata()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); - builder = addOptionalSourceAccessContitionHeader(builder, options.getSourceAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); + builder = addOptionalSourceAccessConditionHeader(builder, options.getSourceAccessCondition()); builder.put(); } @@ -788,7 +788,7 @@ private AcquireLeaseResult putLeaseImpl(String leaseAction, String container, St Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalHeader(builder, "x-ms-lease-id", leaseId); builder = addOptionalHeader(builder, "x-ms-lease-action", leaseAction); - builder = addOptionalAccessContitionHeader(builder, accessCondition); + builder = addOptionalAccessConditionHeader(builder, accessCondition); // Note: Add content type here to enable proper HMAC signing ClientResponse response = builder.put(ClientResponse.class); @@ -834,7 +834,7 @@ private CreateBlobPagesResult updatePageBlobPagesImpl(String action, String cont builder = addOptionalHeader(builder, "Content-MD5", options.getContentMD5()); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); builder = addOptionalHeader(builder, "x-ms-page-write", action); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.put(ClientResponse.class, contentStream); ThrowIfError(response); @@ -862,7 +862,7 @@ public ListBlobRegionsResult listBlobRegions(String container, String blob, List Builder builder = webResource.header("x-ms-version", API_VERSION); builder = addOptionalRangeHeader(builder, options.getRangeStart(), options.getRangeEnd()); builder = addOptionalHeader(builder, "x-ms-lease-id", options.getLeaseId()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); ClientResponse response = builder.get(ClientResponse.class); ThrowIfError(response); @@ -914,7 +914,7 @@ public void commitBlobBlocks(String container, String blob, BlockList blockList, builder = addOptionalHeader(builder, "x-ms-blob-content-language", options.getBlobContentLanguage()); builder = addOptionalHeader(builder, "x-ms-blob-content-md5", options.getBlobContentMD5()); builder = addOptionalMetadataHeader(builder, options.getMetadata()); - builder = addOptionalAccessContitionHeader(builder, options.getAccessCondition()); + builder = addOptionalAccessConditionHeader(builder, options.getAccessCondition()); builder.put(blockList); } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java index 19fcac1f3c6d..7f117707aafc 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java @@ -80,7 +80,7 @@ public static Builder addOptionalRangeHeader(Builder builder, Long rangeStart, L return builder; } - public static Builder addOptionalAccessContitionHeader(Builder builder, AccessCondition accessCondition) { + public static Builder addOptionalAccessConditionHeader(Builder builder, AccessCondition accessCondition) { if (accessCondition != null) { if (accessCondition.getHeader() != AccessConditionHeaderType.NONE) { builder = addOptionalHeader(builder, accessCondition.getHeader().toString(), accessCondition.getValue()); @@ -89,7 +89,7 @@ public static Builder addOptionalAccessContitionHeader(Builder builder, AccessCo return builder; } - public static Builder addOptionalSourceAccessContitionHeader(Builder builder, AccessCondition accessCondition) { + public static Builder addOptionalSourceAccessConditionHeader(Builder builder, AccessCondition accessCondition) { if (accessCondition != null) { if (accessCondition.getHeader() != AccessConditionHeaderType.NONE) { String headerName; diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java index c654e49c6fae..7fc710b76d00 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java @@ -22,7 +22,6 @@ import javax.inject.Inject; import javax.inject.Named; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import org.apache.commons.logging.Log; diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java index cce21d51fbc9..670b3e7ae0a2 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpersTest.java @@ -25,7 +25,7 @@ public class PipelineHelpersTest { @Test - public void addOptionalSourceAccessContitionHeaderSuccess() throws Exception { + public void addOptionalSourceAccessConditionHeaderSuccess() throws Exception { // Arrange Client client = Client.create(); WebResource webResource = client.resource("http://www.microsoft.com"); @@ -33,7 +33,7 @@ public void addOptionalSourceAccessContitionHeaderSuccess() throws Exception { AccessCondition accessCondition = AccessCondition.ifMatch("thisIsAETag"); // Act - Builder resultBuilder = PipelineHelpers.addOptionalSourceAccessContitionHeader(builder, accessCondition); + Builder resultBuilder = PipelineHelpers.addOptionalSourceAccessConditionHeader(builder, accessCondition); // Assert assertNotNull(resultBuilder); From 54ff07606b2b90b9f325d1de5e45af1fd3a6d682 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 1 Aug 2012 10:35:16 -0700 Subject: [PATCH 09/11] Internationalize the service bus for the dev-private branch. --- .../serviceBus/ServiceBusConfiguration.java | 39 ++++++++++--------- .../ServiceBusConfigurationTest.java | 22 ++++++----- .../serviceBus/ServiceBusCreationTest.java | 22 ++++++----- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java index 64c58d8d2a13..f2a2b33d4619 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java @@ -2,15 +2,15 @@ * Copyright 2011 Microsoft Corporation * * 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 + * 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. + * 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 com.microsoft.windowsazure.services.serviceBus; @@ -63,8 +63,10 @@ public class ServiceBusConfiguration { * ServiceBusService class. * */ - public static Configuration configureWithWrapAuthentication(String namespace, String authenticationName, String authenticationPassword) { - return configureWithWrapAuthentication(null, Configuration.getInstance(), namespace, authenticationName, authenticationPassword); + public static Configuration configureWithWrapAuthentication(String namespace, String authenticationName, + String authenticationPassword, String serviceBusRootUri, String serviceBusWrapUri) { + return configureWithWrapAuthentication(null, Configuration.getInstance(), namespace, authenticationName, + authenticationPassword, serviceBusRootUri, serviceBusWrapUri); } /** @@ -87,9 +89,10 @@ public static Configuration configureWithWrapAuthentication(String namespace, St * ServiceBusService class. * */ - public static Configuration configureWithWrapAuthentication(Configuration configuration, String namespace, String authenticationName, - String authenticationPassword) { - return configureWithWrapAuthentication(null, configuration, namespace, authenticationName, authenticationPassword); + public static Configuration configureWithWrapAuthentication(Configuration configuration, String namespace, + String authenticationName, String authenticationPassword, String serviceBusRootUri, String wrapRootUri) { + return configureWithWrapAuthentication(null, configuration, namespace, authenticationName, + authenticationPassword, serviceBusRootUri, wrapRootUri); } /** @@ -115,8 +118,9 @@ public static Configuration configureWithWrapAuthentication(Configuration config * ServiceBusService class. * */ - public static Configuration configureWithWrapAuthentication(String profile, Configuration configuration, String namespace, - String authenticationName, String authenticationPassword) { + public static Configuration configureWithWrapAuthentication(String profile, Configuration configuration, + String namespace, String authenticationName, String authenticationPassword, String serviceBusRootUri, + String wrapRootUri) { if (profile == null) { profile = ""; @@ -125,10 +129,9 @@ else if (profile.length() != 0 && !profile.endsWith(".")) { profile = profile + "."; } - configuration.setProperty(profile + URI, "https://" + namespace + ".servicebus.windows.net/"); + configuration.setProperty(profile + URI, "https://" + namespace + serviceBusRootUri); - configuration - .setProperty(profile + WRAP_URI, "https://" + namespace + "-sb.accesscontrol.windows.net/WRAPv0.9"); + configuration.setProperty(profile + WRAP_URI, "https://" + namespace + wrapRootUri); configuration.setProperty(profile + WRAP_NAME, authenticationName); configuration.setProperty(profile + WRAP_PASSWORD, authenticationPassword); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java index f9262cd86874..5e65f3d22955 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java @@ -2,15 +2,15 @@ * Copyright 2011 Microsoft Corporation * * 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 + * 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. + * 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 com.microsoft.windowsazure.services.serviceBus; @@ -27,7 +27,8 @@ public void ConfigureSetsExpectedProperties() { Configuration config = new Configuration(); // Act - ServiceBusConfiguration.configureWithWrapAuthentication(config, "alpha", "beta", "gamma"); + ServiceBusConfiguration.configureWithWrapAuthentication(config, "alpha", "beta", "gamma", + ".servicebus.windows.net", "-sb.accesscontrol.windows.net/WRAPv0.9"); // Assert assertEquals("https://alpha.servicebus.windows.net/", config.getProperty("serviceBus.uri")); @@ -42,7 +43,8 @@ public void UsingProfileAddsPrefix() { Configuration config = new Configuration(); // Act - ServiceBusConfiguration.configureWithWrapAuthentication("backup", config, "alpha", "beta", "gamma"); + ServiceBusConfiguration.configureWithWrapAuthentication("backup", config, "alpha", "beta", "gamma", + ".servicebus.windows.net", "-sb.accesscontrol.windows.net/WRAPv0.9"); // Assert assertEquals("https://alpha.servicebus.windows.net/", config.getProperty("backup.serviceBus.uri")); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusCreationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusCreationTest.java index aba5eace5bf6..ca6c0ebed64e 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusCreationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusCreationTest.java @@ -2,15 +2,15 @@ * Copyright 2011 Microsoft Corporation * * 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 + * 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. + * 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 com.microsoft.windowsazure.services.serviceBus; @@ -24,7 +24,8 @@ public class ServiceBusCreationTest { @Test public void theServiceClassMayBeCreatedDirectlyViaSingletonConfig() throws Exception { - ServiceBusConfiguration.configureWithWrapAuthentication("my-namespace", "my-identity", "my-shared-secret"); + ServiceBusConfiguration.configureWithWrapAuthentication("my-namespace", "my-identity", "my-shared-secret", + ".servicebus.windows.net", "-sb.accesscontrol.windows.net/WRAPv0.9"); ServiceBusContract service = ServiceBusService.create(); assertNotNull(service); @@ -33,7 +34,8 @@ public void theServiceClassMayBeCreatedDirectlyViaSingletonConfig() throws Excep public Configuration newConfiguration() { Configuration config = new Configuration(); - ServiceBusConfiguration.configureWithWrapAuthentication(config, "my-namespace", "my-identity", "my-shared-secret"); + ServiceBusConfiguration.configureWithWrapAuthentication(config, "my-namespace", "my-identity", + "my-shared-secret", ".servicebus.windows.net", "-sb.accesscontrol.windows.net/WRAPv0.9"); return config; } From 333e2f334f4286125f07c8efd3d08a9c26a2ef03 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 1 Aug 2012 15:29:28 -0700 Subject: [PATCH 10/11] address code review feedback for service bus internationalization. --- .../services/serviceBus/ServiceBusConfiguration.java | 4 ++-- .../services/serviceBus/ServiceBusConfigurationTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java index f2a2b33d4619..24592204de19 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java @@ -64,9 +64,9 @@ public class ServiceBusConfiguration { * */ public static Configuration configureWithWrapAuthentication(String namespace, String authenticationName, - String authenticationPassword, String serviceBusRootUri, String serviceBusWrapUri) { + String authenticationPassword, String serviceBusRootUri, String wrapRootUri) { return configureWithWrapAuthentication(null, Configuration.getInstance(), namespace, authenticationName, - authenticationPassword, serviceBusRootUri, serviceBusWrapUri); + authenticationPassword, serviceBusRootUri, wrapRootUri); } /** diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java index 5e65f3d22955..70c3f97963a0 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java @@ -28,7 +28,7 @@ public void ConfigureSetsExpectedProperties() { // Act ServiceBusConfiguration.configureWithWrapAuthentication(config, "alpha", "beta", "gamma", - ".servicebus.windows.net", "-sb.accesscontrol.windows.net/WRAPv0.9"); + ".servicebus.windows.net/", "-sb.accesscontrol.windows.net/WRAPv0.9"); // Assert assertEquals("https://alpha.servicebus.windows.net/", config.getProperty("serviceBus.uri")); @@ -44,7 +44,7 @@ public void UsingProfileAddsPrefix() { // Act ServiceBusConfiguration.configureWithWrapAuthentication("backup", config, "alpha", "beta", "gamma", - ".servicebus.windows.net", "-sb.accesscontrol.windows.net/WRAPv0.9"); + ".servicebus.windows.net/", "-sb.accesscontrol.windows.net/WRAPv0.9"); // Assert assertEquals("https://alpha.servicebus.windows.net/", config.getProperty("backup.serviceBus.uri")); From 4a759ebec39da880b211fcecb74dc728f3342a7f Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 1 Aug 2012 23:26:35 -0700 Subject: [PATCH 11/11] address code review feedback regarding table encoding. --- .../implementation/AtomReaderWriter.java | 10 +++++-- .../table/TableServiceIntegrationTest.java | 28 +++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java index 9bbabddbffe7..469121547830 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/implementation/AtomReaderWriter.java @@ -76,8 +76,12 @@ public void write(XMLStreamWriter writer) throws XMLStreamException { writer.writeAttribute("m:type", edmType); } - String value = encodeNumericCharacterReference(edmValueConverter.serialize(edmType, entry - .getValue().getValue())); + String value = edmValueConverter.serialize(edmType, entry.getValue().getValue()); + + if ((edmType != null) && (edmType == "Edm.String")) { + value = encodeNumericCharacterReference(value); + } + if (value != null) { writer.writeCharacters(value); } @@ -338,7 +342,7 @@ private String encodeNumericCharacterReference(String value) { StringBuffer stringBuffer = new StringBuffer(); for (int index = 0; index < charArray.length; index++) { if (charArray[index] < 0x20 || charArray[index] > 0x7f) - stringBuffer.append("&#").append(Integer.toHexString(charArray[index])).append(";"); + stringBuffer.append("&#x").append(Integer.toHexString(charArray[index])).append(";"); else stringBuffer.append(charArray[index]); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java index d63acd2cfcc2..4651f71acf0d 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/TableServiceIntegrationTest.java @@ -353,12 +353,12 @@ public void insertEntityEscapeCharactersWorks() throws Exception { // Arrange Configuration config = createConfiguration(); TableContract service = TableService.create(config); - //Entity entity = new Entity().setPartitionKey("001").setRowKey("insertEntityEscapeCharactersWorks") - // .setProperty("test", EdmType.STRING, "t1").setProperty("test2", EdmType.STRING, "t2") - // .setProperty("test3", EdmType.STRING, "t3"); + Entity entity = new Entity().setPartitionKey("001").setRowKey("insertEntityEscapeCharactersWorks") .setProperty("test", EdmType.STRING, "\u0005").setProperty("test2", EdmType.STRING, "\u0011") - .setProperty("test3", EdmType.STRING, "\u0025"); + .setProperty("test3", EdmType.STRING, "\u0025").setProperty("test4", EdmType.STRING, "\uaaaa") + .setProperty("test5", EdmType.STRING, "\ub2e2").setProperty("test6", EdmType.STRING, " \ub2e2") + .setProperty("test7", EdmType.STRING, "ok \ub2e2"); // Act InsertEntityResult result = service.insertEntity(TEST_TABLE_2, entity); @@ -374,16 +374,32 @@ public void insertEntityEscapeCharactersWorks() throws Exception { assertNotNull(result.getEntity().getProperty("test")); String actualTest1 = (String) result.getEntity().getProperty("test").getValue(); - assertEquals("", actualTest1); + assertEquals("", actualTest1); assertNotNull(result.getEntity().getProperty("test2")); String actualTest2 = (String) result.getEntity().getProperty("test2").getValue(); - assertEquals(" ", actualTest2); + assertEquals("", actualTest2); assertNotNull(result.getEntity().getProperty("test3")); String actualTest3 = (String) result.getEntity().getProperty("test3").getValue(); assertEquals("%", actualTest3); + assertNotNull(result.getEntity().getProperty("test4")); + String actualTest4 = (String) result.getEntity().getProperty("test4").getValue(); + assertEquals("ꪪ", actualTest4); + + assertNotNull(result.getEntity().getProperty("test5")); + String actualTest5 = (String) result.getEntity().getProperty("test5").getValue(); + assertEquals("닢", actualTest5); + + assertNotNull(result.getEntity().getProperty("test6")); + String actualTest6 = (String) result.getEntity().getProperty("test6").getValue(); + assertEquals(" 닢", actualTest6); + + assertNotNull(result.getEntity().getProperty("test7")); + String actualTest7 = (String) result.getEntity().getProperty("test7").getValue(); + assertEquals("ok 닢", actualTest7); + } @Test