SGF-354 - SimpleGemfireRepository.deleteAll that supports transactions.

This commit is contained in:
John Blum
2014-12-10 21:32:35 -08:00
parent 2166f9ea6c
commit cf99650add
4 changed files with 366 additions and 14 deletions

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.repository.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Resource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.repository.GemfireRepository;
import org.springframework.data.gemfire.repository.sample.Customer;
import org.springframework.data.repository.core.support.ReflectionEntityInformation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.gemstone.gemfire.cache.Region;
/**
* The SimpleGemfireRepositoryTransactionalIntegrationTest class is a test suite of test cases testing
* the SimpleGemfireRepository class and SDC Repository abstraction implementation in the context of
* GemFire "Cache" Transactions.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.data.gemfire.repository.support.SimpleGemfireRepository
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.6.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class SimpleGemfireRepositoryTransactionalIntegrationTest {
// TODO add additional test cases for SimpleGemfireRepository (Region operations) in the presence of Transactions!!!
protected static final AtomicLong ID_SEQUENCE = new AtomicLong(0l);
@Autowired
private CustomerService customerService;
@Resource(name = "Customers")
private Region customers;
protected static Customer createCustomer(final String firstName, final String lastName) {
Customer customer = new SerializableCustomer(firstName, lastName);
customer.setId(ID_SEQUENCE.incrementAndGet());
return customer;
}
@Before
public void setup() {
assertNotNull("The 'Customers' GemFire Cache Region was not properly configured and initialized!", customers);
assertEquals("Customers", customers.getName());
assertEquals("/Customers", customers.getFullPath());
assertTrue(customers.isEmpty());
}
@After
public void tearDown() {
customers.clear();
}
@Test
public void testDeleteAll() {
Collection<Customer> expectedCustomers = new ArrayList<Customer>(4);
expectedCustomers.add(createCustomer("Jon", "Doe"));
expectedCustomers.add(createCustomer("Jane", "Doe"));
expectedCustomers.add(createCustomer("Pie", "Doe"));
expectedCustomers.add(createCustomer("Cookie", "Doe"));
customerService.saveAll(expectedCustomers);
assertFalse(customers.isEmpty());
assertEquals(expectedCustomers.size(), customers.size());
try {
customerService.removeAllCausingTransactionRollback();
}
catch (RuntimeException ignore) {
// the RuntimeException should cause the Cache Transaction to rollback and avoid the Region modification!
}
assertFalse(customers.isEmpty());
assertEquals(expectedCustomers.size(), customers.size());
customerService.removeAll();
assertTrue(customers.isEmpty());
}
public static class SerializableCustomer extends Customer implements Serializable {
public SerializableCustomer() {
}
public SerializableCustomer(final Long id) {
super(id);
}
public SerializableCustomer(final String firstName, final String lastName) {
super(firstName, lastName);
}
}
public static class CustomerService {
private GemfireRepository<Customer, Long> customerRepository;
private TransactionTemplate transactionTemplate;
@Autowired
public CustomerService(GemfireTemplate customersTemplate, PlatformTransactionManager transactionManager) {
customerRepository = new SimpleGemfireRepository<Customer, Long>(customersTemplate,
new ReflectionEntityInformation<Customer, Long>(Customer.class));
transactionTemplate = new TransactionTemplate(transactionManager);
}
public void saveAll(final Iterable<Customer> customers) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override protected void doInTransactionWithoutResult(final TransactionStatus status) {
customerRepository.save(customers);
}
});
}
public void removeAllCausingTransactionRollback() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override protected void doInTransactionWithoutResult(final TransactionStatus status) {
removeAll();
throw new IllegalStateException("'removeAll' operation not permitted");
}
});
}
public void removeAll() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override protected void doInTransactionWithoutResult(final TransactionStatus status) {
customerRepository.deleteAll();
}
});
}
}
}

View File

@@ -48,7 +48,11 @@ import org.springframework.data.gemfire.repository.sample.Animal;
import org.springframework.data.gemfire.test.support.CollectionUtils;
import org.springframework.data.repository.core.EntityInformation;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheTransactionManager;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
/**
* The SimpleGemfireRepositoryUnitTest class is a test suite of test cases testing the contract and functionality
@@ -57,6 +61,8 @@ import com.gemstone.gemfire.cache.Region;
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.repository.Wrapper
* @see org.springframework.data.gemfire.repository.support.SimpleGemfireRepository
* @since 1.4.5
*/
@@ -87,6 +93,18 @@ public class SimpleGemfireRepositoryUnitTest {
return new GemfireTemplate(region);
}
protected Cache mockCache(final String mockName, final boolean transactionExists) {
Cache mockCache = mock(Cache.class, String.format("%1$s.MockCache", mockName));
CacheTransactionManager mockCacheTransactionManager = mock(CacheTransactionManager.class, String.format(
"%1$s.MockCacheTransactionManager", mockName));
when(mockCache.getCacheTransactionManager()).thenReturn(mockCacheTransactionManager);
when(mockCacheTransactionManager.exists()).thenReturn(transactionExists);
return mockCache;
}
protected EntityInformation<Animal, Long> mockEntityInformation() {
EntityInformation<Animal, Long> mockEntityInformation = mock(EntityInformation.class);
@@ -105,6 +123,20 @@ public class SimpleGemfireRepositoryUnitTest {
return mockEntityInformation;
}
protected Region mockRegion(final String mockName, final Cache mockCache, final DataPolicy dataPolicy) {
Region mockRegion = mock(Region.class, String.format("%1$s.MockRegion", mockName));
when(mockRegion.getRegionService()).thenReturn(mockCache);
RegionAttributes mockRegionAttributes = mock(RegionAttributes.class, String.format(
"%1$s.MockRegionAttributes", mockName));
when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes);
when(mockRegionAttributes.getDataPolicy()).thenReturn(dataPolicy);
return mockRegion;
}
@Test
public void testSave() {
Region<Long, Animal> mockRegion = mock(Region.class, "testSave");
@@ -272,32 +304,89 @@ public class SimpleGemfireRepositoryUnitTest {
@Test
public void testDeleteAllWithClear() {
Region<Long, Animal> mockRegion = mock(Region.class, "testDeleteEntities");
Cache mockCache = mockCache("testDeleteAllWithClear", false);
SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<Animal, Long>(
Region<Long, Animal> mockRegion = mockRegion("testDeleteAllWithClear", mockCache, DataPolicy.REPLICATE);
SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<Animal, Long>(
createGemfireTemplate(mockRegion), mockEntityInformation());
repository.deleteAll();
gemfireRepository.deleteAll();
verify(mockCache, times(1)).getCacheTransactionManager();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(1)).clear();
}
@Test
public void testDeleteAllWithKeys() {
Region<Long, Animal> mockRegion = mock(Region.class, "testDeleteEntities");
public void testDeleteAllWithKeysWhenClearThrowsUnsupportedOperationException() {
Cache mockCache = mockCache("testDeleteAllWithKeysWhenClearThrowsUnsupportedOperationException", false);
Region<Long, Animal> mockRegion = mockRegion("testDeleteAllWithKeysWhenClearThrowsUnsupportedOperationException",
mockCache, DataPolicy.PERSISTENT_REPLICATE);
doThrow(new UnsupportedOperationException("Not Implemented!")).when(mockRegion).clear();
when(mockRegion.keySet()).thenReturn(new HashSet<Long>(Arrays.asList(1l, 2l, 3l)));
SimpleGemfireRepository<Animal, Long> repository = new SimpleGemfireRepository<Animal, Long>(
SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<Animal, Long>(
createGemfireTemplate(mockRegion), mockEntityInformation());
repository.deleteAll();
gemfireRepository.deleteAll();
verify(mockCache, times(1)).getCacheTransactionManager();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(1)).clear();
verify(mockRegion, times(1)).remove(eq(1l));
verify(mockRegion, times(1)).remove(eq(2l));
verify(mockRegion, times(1)).remove(eq(3l));
}
@Test
public void testDeleteAllWithKeysWhenPartitionRegion() {
Cache mockCache = mockCache("testDeleteAllWithKeysWhenPartitionRegion", false);
Region<Long, Animal> mockRegion = mockRegion("testDeleteAllWithKeysWhenPartitionRegion", mockCache,
DataPolicy.PERSISTENT_PARTITION);
when(mockRegion.keySet()).thenReturn(new HashSet<Long>(Arrays.asList(1l, 2l, 3l)));
SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<Animal, Long>(
createGemfireTemplate(mockRegion), mockEntityInformation());
gemfireRepository.deleteAll();
verify(mockCache, times(0)).getCacheTransactionManager();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegion, times(0)).getRegionService();
verify(mockRegion, times(0)).clear();
verify(mockRegion, times(1)).remove(eq(1l));
verify(mockRegion, times(1)).remove(eq(2l));
verify(mockRegion, times(1)).remove(eq(3l));
}
@Test
public void testDeleteAllWithKeysWhenTransactionPresent() {
Cache mockCache = mockCache("testDeleteAllWithKeysWhenTransactionPresent", true);
Region<Long, Animal> mockRegion = mockRegion("testDeleteAllWithKeysWhenTransactionPresent", mockCache,
DataPolicy.REPLICATE);
when(mockRegion.keySet()).thenReturn(new HashSet<Long>(Arrays.asList(1l, 2l, 3l)));
SimpleGemfireRepository<Animal, Long> gemfireRepository = new SimpleGemfireRepository<Animal, Long>(
createGemfireTemplate(mockRegion), mockEntityInformation());
gemfireRepository.deleteAll();
verify(mockCache, times(1)).getCacheTransactionManager();
verify(mockRegion, times(2)).getAttributes();
verify(mockRegion, times(2)).getRegionService();
verify(mockRegion, times(0)).clear();
verify(mockRegion, times(1)).remove(eq(1l));
verify(mockRegion, times(1)).remove(eq(2l));
verify(mockRegion, times(1)).remove(eq(3l));
}
}