Skip to content

Commit

Permalink
Merge pull request Azure#74 from jofriedm-msft/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
jofriedm-msft committed Jul 11, 2017
2 parents 5cd406b + aff76b9 commit b0e4667
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 51 deletions.
4 changes: 4 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
2017.XX.XX Version X.X.X
* Added ErrorReceivingResponseEvent which fires when a network error occurs before the responseReceivedEvent fires. If the responseReceivedEvent fires sucessfully, this new event will not fire.

2017.06.21 Version 5.3.1
* Fixed a bug in specific upload case for block blobs. This only affects uploads greater than the max put blob threshold, that have increased the streamWriteSizeInBytes beyond the 4 MB and storeBlobContentMD5 has been disabled.
* In some cases in the above mentioned upload path, fixed a bug where StorageExceptions were being thrown instead of IOExceptions.

2017.06.13 Version 5.3.0
* Fixed a bug where the transactional MD5 check would fail when downloading a range of blob or file and the recovery action is performed on a subsection of the range.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
*/
package com.microsoft.azure.storage;

import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.*;
import com.microsoft.azure.storage.core.SR;
import com.microsoft.azure.storage.TestRunners.CloudTests;
import com.microsoft.azure.storage.TestRunners.DevFabricTests;
import com.microsoft.azure.storage.TestRunners.DevStoreTests;

import org.apache.http.protocol.HTTP;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.URISyntaxException;
import java.util.ArrayList;

Expand Down Expand Up @@ -111,6 +113,22 @@ public void eventOccurred(ResponseReceivedEvent eventArg) {
}
});

eventContext.getErrorReceivingResponseEventHandler().addListener(new StorageEvent<ErrorReceivingResponseEvent>() {

@Override
public void eventOccurred(ErrorReceivingResponseEvent eventArg) {
fail("This event should not trigger");
}
});

OperationContext.getGlobalErrorReceivingResponseEventHandler().addListener(new StorageEvent<ErrorReceivingResponseEvent>() {

@Override
public void eventOccurred(ErrorReceivingResponseEvent eventArg) {
fail("This event should not trigger");
}
});

assertEquals(0, callList.size());
assertEquals(0, globalCallList.size());

Expand Down Expand Up @@ -138,6 +156,85 @@ public void eventOccurred(ResponseReceivedEvent eventArg) {
assertEquals(2, globalCallList.size());
}

@Test
public void testErrorReceivingResponseEvent() throws URISyntaxException, StorageException {
final ArrayList<Boolean> callList = new ArrayList<Boolean>();
final ArrayList<Boolean> globalCallList = new ArrayList<Boolean>();

OperationContext eventContext = new OperationContext();
BlobRequestOptions options = new BlobRequestOptions();
options.setRetryPolicyFactory(new RetryNoRetry());

// setting the sending request event handler to trigger an exception.
// this is a retryable exception
eventContext.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() {
@Override
public void eventOccurred(SendingRequestEvent eventArg) {
HttpURLConnection connection = (HttpURLConnection) eventArg.getConnectionObject();
connection.setFixedLengthStreamingMode(0);
}
});

eventContext.getErrorReceivingResponseEventHandler().addListener(new StorageEvent<ErrorReceivingResponseEvent>() {
@Override
public void eventOccurred(ErrorReceivingResponseEvent eventArg) {
assertEquals(eventArg.getRequestResult(), eventArg.getOpContext().getLastResult());
callList.add(true);
}
});

OperationContext.getGlobalErrorReceivingResponseEventHandler().addListener(new StorageEvent<ErrorReceivingResponseEvent>() {
@Override
public void eventOccurred(ErrorReceivingResponseEvent eventArg) {
assertEquals(eventArg.getRequestResult(), eventArg.getOpContext().getLastResult());
globalCallList.add(true);
}
});

CloudBlobClient blobClient = TestHelper.createCloudBlobClient();
CloudBlobContainer container = blobClient.getContainerReference("container1");
container.createIfNotExists();

