/** * 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.table; import com.microsoft.azure.storage.LocationMode; import com.microsoft.azure.storage.RetryNoRetry; import com.microsoft.azure.storage.SecondaryTests; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.core.SR; import com.microsoft.azure.storage.table.TableTestHelper.Class1; import com.microsoft.azure.storage.table.TableTestHelper.Class2; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Random; import java.util.UUID; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; import static org.junit.Assert.*; @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) public class TableBatchOperationTests { private CloudTable table; @Before public void tableTestMethodSetUp() throws URISyntaxException, StorageException { this.table = TableTestHelper.getRandomTableReference(); this.table.createIfNotExists(); } @After public void tableTestMethodTearDown() throws StorageException { this.table.deleteIfExists(); } @Test public void testBatchAddAll() throws StorageException { ArrayList<TableOperation> ops = allOpsList(); TableBatchOperation batch = new TableBatchOperation(); boolean added = batch.addAll(ops); assertTrue(added); ArrayList<TableResult> results = this.table.execute(batch, null, null); assertEquals(8, results.size()); Iterator<TableResult> iter = results.iterator(); for (int i = 0; i < 7; i++) { assertEquals(HttpURLConnection.HTTP_NO_CONTENT, iter.next().getHttpStatusCode()); } // test to make sure we can't now add a query with addAll() ops.clear(); Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); ops.add(TableOperation.retrieve(ref.partitionKey, ref.rowKey, ref.getClass())); try { batch.addAll(ops); fail(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } catch (Exception e) { assertEquals(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH, e.getMessage()); } // test to make sure we can't now add an operation with a different partition key with addAll() ops.clear(); ref.partitionKey = "jxscl_odata_different"; ops.add(TableOperation.insert(ref)); try { batch.addAll(ops); fail(SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY); } catch (Exception e) { assertEquals(SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY, e.getMessage()); } } @Test public void testBatchAddAllIndex() throws StorageException { ArrayList<TableOperation> ops = allOpsList(); TableBatchOperation batch = new TableBatchOperation(); boolean added = batch.addAll(0, ops); assertTrue(added); ArrayList<TableResult> results = this.table.execute(batch, null, null); assertEquals(8, results.size()); Iterator<TableResult> iter = results.iterator(); for (int i = 0; i < 7; i++) { assertEquals(HttpURLConnection.HTTP_NO_CONTENT, iter.next().getHttpStatusCode()); } // test to make sure we can't now add a query with addAll() ops.clear(); Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); ops.add(TableOperation.retrieve(ref.partitionKey, ref.rowKey, ref.getClass())); try { batch.addAll(0, ops); fail(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } catch (Exception e) { assertEquals(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH, e.getMessage()); } // test to make sure we can't now add an operation with a different partition key with addAll() ops.clear(); ref.partitionKey = "jxscl_odata_different"; ops.add(TableOperation.insert(ref)); try { batch.addAll(0, ops); fail(SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY); } catch (Exception e) { assertEquals(SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY, e.getMessage()); } } @Test public void testBatchAddAllWithRetrieveShouldThrow() throws StorageException { ArrayList<TableOperation> ops = allOpsList(); TableBatchOperation batch = new TableBatchOperation(); // Insert entity to retrieve Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), null, null); ops.add(TableOperation.retrieve(baseEntity.getPartitionKey(), baseEntity.getRowKey(), Class1.class)); try { batch.addAll(ops); fail(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } catch (IllegalArgumentException e) { assertEquals(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH, e.getMessage()); } } @Test @Category(SecondaryTests.class) public void testBatchAddIndex() { TableBatchOperation batch = new TableBatchOperation(); // add a retrieve Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); TableOperation queryOp = TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.add(0, queryOp); // remove the retrieve batch.remove(0); assertEquals(0, batch.size()); // should be able to add an entity with a different partition key Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata_2"); TableOperation op = TableOperation.insert(baseEntity); batch.add(0, op); // remove the insert batch.remove(0); assertEquals(0, batch.size()); // add a retrieve to the batch ref = TableTestHelper.generateRandomEntity("jxscl_odata"); queryOp = TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.add(0, queryOp); } @Test public void testBatchRemoveAll() throws StorageException { ArrayList<TableOperation> ops = allOpsList(); TableBatchOperation batch = new TableBatchOperation(); batch.addAll(ops); assertTrue(batch.removeAll(ops)); assertEquals(0, batch.size()); // should be able to add an entity with a different partition key Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata_2"); batch.insert(baseEntity); } @Test @Category(SecondaryTests.class) public void testBatchRemoveRange() throws StorageException { ArrayList<TableOperation> ops = allOpsList(); TableBatchOperation batch = new TableBatchOperation(); batch.addAll(ops); batch.removeRange(0, ops.size()); assertEquals(0, batch.size()); // should be able to add an entity with a different partition key Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata_2"); batch.insert(baseEntity); batch.removeRange(0, 1); batch.addAll(ops); batch.removeRange(0, ops.size() - 1); // should be not be able to add an entity with a different partition key baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata_2"); try { batch.insert(baseEntity); fail(SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY); } catch (IllegalArgumentException e) { assertEquals(SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY, e.getMessage()); } batch.removeRange(0, 1); // should be able to add a retrieve to the batch Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); TableOperation queryOp = TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.add(queryOp); } @Test @Category(SecondaryTests.class) public void testBatchRemove() { TableBatchOperation batch = new TableBatchOperation(); // add a retrieve Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); TableOperation queryOp = TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.add(queryOp); // remove the retrieve batch.remove(queryOp); assertEquals(0, batch.size()); // should be able to add an entity with a different partition key Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata_2"); TableOperation op = TableOperation.insert(baseEntity); batch.add(op); // remove the insert batch.remove(op); assertEquals(0, batch.size()); // should be able to add a retrieve to the batch ref = TableTestHelper.generateRandomEntity("jxscl_odata"); queryOp = TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.add(queryOp); } @Test public void testBatchLockToPartitionKey() { try { TableBatchOperation batch = new TableBatchOperation(); batch.insert(TableTestHelper.generateRandomEntity("jxscl_odata")); batch.insert(TableTestHelper.generateRandomEntity("jxscl_odata2")); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), SR.OPS_IN_BATCH_MUST_HAVE_SAME_PARTITION_KEY); } } @Test public void testBatchRetrieveAndOneMoreOperationShouldThrow() { Class1 ref2 = TableTestHelper.generateRandomEntity("jxscl_odata"); try { TableBatchOperation batch = new TableBatchOperation(); batch.insert(TableTestHelper.generateRandomEntity("jxscl_odata")); batch.retrieve(ref2.getPartitionKey(), ref2.getRowKey(), ref2.getClass()); fail(); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } try { TableBatchOperation batch = new TableBatchOperation(); batch.retrieve(ref2.getPartitionKey(), ref2.getRowKey(), ref2.getClass()); batch.insert(TableTestHelper.generateRandomEntity("jxscl_odata")); fail(); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } } @Test public void testBatchAddNullShouldThrow() { try { TableBatchOperation batch = new TableBatchOperation(); batch.add(null); fail(); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), String.format(SR.ARGUMENT_NULL_OR_EMPTY, "element")); } } @Test public void testBatchRetrieveWithNullResolverShouldThrow() { try { TableBatchOperation batch = new TableBatchOperation(); batch.retrieve("foo", "blah", (EntityResolver<?>) null); fail(); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), String.format(SR.ARGUMENT_NULL_OR_EMPTY, SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER)); } } @Test public void testEmptyBatchShouldThrow() throws StorageException { TableBatchOperation batch = new TableBatchOperation(); try { this.table.execute(batch, null, null); fail(SR.EMPTY_BATCH_NOT_ALLOWED); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), SR.EMPTY_BATCH_NOT_ALLOWED); } } @Test public void testBatchMultiQueryShouldThrow() { Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); Class1 ref2 = TableTestHelper.generateRandomEntity("jxscl_odata"); try { TableBatchOperation batch = new TableBatchOperation(); batch.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.retrieve(ref2.getPartitionKey(), ref2.getRowKey(), ref2.getClass()); fail(SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), SR.RETRIEVE_MUST_BE_ONLY_OPERATION_IN_BATCH); } } @Test // don't need the category secondary as the request will fail before being sent public void testBatchSecondaryWriteShouldThrow() { // create batch with an insert Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); TableOperation op = TableOperation.insert(baseEntity); TableBatchOperation batch = new TableBatchOperation(); batch.add(op); // should not be able to make a request to secondary as there are writes try { TableRequestOptions options = new TableRequestOptions(); options.setLocationMode(LocationMode.SECONDARY_ONLY); options.setRetryPolicyFactory(new RetryNoRetry()); this.table.execute(batch, options, null); fail("Should not be able to make a request to secondary as there are writes."); } catch (StorageException e) { assertEquals(IllegalArgumentException.class, e.getCause().getClass()); assertEquals(SR.PRIMARY_ONLY_COMMAND, e.getCause().getMessage()); } } @Test @Category(SecondaryTests.class) public void testBatchSecondaryNoWrite() throws StorageException { // create and insert an entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(ref)); // create a batch and add a query for this entity TableBatchOperation batch = new TableBatchOperation(); TableOperation queryOp = TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); batch.add(queryOp); // should be able to make a request to secondary as there are no writes TableRequestOptions options = new TableRequestOptions(); options.setLocationMode(LocationMode.SECONDARY_ONLY); options.setRetryPolicyFactory(new RetryNoRetry()); this.table.execute(batch, options, null); } @Test public void testBatchOver100Entities() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); try { for (int m = 0; m < 101; m++) { batch.insert(TableTestHelper.generateRandomEntity("jxscl_odata")); } this.table.execute(batch, options, null); fail("Batch with over 100 entities should fail."); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Bad Request"); String errorAfterSemiColon = ex.getExtendedErrorInformation().getErrorMessage(); errorAfterSemiColon = errorAfterSemiColon.substring(errorAfterSemiColon.indexOf(":") + 1); assertTrue(errorAfterSemiColon.startsWith("The batch request operation exceeds the maximum 100 changes per change set.")); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.INVALID_INPUT); } } @Test public void testBatchInsertEntityOver1MB() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); Class1 bigEnt = new Class1(); bigEnt.setA("foo_A"); bigEnt.setB("foo_B"); bigEnt.setC("foo_C"); // 1mb right here bigEnt.setD(new byte[1024 * 1024]); bigEnt.setPartitionKey("jxscl_odata"); bigEnt.setRowKey(UUID.randomUUID().toString()); batch.insert(bigEnt); for (int m = 0; m < 3; m++) { Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); ref.setC("foo_C"); ref.setPartitionKey("jxscl_odata"); ref.setRowKey(UUID.randomUUID().toString()); batch.insert(ref); } try { this.table.execute(batch, options, null); fail(); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Bad Request"); String errorAfterSemiColon = ex.getExtendedErrorInformation().getErrorMessage(); errorAfterSemiColon = errorAfterSemiColon.substring(errorAfterSemiColon.indexOf(":") + 1); assertTrue(errorAfterSemiColon.startsWith("The entity is larger than the maximum allowed size (1MB).")); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.ENTITY_TOO_LARGE); } } @Test public void testBatchInsertEntityWithPropertyMoreThan255chars() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); DynamicTableEntity bigEnt = new DynamicTableEntity(); String propName = ""; for (int m = 0; m < 256; m++) { propName = propName.concat("a"); } bigEnt.getProperties().put(propName, new EntityProperty("test")); bigEnt.setPartitionKey("jxscl_odata"); bigEnt.setRowKey(UUID.randomUUID().toString()); batch.insert(bigEnt); for (int m = 0; m < 3; m++) { Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); ref.setC("foo_C"); ref.setPartitionKey("jxscl_odata"); ref.setRowKey(UUID.randomUUID().toString()); batch.insert(ref); } try { this.table.execute(batch, options, null); fail(); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Bad Request"); String errorAfterSemiColon = ex.getExtendedErrorInformation().getErrorMessage(); errorAfterSemiColon = errorAfterSemiColon.substring(errorAfterSemiColon.indexOf(":") + 1); assertTrue(errorAfterSemiColon.startsWith("The property name exceeds the maximum allowed length (255).")); assertEquals(ex.getErrorCode(), "PropertyNameTooLong"); } } @Test public void testBatchSizeOver4mb() { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); byte[] datArr = new byte[1024 * 128]; Random rand = new Random(); rand.nextBytes(datArr); // Each entity is approx 128kb, meaning ~32 entities will result in a request over 4mb. try { for (int m = 0; m < 32; m++) { Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); ref.setC("foo_C"); ref.setD(datArr); ref.setPartitionKey("jxscl_odata"); ref.setRowKey(UUID.randomUUID().toString()); batch.insert(ref); } this.table.execute(batch, options, null); fail(); } catch (StorageException ex) { assertEquals(ex.getHttpStatusCode(), HttpURLConnection.HTTP_ENTITY_TOO_LARGE); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.REQUEST_BODY_TOO_LARGE); assertTrue(ex.getMessage().startsWith( "The request body is too large and exceeds the maximum permissible limit.")); } } @Test public void testBatchDeleteFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); // Insert entity to delete Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); Class1 updatedEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); updatedEntity.setPartitionKey(baseEntity.getPartitionKey()); updatedEntity.setRowKey(baseEntity.getRowKey()); updatedEntity.setEtag(baseEntity.getEtag()); this.table.execute(TableOperation.replace(updatedEntity), options, null); // add delete to fail batch.delete(baseEntity); try { this.table.execute(batch, options, null); fail(); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Precondition Failed"); assertTrue(ex.getExtendedErrorInformation().getErrorMessage() .startsWith("The update condition specified in the request was not satisfied.")); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.UPDATE_CONDITION_NOT_SATISFIED); } } @Test public void testBatchInsertFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); // insert entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(ref), options, null); try { TableBatchOperation batch = new TableBatchOperation(); batch.insert(ref); this.table.execute(batch, options, null); fail(); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Conflict"); assertTrue(ex.getExtendedErrorInformation().getErrorMessage() .startsWith("The specified entity already exists")); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.ENTITY_ALREADY_EXISTS); } } @Test public void testBatchReplaceFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); // Insert entity to merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); Class1 updatedEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); updatedEntity.setPartitionKey(baseEntity.getPartitionKey()); updatedEntity.setRowKey(baseEntity.getRowKey()); updatedEntity.setEtag(baseEntity.getEtag()); this.table.execute(TableOperation.replace(updatedEntity), options, null); // add merge to fail addReplaceToBatch(baseEntity, batch); try { this.table.execute(batch); fail(); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Precondition Failed"); assertTrue(ex.getExtendedErrorInformation().getErrorMessage() .startsWith("The update condition specified in the request was not satisfied.")); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.UPDATE_CONDITION_NOT_SATISFIED); } } @Test public void testBatchMergeFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); TableBatchOperation batch = new TableBatchOperation(); addInsertBatch(batch); // Insert entity to merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); Class1 updatedEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); updatedEntity.setPartitionKey(baseEntity.getPartitionKey()); updatedEntity.setRowKey(baseEntity.getRowKey()); updatedEntity.setEtag(baseEntity.getEtag()); this.table.execute(TableOperation.replace(updatedEntity), options, null); // add merge to fail addMergeToBatch(baseEntity, batch); try { this.table.execute(batch, options, null); fail(); } catch (TableServiceException ex) { assertEquals(ex.getMessage(), "Precondition Failed"); String errorAfterSemiColon = ex.getExtendedErrorInformation().getErrorMessage(); errorAfterSemiColon = errorAfterSemiColon.substring(errorAfterSemiColon.indexOf(":") + 1); assertTrue(errorAfterSemiColon .startsWith("The update condition specified in the request was not satisfied.")); assertEquals(ex.getErrorCode(), StorageErrorCodeStrings.UPDATE_CONDITION_NOT_SATISFIED); } } @Test public void testBatchEmptyQuery() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.Json); // insert entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); TableBatchOperation batch = new TableBatchOperation(); batch.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); ArrayList<TableResult> results = this.table.execute(batch, options, null); assertEquals(results.size(), 1); assertNull(results.get(0).getResult()); assertEquals(results.get(0).getHttpStatusCode(), HttpURLConnection.HTTP_NOT_FOUND); } @Test public void testBatchWithAllOperations() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchWithAllOperations(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchWithAllOperations(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchWithAllOperations(options); } private void testBatchWithAllOperations(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); // insert addInsertBatch(batch); { // insert entity to delete Class1 delRef = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(delRef), options, null); batch.delete(delRef); } { // Insert entity to replace Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addReplaceToBatch(baseEntity, batch); } { // Insert entity to insert or replace Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addInsertOrReplaceToBatch(baseEntity, batch); } { // Insert entity to merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addMergeToBatch(baseEntity, batch); } { // Insert entity to merge, no pre-existing entity Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addInsertOrMergeToBatch(baseEntity, batch); } ArrayList<TableResult> results = this.table.execute(batch, options, null); assertEquals(results.size(), 6); Iterator<TableResult> iter = results.iterator(); // insert assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // delete assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // replace assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // insert or replace assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // merge assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // insert or merge assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } @Test public void testBatchDelete() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchDelete(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchDelete(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchDelete(options); } private void testBatchDelete(TableRequestOptions options) throws StorageException { Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); // insert entity this.table.execute(TableOperation.insert(ref), options, null); TableBatchOperation batch = new TableBatchOperation(); batch.delete(ref); ArrayList<TableResult> delResults = this.table.execute(batch, options, null); for (TableResult r : delResults) { assertEquals(r.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } try { this.table.execute(batch, options, null); fail(); } catch (StorageException ex) { assertEquals(ex.getHttpStatusCode(), HttpURLConnection.HTTP_NOT_FOUND); } } @Test public void testBatchRetrieve() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchRetrieve(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchRetrieve(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchRetrieve(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); options.setPropertyResolver(new Class1()); testBatchRetrieve(options); } private void testBatchRetrieve(TableRequestOptions options) throws StorageException { // insert entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(ref), options, null); TableBatchOperation batch = new TableBatchOperation(); batch.retrieve(ref.getPartitionKey(), ref.getRowKey(), ref.getClass()); ArrayList<TableResult> results = this.table.execute(batch, options, null); assertEquals(results.size(), 1); assertEquals(results.get(0).getHttpStatusCode(), HttpURLConnection.HTTP_OK); Class1 retrievedRef = results.get(0).getResultAsType(); assertEquals(ref.getA(), retrievedRef.getA()); assertEquals(ref.getB(), retrievedRef.getB()); assertEquals(ref.getC(), retrievedRef.getC()); assertTrue(Arrays.equals(ref.getD(), retrievedRef.getD())); this.table.execute(TableOperation.delete(ref), options, null); } @Test public void tableBatchRetrieveWithEntityResolver() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); tableBatchRetrieveWithEntityResolver(options); options.setTablePayloadFormat(TablePayloadFormat.Json); tableBatchRetrieveWithEntityResolver(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); tableBatchRetrieveWithEntityResolver(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); options.setPropertyResolver(new Class1()); tableBatchRetrieveWithEntityResolver(options); } private void tableBatchRetrieveWithEntityResolver(TableRequestOptions options) throws StorageException { // insert entity Class1 randEnt = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(randEnt), options, null); TableBatchOperation batch = new TableBatchOperation(); batch.retrieve(randEnt.getPartitionKey(), randEnt.getRowKey(), new EntityResolver<Class1>() { @Override public Class1 resolve(String partitionKey, String rowKey, Date timeStamp, HashMap<String, EntityProperty> properties, String etag) { assertEquals(properties.size(), 4); Class1 ref = new Class1(); ref.setA(properties.get("A").getValueAsString()); ref.setB(properties.get("B").getValueAsString()); ref.setC(properties.get("C").getValueAsString()); ref.setD(properties.get("D").getValueAsByteArray()); return ref; } }); ArrayList<TableResult> results = this.table.execute(batch, options, null); assertEquals(results.size(), 1); Class1 ent = (Class1) results.get(0).getResult(); // Validate results assertEquals(ent.getA(), randEnt.getA()); assertEquals(ent.getB(), randEnt.getB()); assertEquals(ent.getC(), randEnt.getC()); assertTrue(Arrays.equals(ent.getD(), randEnt.getD())); } @Test public void testBatchInsert() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchInsert(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchInsert(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchInsert(options); } private void testBatchInsert(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); // Add 3 inserts addInsertBatch(batch); // default echo content (true) addInsertBatch(batch, true); // set echo content to true addInsertBatch(batch, false); // set echo content to false // insert entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(ref), options, null); batch.delete(ref); ArrayList<TableResult> results = this.table.execute(batch, options, null); assertEquals(results.size(), 4); Iterator<TableResult> iter = results.iterator(); TableResult res = iter.next(); assertEquals(res.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); res = iter.next(); assertEquals(res.getHttpStatusCode(), HttpURLConnection.HTTP_CREATED); res = iter.next(); assertEquals(res.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // delete assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } @Test public void testBatchMerge() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchMerge(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchMerge(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchMerge(options); } private void testBatchMerge(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); addInsertBatch(batch); // insert entity to delete Class1 delRef = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(delRef)); batch.delete(delRef); // Insert entity to merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addMergeToBatch(baseEntity, batch); ArrayList<TableResult> results = this.table.execute(batch); assertEquals(results.size(), 3); Iterator<TableResult> iter = results.iterator(); // insert assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // delete assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // merge assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } @Test public void testBatchReplace() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchReplace(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchReplace(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchReplace(options); } private void testBatchReplace(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); addInsertBatch(batch); // insert entity to delete Class1 delRef = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(delRef), options, null); batch.delete(delRef); // Insert entity to replace Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addReplaceToBatch(baseEntity, batch); ArrayList<TableResult> results = this.table.execute(batch); assertEquals(results.size(), 3); Iterator<TableResult> iter = results.iterator(); // insert assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // delete assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // replace assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } @Test public void testBatchInsertOrMerge() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchInsertOrMerge(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchInsertOrMerge(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchInsertOrMerge(options); } private void testBatchInsertOrMerge(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); addInsertBatch(batch); // insert entity to delete Class1 delRef = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(delRef), options, null); batch.delete(delRef); // Insert entity to merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addInsertOrMergeToBatch(baseEntity, batch); ArrayList<TableResult> results = this.table.execute(batch); assertEquals(results.size(), 3); Iterator<TableResult> iter = results.iterator(); // insert assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // delete assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // merge assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } @Test public void testBatchInsertOrReplace() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchInsertOrReplace(options); options.setTablePayloadFormat(TablePayloadFormat.Json); testBatchInsertOrReplace(options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); testBatchInsertOrReplace(options); } private void testBatchInsertOrReplace(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); addInsertBatch(batch); // insert entity to delete Class1 delRef = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(delRef), options, null); batch.delete(delRef); // Insert entity to replace Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity), options, null); addInsertOrReplaceToBatch(baseEntity, batch); ArrayList<TableResult> results = this.table.execute(batch, options, null); assertEquals(results.size(), 3); Iterator<TableResult> iter = results.iterator(); // insert assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // delete assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // replace assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } @Test public void testInsertBatch1() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); insertAndDeleteBatchWithX(1, options); options.setTablePayloadFormat(TablePayloadFormat.Json); insertAndDeleteBatchWithX(1, options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); insertAndDeleteBatchWithX(1, options); } @Test public void testInsertBatch10() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); insertAndDeleteBatchWithX(10, options); options.setTablePayloadFormat(TablePayloadFormat.Json); insertAndDeleteBatchWithX(10, options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); insertAndDeleteBatchWithX(10, options); } @Test public void testInsertBatch100() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); insertAndDeleteBatchWithX(100, options); options.setTablePayloadFormat(TablePayloadFormat.Json); insertAndDeleteBatchWithX(100, options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); insertAndDeleteBatchWithX(100, options); } @Test public void testUpsertBatch1() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); upsertAndDeleteBatchWithX(1, options); options.setTablePayloadFormat(TablePayloadFormat.Json); upsertAndDeleteBatchWithX(1, options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); upsertAndDeleteBatchWithX(1, options); } @Test public void testUpsertBatch10() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); upsertAndDeleteBatchWithX(10, options); options.setTablePayloadFormat(TablePayloadFormat.Json); upsertAndDeleteBatchWithX(10, options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); upsertAndDeleteBatchWithX(10, options); } @Test public void testUpsertBatch100() throws StorageException { TableRequestOptions options = new TableRequestOptions(); options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); upsertAndDeleteBatchWithX(100, options); options.setTablePayloadFormat(TablePayloadFormat.Json); upsertAndDeleteBatchWithX(100, options); options.setTablePayloadFormat(TablePayloadFormat.JsonNoMetadata); upsertAndDeleteBatchWithX(100, options); } private Class1 addInsertBatch(TableBatchOperation batch) { Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); batch.insert(ref); return ref; } private Class1 addInsertBatch(TableBatchOperation batch, boolean echoContent) { Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); batch.insert(ref, echoContent); return ref; } private Class2 addInsertOrMergeToBatch(Class1 baseEntity, TableBatchOperation batch) { Class2 secondEntity = createEntityToReplaceOrMerge(baseEntity); batch.insertOrMerge(secondEntity); return secondEntity; } private Class2 addInsertOrReplaceToBatch(Class1 baseEntity, TableBatchOperation batch) { Class2 secondEntity = createEntityToReplaceOrMerge(baseEntity); batch.insertOrReplace(secondEntity); return secondEntity; } private Class2 addMergeToBatch(Class1 baseEntity, TableBatchOperation batch) { Class2 secondEntity = createEntityToReplaceOrMerge(baseEntity); batch.merge(secondEntity); return secondEntity; } private Class2 addReplaceToBatch(Class1 baseEntity, TableBatchOperation batch) { Class2 secondEntity = createEntityToReplaceOrMerge(baseEntity); batch.replace(secondEntity); return secondEntity; } private void insertAndDeleteBatchWithX(int x, TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); for (int m = 0; m < x; m++) { addInsertBatch(batch); } TableBatchOperation delBatch = new TableBatchOperation(); ArrayList<TableResult> results = this.table.execute(batch, options, null); for (TableResult r : results) { assertEquals(r.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); delBatch.delete((Class1) r.getResult()); } ArrayList<TableResult> delResults = this.table.execute(delBatch, options, null); for (TableResult r : delResults) { assertEquals(r.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } } private void upsertAndDeleteBatchWithX(int x, TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); for (int m = 0; m < x; m++) { addInsertOrMergeToBatch(TableTestHelper.generateRandomEntity("jxscl_odata"), batch); } TableBatchOperation delBatch = new TableBatchOperation(); ArrayList<TableResult> results = this.table.execute(batch, options, null); for (TableResult r : results) { assertEquals(r.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); delBatch.delete((Class2) r.getResult()); } ArrayList<TableResult> delResults = this.table.execute(delBatch, options, null); for (TableResult r : delResults) { assertEquals(r.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } } private ArrayList<TableOperation> allOpsList() throws StorageException { ArrayList<TableOperation> ops = new ArrayList<TableOperation>(); // insert ops.add(TableOperation.insert(TableTestHelper.generateRandomEntity("jxscl_odata"))); { // Insert entity to delete Class1 delRef = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(delRef)); ops.add(TableOperation.delete(delRef)); } { // Insert entity to replace Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity)); ops.add(TableOperation.replace(createEntityToReplaceOrMerge(baseEntity))); } { // Insert entity to insert or replace Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity)); ops.add(TableOperation.insertOrReplace(createEntityToReplaceOrMerge(baseEntity))); } { // Insert or replace, no pre-existing entity Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); ops.add(TableOperation.insertOrReplace(createEntityToReplaceOrMerge(baseEntity))); } { // Insert entity to merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity)); ops.add(TableOperation.merge(createEntityToReplaceOrMerge(baseEntity))); } { // Insert entity to insert or merge Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(baseEntity)); ops.add(TableOperation.insertOrMerge(baseEntity)); } { // Insert or merge, no pre-existing entity Class1 baseEntity = TableTestHelper.generateRandomEntity("jxscl_odata"); ops.add(TableOperation.insertOrMerge(baseEntity)); } return ops; } private Class2 createEntityToReplaceOrMerge(Class1 baseEntity) { Class2 secondEntity = new Class2(); secondEntity.setL("foo_L"); secondEntity.setM("foo_M"); secondEntity.setN("foo_N"); secondEntity.setO("foo_O"); secondEntity.setPartitionKey(baseEntity.getPartitionKey()); secondEntity.setRowKey(baseEntity.getRowKey()); secondEntity.setEtag(baseEntity.getEtag()); return secondEntity; } }