blob: d1ab73aa0941aae3577bd7292bf159a489090769 [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.cache.internal;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.apache.geode.distributed.internal.DistributionConfig.GEMFIRE_PREFIX;
import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Properties;
import org.apache.logging.log4j.Logger;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheException;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.CacheTransactionManager;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.RegionExistsException;
import org.apache.geode.cache.TimeoutException;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.PoolManager;
import org.apache.geode.cache30.CacheSerializableRunnable;
import org.apache.geode.distributed.internal.DistributionMessageObserver;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.LocalRegion;
import org.apache.geode.internal.cache.xmlcache.CacheCreation;
import org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator;
import org.apache.geode.logging.internal.log4j.api.LogService;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.Invoke;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.DistributedRule;
/**
* This class is the base class for all distributed tests using JUnit 4 that require the creation of
* a {@link Cache}.
*
* @deprecated Please use {@link DistributedRule} and Geode User APIs or {@link ClusterStartupRule}
* instead.
*/
public abstract class JUnit4CacheTestCase extends JUnit4DistributedTestCase
implements CacheTestFixture {
private static final Logger logger = LogService.getLogger();
/**
* The Cache from which regions are obtained.
*
* <p>
* All references synchronized via {@code JUnit4CacheTestCase.class}.
*
* <p>
* Field is static so it doesn't get serialized with SerializableRunnable inner classes.
*/
protected static InternalCache cache;
private final CacheTestFixture cacheTestFixture;
public JUnit4CacheTestCase() {
this(null);
}
JUnit4CacheTestCase(final CacheTestFixture cacheTestFixture) {
super(cacheTestFixture);
if (cacheTestFixture == null) {
this.cacheTestFixture = this;
} else {
this.cacheTestFixture = cacheTestFixture;
}
}
/**
* Creates the {@code Cache} for this test
*/
private final void createCache() {
createCache(false);
}
private final void createCache(final boolean client) {
createCache(client, null);
}
private final void createCache(final boolean client, final CacheFactory factory) {
synchronized (JUnit4CacheTestCase.class) {
try {
System.setProperty(GEMFIRE_PREFIX + "DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
InternalCache newCache;
if (client) {
System.setProperty(GEMFIRE_PREFIX + "locators", "");
System.setProperty(GEMFIRE_PREFIX + MCAST_PORT, "0");
newCache = (InternalCache) new ClientCacheFactory(getSystem().getProperties()).create();
} else {
if (factory == null) {
newCache = (InternalCache) CacheFactory.create(getSystem());
} else {
Properties config = getSystem().getProperties();
for (Map.Entry entry : config.entrySet()) {
factory.set((String) entry.getKey(), (String) entry.getValue());
}
newCache = (InternalCache) factory.create();
}
}
cache = newCache;
} finally {
System.clearProperty(GEMFIRE_PREFIX + "DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
System.clearProperty(GEMFIRE_PREFIX + "locators");
System.clearProperty(GEMFIRE_PREFIX + MCAST_PORT);
}
}
}
/**
* Creates the {@code Cache} for this test that is not connected to other members.
*/
public final InternalCache createLonerCache() {
synchronized (JUnit4CacheTestCase.class) {
try {
System.setProperty(GEMFIRE_PREFIX + "DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
InternalCache newCache = (InternalCache) CacheFactory.create(getLonerSystem());
cache = newCache;
} finally {
System.clearProperty(GEMFIRE_PREFIX + "DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
}
return cache;
}
}
/**
* Sets this test up with a {@code CacheCreation} as its cache. Any existing cache is closed.
* Whoever calls this must also call {@code finishCacheXml}.
*/
public static synchronized void beginCacheXml() {
closeCache();
cache = new TestCacheCreation();
}
/**
* Finish what {@code beginCacheXml} started. It does this be generating a cache.xml file and then
* creating a real cache using that cache.xml.
*/
public final void finishCacheXml(final String name) {
synchronized (JUnit4CacheTestCase.class) {
try {
File file = new File(name + "-cache.xml");
PrintWriter printWriter = new PrintWriter(new FileWriter(file), true);
CacheXmlGenerator.generate(cache, printWriter);
printWriter.close();
cache = null;
GemFireCacheImpl.testCacheXml = file;
createCache();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
GemFireCacheImpl.testCacheXml = null;
}
}
}
/**
* Finish what {@code beginCacheXml} started. It does this be generating a cache.xml file and then
* creating a real cache using that cache.xml.
*/
public final void finishCacheXml(final File root, final String name, final boolean useSchema,
final String xmlVersion) throws IOException {
synchronized (JUnit4CacheTestCase.class) {
File dir = new File(root, "XML_" + xmlVersion);
dir.mkdirs();
File file = new File(dir, name + ".xml");
PrintWriter printWriter = new PrintWriter(new FileWriter(file), true);
CacheXmlGenerator.generate(cache, printWriter, useSchema, xmlVersion);
printWriter.close();
cache = null;
GemFireCacheImpl.testCacheXml = file;
try {
createCache();
} finally {
GemFireCacheImpl.testCacheXml = null;
}
}
}
/**
* Return a cache for obtaining regions, created lazily.
*/
public final InternalCache getCache() {
return getCache(false);
}
public final InternalCache getCache(final CacheFactory factory) {
return getCache(false, factory);
}
public final InternalCache getCache(final Properties properties) {
getSystem(properties);
return getCache();
}
public final InternalCache getCache(final boolean client) {
return getCache(client, null);
}
public final InternalCache getCache(final boolean client, final CacheFactory factory) {
synchronized (JUnit4CacheTestCase.class) {
InternalCache gemFireCache = GemFireCacheImpl.getInstance();
if (gemFireCache != null && !gemFireCache.isClosed()
&& gemFireCache.getCancelCriterion().isCancelInProgress()) {
await("waiting for cache to close")
.until(gemFireCache::isClosed);
}
if (cache == null || cache.isClosed()) {
cache = null;
createCache(client, factory);
}
if (client && cache != null) {
IgnoredException.addIgnoredException("java.net.ConnectException");
}
return cache;
}
}
/**
* Creates a client cache from the factory if one does not already exist.
*
* @since GemFire 6.5
*/
public final ClientCache getClientCache(final ClientCacheFactory factory) {
synchronized (JUnit4CacheTestCase.class) {
InternalCache gemFireCache = GemFireCacheImpl.getInstance();
if (gemFireCache != null && !gemFireCache.isClosed()
&& gemFireCache.getCancelCriterion().isCancelInProgress()) {
await("waiting for cache to close")
.until(gemFireCache::isClosed);
}
if (cache == null || cache.isClosed()) {
cache = null;
disconnectFromDS();
cache = (InternalCache) factory.create();
}
if (cache != null) {
IgnoredException.addIgnoredException("java.net.ConnectException");
}
return (ClientCache) cache;
}
}
public final ClientCache getClientCache() {
return (ClientCache) cache;
}
/**
* Invokes {@link #getCache()} and casts the return to {@code GemFireCacheImpl}.
*
* <p>
* TODO: change all callers to use getCache and delete getGemfireCache
*
* @deprecated Please use {@link #getCache} which returns InternalCache instead.
*/
@Deprecated
public final GemFireCacheImpl getGemfireCache() {
return (GemFireCacheImpl) getCache();
}
public static final synchronized boolean hasCache() {
return cache != null;
}
/**
* Return current cache without creating one.
*/
public static final synchronized InternalCache basicGetCache() {
return cache;
}
/**
* Close the cache.
*/
public static final synchronized void closeCache() {
// Workaround for the fact that some classes are now extending
// CacheTestCase but not using it properly.
if (cache == null) {
cache = GemFireCacheImpl.getInstance();
}
try {
if (cache != null) {
try {
if (!cache.isClosed()) {
if (cache instanceof GemFireCacheImpl) {
// this unnecessary type-cast prevents NoSuchMethodError
// java.lang.NoSuchMethodError:
// org.apache.geode.internal.cache.InternalCache.getTxManager()Lorg/apache/geode/internal/cache/TXManagerImpl
CacheTransactionManager transactionManager =
((GemFireCacheImpl) cache).getTxManager();
if (transactionManager != null) {
if (transactionManager.exists()) {
try {
// make sure we cleanup this threads txid stored in a thread local
transactionManager.rollback();
} catch (Exception ignore) {
}
}
}
}
cache.close();
}
} finally {
cache = null;
}
} // cache != null
} finally {
// Make sure all pools are closed, even if we never created a cache
PoolManager.close(false);
}
}
/**
* Close the cache in all VMs.
*/
protected final void closeAllCache() {
closeCache();
Invoke.invokeInEveryVM(JUnit4CacheTestCase::closeCache);
}
@Override
public final void preTearDown() throws Exception {
preTearDownCacheTestCase();
tearDownCacheTestCase();
postTearDownCacheTestCase();
}
private final void tearDownCacheTestCase() {
remoteTearDown();
Invoke.invokeInEveryVM(JUnit4CacheTestCase::remoteTearDown);
}
@Override
public void preTearDownCacheTestCase() throws Exception {
if (this.cacheTestFixture != this) {
this.cacheTestFixture.preTearDownCacheTestCase();
}
}
@Override
public void postTearDownCacheTestCase() throws Exception {
if (this.cacheTestFixture != this) {
this.cacheTestFixture.postTearDownCacheTestCase();
}
}
/**
* Local destroy all root regions and close the cache.
*/
private static synchronized void remoteTearDown() {
try {
DistributionMessageObserver.setInstance(null);
destroyRegions(cache);
} finally {
try {
closeCache();
} finally {
try {
cleanDiskDirs();
} catch (Exception e) {
logger.error("Error cleaning disk dirs", e);
}
}
}
}
/**
* Returns a region with the given name and attributes.
*/
public final <K, V> Region<K, V> createRegion(final String name,
final RegionAttributes<K, V> attributes)
throws CacheException {
return createRegion(name, "root", attributes);
}
public final <K, V> Region<K, V> createPartitionedRegion(final String rootName,
final RegionAttributes<K, V> attributes) throws CacheException {
Region<K, V> root = getRootRegion(rootName);
if (root == null) {
// don't put listeners on root region
AttributesFactory<K, V> attributesFactory = new AttributesFactory<>(attributes);
ExpirationAttributes expiration = ExpirationAttributes.DEFAULT;
attributesFactory.setCacheLoader(null);
attributesFactory.setCacheWriter(null);
attributesFactory.setPoolName(null);
attributesFactory.setDataPolicy(DataPolicy.PARTITION);
attributesFactory.setRegionTimeToLive(expiration);
attributesFactory.setEntryTimeToLive(expiration);
attributesFactory.setRegionIdleTimeout(expiration);
attributesFactory.setEntryIdleTimeout(expiration);
RegionAttributes<K, V> rootAttrs = attributesFactory.create();
root = createRootRegion(rootName, rootAttrs);
}
return root;
}
public final <K, V> Region<K, V> createRegion(final String name, final String rootName,
final RegionAttributes<K, V> attributes) throws CacheException {
Region<K, V> root = getRootRegion(rootName);
if (root == null) {
// don't put listeners on root region
AttributesFactory<K, V> attributesFactory = new AttributesFactory<>(attributes);
ExpirationAttributes expiration = ExpirationAttributes.DEFAULT;
attributesFactory.setCacheLoader(null);
attributesFactory.setCacheWriter(null);
attributesFactory.setPoolName(null);
attributesFactory.setPartitionAttributes(null);
attributesFactory.setRegionTimeToLive(expiration);
attributesFactory.setEntryTimeToLive(expiration);
attributesFactory.setRegionIdleTimeout(expiration);
attributesFactory.setEntryIdleTimeout(expiration);
RegionAttributes<K, V> rootAttrs = attributesFactory.create();
root = createRootRegion(rootName, rootAttrs);
}
return root.createSubregion(name, attributes);
}
public final <K, V> Region<K, V> getRootRegion() {
return getRootRegion("root");
}
public final <K, V> Region<K, V> getRootRegion(final String rootName) {
return getCache().getRegion(rootName);
}
protected final <K, V> Region<K, V> createRootRegion(final RegionAttributes<K, V> attributes)
throws RegionExistsException, TimeoutException {
return createRootRegion("root", attributes);
}
public final <K, V> Region<K, V> createRootRegion(final String rootName,
final RegionAttributes<K, V> attributes)
throws RegionExistsException, TimeoutException {
return getCache().createRegion(rootName, attributes);
}
public final <K, V> Region<K, V> createExpiryRootRegion(final String rootName,
final RegionAttributes<K, V> attributes) throws RegionExistsException, TimeoutException {
System.setProperty(LocalRegion.EXPIRY_MS_PROPERTY, "true");
try {
return createRootRegion(rootName, attributes);
} finally {
System.clearProperty(LocalRegion.EXPIRY_MS_PROPERTY);
}
}
/**
* @deprecated Please use {@link IgnoredException#addIgnoredException(String)} instead.
*/
@Deprecated
public final CacheSerializableRunnable addExceptionTag1(final String exceptionStringToIgnore) {
return new CacheSerializableRunnable("addExceptionTag") {
@Override
public void run2() {
getCache().getLogger().info(
"<ExpectedException action=add>" + exceptionStringToIgnore + "</ExpectedException>");
}
};
}
/**
* TODO: delete removeExceptionTag1 method
*
* @deprecated Please use {@link IgnoredException#addIgnoredException(String)} instead.
*/
@Deprecated
public final CacheSerializableRunnable removeExceptionTag1(final String exceptionStringToIgnore) {
return new CacheSerializableRunnable("removeExceptionTag") {
@Override
public void run2() throws CacheException {
getCache().getLogger().info(
"<ExpectedException action=remove>" + exceptionStringToIgnore + "</ExpectedException>");
}
};
}
public static final File getDiskDir() {
int vmNum = VM.getCurrentVMNum();
File dir = new File("diskDir", "disk" + String.valueOf(vmNum)).getAbsoluteFile();
dir.mkdirs();
return dir;
}
/**
* Return a set of disk directories for persistence tests. These directories will be automatically
* cleaned up on test case closure.
*/
public static final File[] getDiskDirs() {
return new File[] {getDiskDir()};
}
/**
* Used to generate a cache.xml. Basically just a {@code CacheCreation} with a few more methods
* implemented.
*/
private static class TestCacheCreation extends CacheCreation {
private boolean closed = false;
@Override
public void close() {
this.closed = true;
}
@Override
public boolean isClosed() {
return this.closed;
}
}
}