try {
CloudBlockBlob blob1 = container.getBlockBlobReference("blob1");
try {
String blockID = String.format("%08d", 1);
blob1.uploadBlock(blockID, BlobTestHelper.getRandomDataStream(10), 10, null, options, eventContext);
} catch (Exception e) { }

// make sure both the local and globab context update
assertEquals(1, callList.size());
assertEquals(1, globalCallList.size());

// make sure only global updates by replacing the local with a no-op event
eventContext
.setErrorReceivingResponseEventHandler(new StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>>());
try {
String blockID2 = String.format("%08d", 2);
blob1.uploadBlock(blockID2, BlobTestHelper.getRandomDataStream(10), 10, null, options, eventContext);
} catch (Exception e) { }

assertEquals(1, callList.size());
assertEquals(2, globalCallList.size());

// make sure global does not update by replacing the global with a no-op
OperationContext
.setGlobalErrorReceivingResponseEventHandler(new StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>>());

// make sure neither update
try {
String blockID3 = String.format("%08d", 3);
blob1.uploadBlock(blockID3, BlobTestHelper.getRandomDataStream(10), 10, null, options, eventContext);
} catch (Exception e) { }

assertEquals(1, callList.size());
assertEquals(2, globalCallList.size());
}
finally {
container.deleteIfExists();
}
}

