Java tutorial
/* * 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 java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Map; import java.util.Properties; import org.apache.commons.io.FileUtils; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheException; import org.apache.geode.cache.CacheExistsException; import org.apache.geode.cache.CacheFactory; import org.apache.geode.cache.CacheTransactionManager; 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.test.dunit.Assert; import org.apache.geode.test.dunit.IgnoredException; import org.apache.geode.test.dunit.Invoke; import org.apache.geode.test.dunit.LogWriterUtils; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.Wait; import org.apache.geode.test.dunit.WaitCriterion; import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase; /** * This class is the base class for all distributed tests using JUnit 4 that require the creation of * a {@link Cache}. */ public abstract class JUnit4CacheTestCase extends JUnit4DistributedTestCase implements CacheTestFixture { /** * 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. */ private 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; } catch (CacheExistsException e) { Assert.fail("the cache already exists", e); // TODO: remove error handling } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { Assert.fail("Checked exception while initializing cache??", ex); } 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; } catch (CacheExistsException e) { Assert.fail("the cache already exists", e); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { Assert.fail("Checked exception while initializing cache??", ex); } 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 final 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) { File file = new File(name + "-cache.xml"); try { PrintWriter printWriter = new PrintWriter(new FileWriter(file), true); CacheXmlGenerator.generate(cache, printWriter); printWriter.close(); } catch (IOException ex) { Assert.fail("IOException during cache.xml generation to " + file, ex); } // TODO: System.setProperty(GEMFIRE_PREFIX + CACHE_XML_FILE, file.getAbsolutePath()); cache = null; GemFireCacheImpl.testCacheXml = file; try { createCache(); } 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) { synchronized (JUnit4CacheTestCase.class) { File dir = new File(root, "XML_" + xmlVersion); dir.mkdirs(); File file = new File(dir, name + ".xml"); try { PrintWriter printWriter = new PrintWriter(new FileWriter(file), true); CacheXmlGenerator.generate(cache, printWriter, useSchema, xmlVersion); printWriter.close(); } catch (IOException ex) { // TODO: remove error handling Assert.fail("IOException during cache.xml generation to " + file, ex); } 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()) { Wait.waitForCriterion(new WaitCriterion() { // TODO: replace with Awaitility @Override public boolean done() { return gemFireCache.isClosed(); } @Override public String description() { return "waiting for cache to close"; } }, 30 * 1000, 300, true); } 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()) { Wait.waitForCriterion(new WaitCriterion() { // TODO: replace with Awaitility @Override public boolean done() { return gemFireCache.isClosed(); } @Override public String description() { return "waiting for cache to close"; } }, 30 * 1000, 300, true); } if (cache == null || cache.isClosed()) { cache = null; disconnectFromDS(); cache = (InternalCache) factory.create(); } if (cache != null) { IgnoredException.addIgnoredException("java.net.ConnectException"); } 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(() -> closeCache()); } @Override public final void preTearDown() throws Exception { preTearDownCacheTestCase(); tearDownCacheTestCase(); postTearDownCacheTestCase(); } private final void tearDownCacheTestCase() { remoteTearDown(); Invoke.invokeInEveryVM(() -> 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. */ protected static final synchronized void remoteTearDown() { try { DistributionMessageObserver.setInstance(null); destroyRegions(cache); } finally { try { closeCache(); } finally { try { cleanDiskDirs(); } catch (Exception e) { LogWriterUtils.getLogWriter().error("Error cleaning disk dirs", e); } } } } /** * Returns a region with the given name and attributes. */ public final Region createRegion(final String name, final RegionAttributes attributes) throws CacheException { return createRegion(name, "root", attributes); } public final Region createRegion(final String name, final String rootName, final RegionAttributes attributes) throws CacheException { Region root = getRootRegion(rootName); if (root == null) { // don't put listeners on root region AttributesFactory 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 rootAttrs = attributesFactory.create(); root = createRootRegion(rootName, rootAttrs); } return root.createSubregion(name, attributes); } public final Region getRootRegion() { return getRootRegion("root"); } public final Region getRootRegion(final String rootName) { return getCache().getRegion(rootName); } protected final Region createRootRegion(final RegionAttributes attributes) throws RegionExistsException, TimeoutException { return createRootRegion("root", attributes); } public final Region createRootRegion(final String rootName, final RegionAttributes attributes) throws RegionExistsException, TimeoutException { return getCache().createRegion(rootName, attributes); } public final Region createExpiryRootRegion(final String rootName, final RegionAttributes attributes) throws RegionExistsException, TimeoutException { System.setProperty(LocalRegion.EXPIRY_MS_PROPERTY, "true"); try { return createRootRegion(rootName, attributes); } finally { System.clearProperty(LocalRegion.EXPIRY_MS_PROPERTY); } } /** * TODO: delete addExceptionTag1 method * * @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() }; } public static final void cleanDiskDirs() throws IOException { FileUtils.deleteQuietly(getDiskDir()); Arrays.stream(new File(".").listFiles()).forEach(file -> deleteBACKUPDiskStoreFile(file)); } private static void deleteBACKUPDiskStoreFile(final File file) { if (file.getName().startsWith("BACKUPDiskStore-")) { FileUtils.deleteQuietly(file); } } /** * 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; } } }