blob: ca631f34607395c01459771a35d893ed854ac9f7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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.apache.geode.test.dunit.rules;
import static org.apache.geode.test.dunit.Disconnect.disconnectAllFromDS;
import static org.apache.geode.test.dunit.VM.DEFAULT_VM_COUNT;
import static org.apache.geode.test.dunit.internal.DUnitLauncher.getDistributedSystemProperties;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Region;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.internal.cache.HARegion;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.test.dunit.VM;
/**
* JUnit Rule that creates Cache instances in DistributedTest VMs without {@code CacheTestCase}.
*
* <p>
* {@code CacheRule} can be used in DistributedTests as a {@code Rule}:
*
* <pre>
* {@literal @}Rule
* public DistributedRule distributedRule = new DistributedRule();
*
* {@literal @}Rule
* public CacheRule cacheRule = new CacheRule();
*
* {@literal @}Before
* public void setUp() {
* getVM(0).invoke(() -> cacheRule.createCache(new CacheFactory().setPdxDiskStore(myDiskStore))));
* }
*
* {@literal @}Test
* public void createRegionWithRegionFactory() {
* getVM(0).invoke(() -> {
* RegionFactory regionFactory = cacheRule.getCache().createRegionFactory();
* Region region = regionFactory.create("RegionName");
* assertThat(region).isNotNull();
* });
* }
* </pre>
*
* <p>
* {@link CacheRule.Builder} can also be used to construct an instance with more options:
*
* <pre>
* {@literal @}Rule
* public DistributedRule distributedRule = new DistributedRule();
*
* {@literal @}Rule
* public CacheRule cacheRule = CacheRule.builder().createCacheInAll().build();
*
* {@literal @}Test
* public void controllerVmCreatedCache() {
* assertThat(cacheRule.getCache()).isNotNull();
* }
*
* {@literal @}Test
* public void remoteVmsCreatedCache() {
* for (VM vm : Host.getHost(0).getAllVMs()) {
* vm.invoke(() -> assertThat(cacheRule.getCache()).isNotNull());
* }
* }
* </pre>
*/
@SuppressWarnings("serial,unused")
public class CacheRule extends AbstractDistributedRule {
private static volatile InternalCache cache;
private final boolean createCacheInAll;
private final boolean createCache;
private final boolean disconnectAfter;
private final boolean destroyRegions;
private final boolean replaceConfig;
private final List<VM> createCacheInVMs;
private final Properties config;
private final Properties systemProperties;
/**
* Use {@code Builder} for more options in constructing {@code CacheRule}.
*/
public static Builder builder() {
return new Builder();
}
public CacheRule() {
this(new Builder());
}
public CacheRule(final int vmCount) {
this(new Builder().vmCount(vmCount));
}
CacheRule(final Builder builder) {
super(builder.vmCount);
createCacheInAll = builder.createCacheInAll;
createCache = builder.createCache;
disconnectAfter = builder.disconnectAfter;
destroyRegions = builder.destroyRegions;
replaceConfig = builder.replaceConfig;
createCacheInVMs = builder.createCacheInVMs;
config = builder.config;
systemProperties = builder.systemProperties;
}
@Override
protected void before() {
if (createCacheInAll) {
invoker().invokeInEveryVMAndController(() -> createCache(config(), systemProperties));
} else {
if (createCache) {
createCache(config(), systemProperties);
}
for (VM vm : createCacheInVMs) {
vm.invoke(() -> createCache(config(), systemProperties));
}
}
}
@Override
protected void after() {
closeAndNullCache();
invoker().invokeInEveryVMAndController(() -> closeAndNullCache());
if (disconnectAfter) {
disconnectAllFromDS();
}
}
public InternalCache getCache() {
return cache;
}
public InternalDistributedSystem getSystem() {
return cache.getInternalDistributedSystem();
}
public void createCache() {
cache = (InternalCache) new CacheFactory(config()).create();
}
public void createCache(final CacheFactory cacheFactory) {
cache = (InternalCache) cacheFactory.create();
}
public void createCache(final Properties config) {
cache = (InternalCache) new CacheFactory(config(config)).create();
}
public void createCache(final Properties config, final Properties systemProperties) {
System.getProperties().putAll(systemProperties);
cache = (InternalCache) new CacheFactory(config(config)).create();
}
public InternalCache getOrCreateCache() {
if (cache == null || cache.isClosed()) {
cache = null;
createCache();
assertThat(cache).isNotNull();
}
return cache;
}
public InternalCache getOrCreateCache(final CacheFactory cacheFactory) {
if (cache == null || cache.isClosed()) {
cache = null;
createCache(cacheFactory);
assertThat(cache).isNotNull();
}
return cache;
}
public InternalCache getOrCreateCache(final Properties config) {
if (cache == null || cache.isClosed()) {
cache = null;
createCache(config);
assertThat(cache).isNotNull();
}
return cache;
}
public InternalCache getOrCreateCache(final Properties config,
final Properties systemProperties) {
if (cache == null || cache.isClosed()) {
cache = null;
createCache(config, systemProperties);
assertThat(cache).isNotNull();
}
return cache;
}
public void closeAndNullCache() {
closeCache();
nullCache();
}
private Properties config() {
return config(new Properties());
}
private Properties config(final Properties config) {
if (replaceConfig) {
return config;
}
Properties allConfig = getDistributedSystemProperties();
allConfig.putAll(this.config);
allConfig.putAll(config);
return allConfig;
}
private void closeCache() {
try {
if (cache != null) {
if (destroyRegions) {
destroyRegions(cache);
}
cache.close();
}
} catch (Exception ignored) {
// ignored
}
}
private static void nullCache() {
cache = null;
}
private static void destroyRegions(final Cache cache) {
if (cache != null && !cache.isClosed()) {
// try to destroy the root regions first so that we clean up any persistent files.
for (Region<?, ?> root : cache.rootRegions()) {
String regionFullPath = root == null ? null : root.getFullPath();
// for colocated regions you can't locally destroy a partitioned region.
if (root.isDestroyed() || root instanceof HARegion || root instanceof PartitionedRegion) {
continue;
}
try {
root.localDestroyRegion("CacheRule_tearDown");
} catch (Exception ignore) {
}
}
}
}
/**
* Builds an instance of CacheRule.
*/
public static class Builder {
private final List<VM> createCacheInVMs = new ArrayList<>();
private final Properties systemProperties = new Properties();
private boolean createCacheInAll;
private boolean createCache;
private boolean disconnectAfter;
private boolean destroyRegions;
private boolean replaceConfig;
private Properties config = new Properties();
private int vmCount = DEFAULT_VM_COUNT;
public Builder() {
// nothing
}
/**
* Create Cache in every VM including controller and all DUnit VMs. Default is false.
*/
public Builder createCacheInAll() {
createCacheInAll = true;
return this;
}
/**
* Create Cache in specified VM. Default is none.
*/
public Builder createCacheIn(final VM vm) {
if (!createCacheInVMs.contains(vm)) {
createCacheInVMs.add(vm);
}
return this;
}
/**
* Create Cache in local JVM (controller). Default is false.
*/
public Builder createCacheInLocal() {
createCache = true;
return this;
}
/**
* Disconnect all VMs from DistributedSystem after each test. Cache is always closed regardless.
* Default is false.
*/
public Builder disconnectAfter() {
disconnectAfter = true;
return this;
}
/**
* Destroy all Regions before closing the Cache. This will cleanup the presence of each Region
* in DiskStores, but this is not needed if the disk files are on a TemporaryFolder. Default is
* false.
*/
public Builder destroyRegions() {
destroyRegions = true;
return this;
}
public Builder replaceConfig(final Properties config) {
this.config = config;
replaceConfig = true;
return this;
}
public Builder addConfig(final String key, final String value) {
config.put(key, value);
return this;
}
public Builder addConfig(final Properties config) {
this.config.putAll(config);
return this;
}
public Builder addSystemProperty(final String key, final String value) {
systemProperties.put(key, value);
return this;
}
public Builder addSystemProperties(final Properties config) {
systemProperties.putAll(config);
return this;
}
public Builder vmCount(final int vmCount) {
this.vmCount = vmCount;
return this;
}
public CacheRule build() {
return new CacheRule(this);
}
}
}