@Test
public void testRequestCompletedEvents() throws URISyntaxException, StorageException {
final ArrayList<Boolean> callList = new ArrayList<Boolean>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1200,4 +1200,36 @@ else if (overload == 2) {
assertNotNull(copy.properties.getCopyState().getCopyDestinationSnapshotID());
assertNotNull(copy.getCopyState().getCompletionTime());
}

@Test
public void testEightTBBlob() throws StorageException, URISyntaxException, IOException {
CloudPageBlob blob = this.container.getPageBlobReference("blob1");
CloudPageBlob blob2 = this.container.getPageBlobReference("blob1");

long eightTb = 8L * 1024L * 1024L * 1024L * 1024L;
blob.create(eightTb);
assertEquals(eightTb, blob.getProperties().getLength());

blob2.downloadAttributes();
assertEquals(eightTb, blob2.getProperties().getLength());

for (ListBlobItem listBlob : this.container.listBlobs()) {
CloudPageBlob listPageBlob = (CloudPageBlob)listBlob;
assertEquals(eightTb, listPageBlob.getProperties().getLength());
}

CloudPageBlob blob3 = this.container.getPageBlobReference("blob3");
blob3.create(1024);
blob3.resize(eightTb);

final Random randGenerator = new Random();
final byte[] buffer = new byte[1024];
randGenerator.nextBytes(buffer);
blob.uploadPages(new ByteArrayInputStream(buffer), eightTb - 512L, 512L);

ArrayList<PageRange> ranges = blob.downloadPageRanges();
assertEquals(1, ranges.size());
assertEquals(eightTb - 512L, ranges.get(0).getStartOffset());
assertEquals(eightTb - 1L, ranges.get(0).getEndOffset());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright 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.azure.storage;

/**
* Represents an event that is fired when a network error occurs before the HTTP response status and headers are received.
*/
public final class ErrorReceivingResponseEvent extends BaseEvent {

/**
* Creates an instance of the <code>BaseEvent</code> class that is fired when a network error occurs before the HTTP response status and headers are received.
*
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
* @param connectionObject
* Represents a connection object. Currently only <code>java.net.HttpURLConnection</code> is supported as
* a connection object.
* @param requestResult
* A {@link RequestResult} object that represents the current request result.
*/
public ErrorReceivingResponseEvent(OperationContext opContext, Object connectionObject, RequestResult requestResult) {
super(opContext, connectionObject, requestResult);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ public final class OperationContext {
private HashMap<String, String> userHeaders;

/**
* Represents an event that is triggered before sending a request.
* Represents an event that is triggered before sending a
* request.
*
* @see StorageEvent
* @see StorageEventMultiCaster
Expand All @@ -92,15 +93,23 @@ public final class OperationContext {
private static StorageEventMultiCaster<SendingRequestEvent, StorageEvent<SendingRequestEvent>> globalSendingRequestEventHandler = new StorageEventMultiCaster<SendingRequestEvent, StorageEvent<SendingRequestEvent>>();

/**
* Represents an event that is triggered when a response is received from the storage service while processing a
* request.
*
* Represents an event that is triggered when a response is received from the storage service while processing a request
*
* @see StorageEvent
* @see StorageEventMultiCaster
* @see ResponseReceivedEvent
*/
private static StorageEventMultiCaster<ResponseReceivedEvent, StorageEvent<ResponseReceivedEvent>> globalResponseReceivedEventHandler = new StorageEventMultiCaster<ResponseReceivedEvent, StorageEvent<ResponseReceivedEvent>>();

/**
* Represents an event that is triggered when a network error occurs before the HTTP response status and headers are received.
*
* @see StorageEvent
* @see StorageEventMultiCaster
* @see ErrorReceivingResponseEvent
*/
private static StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>> globalErrorReceivingResponseEventHandler = new StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>>();

/**
* Represents an event that is triggered when a response received from the service is fully processed.
*
Expand Down Expand Up @@ -138,6 +147,15 @@ public final class OperationContext {
*/
private StorageEventMultiCaster<ResponseReceivedEvent, StorageEvent<ResponseReceivedEvent>> responseReceivedEventHandler = new StorageEventMultiCaster<ResponseReceivedEvent, StorageEvent<ResponseReceivedEvent>>();

/**
* Represents an event that is triggered when a network error occurs before the HTTP response status and headers are received.
*
* @see StorageEvent
* @see StorageEventMultiCaster
* @see ErrorReceivingResponseEvent
*/
private StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>> errorReceivingResponseEventHandler = new StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>>();

/**
* Represents an event that is triggered when a response received from the service is fully processed.
*
Expand Down Expand Up @@ -285,6 +303,16 @@ public static StorageEventMultiCaster<ResponseReceivedEvent, StorageEvent<Respon
return OperationContext.globalResponseReceivedEventHandler;
}

/**
* Gets a global event multi-caster that is triggered when a network error occurs before the HTTP response status and headers are received.
* It allows event listeners to be dynamically added and removed.
*
* @return A {@link StorageEventMultiCaster} object for the <code>globabErrorReceivingResponseEventHandler</code>.
*/
public static StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>> getGlobalErrorReceivingResponseEventHandler() {
return OperationContext.globalErrorReceivingResponseEventHandler;
}

/**
* Gets a global event multi-caster that is triggered when a request is completed. It allows event listeners to be
* dynamically added and removed.
Expand Down Expand Up @@ -325,6 +353,16 @@ public StorageEventMultiCaster<ResponseReceivedEvent, StorageEvent<ResponseRecei
return this.responseReceivedEventHandler;
}

/**
* Gets an event multi-caster that is triggered when a network error occurs before the HTTP response status and headers are received.
* It allows event listeners to be dynamically added and removed.
*
* @return A {@link StorageEventMultiCaster} object for the <code>errorReceivingResponseEventHandler</code>.
*/
public StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>> getErrorReceivingResponseEventHandler() {
return this.errorReceivingResponseEventHandler;
}

/**
* Gets an event multi-caster that is triggered when a request is completed. It allows event listeners to be
* dynamically added and removed.
Expand Down Expand Up @@ -450,6 +488,17 @@ public static void setGlobalResponseReceivedEventHandler(
OperationContext.globalResponseReceivedEventHandler = globalResponseReceivedEventHandler;
}

/**
* Sets a global event multi-caster that is triggered when a network error occurs before the HTTP response status and headers are received.
*
* @param globalErrorReceivingResponseEventHandler
* The {@link StorageEventMultiCaster} object to set for the <code>globalErrorReceivingResponseEventHandler</code>.
*/
public static void setGlobalErrorReceivingResponseEventHandler(
final StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>> globalErrorReceivingResponseEventHandler) {
OperationContext.globalErrorReceivingResponseEventHandler = globalErrorReceivingResponseEventHandler;
}

/**
* Sets a global event multi-caster that is triggered when a request is completed.
*
Expand Down Expand Up @@ -494,6 +543,17 @@ public void setResponseReceivedEventHandler(
this.responseReceivedEventHandler = responseReceivedEventHandler;
}

/**
* Sets an event multi-caster that is triggered when a network error occurs before the HTTP response status and headers are received.
*
* @param errorReceivingResponseEventHandler
* The {@link StorageEventMultiCaster} object to set for the <code>errorReceivingResponseEventHandler</code>.
*/
public void setErrorReceivingResponseEventHandler(
final StorageEventMultiCaster<ErrorReceivingResponseEvent, StorageEvent<ErrorReceivingResponseEvent>> errorReceivingResponseEventHandler) {
this.errorReceivingResponseEventHandler = errorReceivingResponseEventHandler;
}

/**
* Sets an event multi-caster that is triggered when a request is completed.
*
Expand Down
Loading

0 comments on commit b0e4667

Please sign in to comment.