blob: c145a3757136b9cf6b4411eac32785cc049098a6 [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.cache;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.geode.InternalGemFireError;
import org.apache.geode.SystemFailure;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.client.PoolManager;
import org.apache.geode.cache.client.internal.ServerRegionProxy;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.geode.cache.wan.GatewaySender;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.cache.DistributedRegion;
import org.apache.geode.internal.cache.DynamicRegionAttributes;
import org.apache.geode.internal.cache.DynamicRegionFactoryImpl;
import org.apache.geode.internal.cache.EntryEventImpl;
import org.apache.geode.internal.cache.EnumListenerEvent;
import org.apache.geode.internal.cache.EvictionAttributesImpl;
import org.apache.geode.internal.cache.InitialImageOperation;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.InternalRegion;
import org.apache.geode.internal.cache.InternalRegionArguments;
import org.apache.geode.internal.cache.LocalRegion;
import org.apache.geode.internal.cache.RegionEntry;
import org.apache.geode.internal.cache.RegionEventImpl;
import org.apache.geode.security.GemFireSecurityException;
/**
* DynamicRegionFactory provides a distributed region creation service. Any other member of the
* GemFire DistributedSystem that has created an instance of this class will automatically
* instantiate regions created through the factory from anywhere else in the DistributedSystem.
* <p>
* <p>
* Instructions for Use:<br>
* <ul>
* <li>If your application is a client in a client/server installation, either specify the pool name
* in the {@link DynamicRegionFactory.Config} you'll use to create a DynamicRegionFactory <i>or</i>
* specify it in a dynamic-region-factory element in your cache.xml.
*
* <li>Before you've created a GemFire Cache in your application, add a line of code as follows:<br>
*
* <pre>
* {
* DynamicRegionFactory factory = DynamicRegionFactory.get();
* factory.open(config);
* }
* </pre>
*
* <pre>
* {
* DynamicRegionFactory myFactoryHandle = DynamicRegionFactory.get().open(config);
* }
* </pre>
*
* or just use a dynamic-region-factory element in the cache.xml.
*
* <li>Create the GemFire Cache. During cache creation, the list of dynamic Regions will either be
* discovered by recovering their names from disk (see
* {@link DynamicRegionFactory.Config#persistBackup}) or from other members of the distributed
* system. These dynamic Regions will be created before Cache creation completes.
*
* <li>Thereafter, when you want to create dynamic distributed Regions, create them using the
* {@link #createDynamicRegion}. Regions created with the factory will inherit their
* RegionAttributes from their parent Region, though you can override callbacks when you configure
* the factory.
*
* <p>
* All other instances of GemFire across the distributed system that instantiate and open a
* DynamicRegionFactory will also get the dynamic distributed Regions.
*
* <li>Non-dynamic parent Regions should be declared in cache.xml so that they can be created before
* the dynamic Region factory goes active and starts creating Regions. You will have cache creation
* problems if this isn't done.
*
* <li>A DynamicRegionListener can be registered before open is called and before cache creation so
* that the listener will be called if dynamic Regions are created during cache creation.
*
* </ul>
* <p>
* Saving the factory on disk: If {@link DynamicRegionFactory.Config#persistBackup} is configured
* for the factory, dynamic Region information is written to disk for recovery. By default the
* current directory is used for this information. The {@link DynamicRegionFactory.Config#diskDir}
* can be used to change this default.
* <p>
* Registering interest in cache server information: The
* {@link DynamicRegionFactory.Config#registerInterest} setting determines whether clients will
* register interest in server keys or not. You will generally want this to be turned on so that
* clients will see updates made to servers. In server processes, DynamicRegionFactory forces use of
* NotifyBySubscription.
* </ul>
* <p>
* Notes:
* <ul>
* <li>DynamicRegionFactories in non-client VMs must not be configured with a pool.
* <li>If {@link #open()} is called before cache creation and the cache.xml has a
* dynamic-region-factory element then the cache.xml will override the open call's configuration.
*
* <li>Since the RegionAttributes of a dynamically created Region are copied from the parent Region,
* any callbacks, ({@link CacheListener}, {@link CacheWriter}, and {@link CacheLoader} are shared by
* the parent and all its dynamic children so make sure the callback is thread-safe and that its
* {@link CacheCallback#close} implementation does not stop it from functioning. However the
* products EvictionAlgorithm instances will be cloned so that each dynamic Region has its own
* callback.
*
* <li>The root Region name "DynamicRegions" is reserved. The factory creates a root Region of that
* name and uses it to keep track of what dynamic Regions exist. Applications should not directly
* access this Region; instead use the methods on this factory.
* </ul>
*
* @since GemFire 4.3
* @deprecated This class is deprecated. Use {@link FunctionService} to create regions on other
* members instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
public abstract class DynamicRegionFactory {
public static final String DYNAMIC_REGION_LIST_NAME = "__DynamicRegions";
private Region dynamicRegionList = null;
/**
* This controls the delay introduced to try and avoid any race conditions between propagation of
* newly created Dynamic Regions and the Entries put into them.
*/
private static final long regionCreateSleepMillis =
Long.getLong("DynamicRegionFactory.msDelay", 250);
@MakeNotStatic
private static final DynamicRegionFactory singleInstance = new DynamicRegionFactoryImpl();
InternalCache cache = null;
private Config config = null;
/** The region listeners registered on this DynamicRegionFactory */
@MakeNotStatic
private static volatile List regionListeners = Collections.emptyList();
private static final Object regionListenerLock = new Object();
/**
* Opens the DynamicRegionFactory with default settings.
*/
public void open() {
open(new Config());
}
/**
* Opens the factory with the given settings. This should be sent to the factory before creating a
* cache. The cache will otherwise open a factory with default settings. This does not need to be
* sent if the cache.xml declares the use of dynamic regions.
*
* @param conf the configuration for this factory.
*/
public void open(Config conf) {
this.config = new Config(conf);
}
/**
* Closes the dynamic region factory, disabling any further creation or destruction of dynamic
* regions in this cache.
*/
protected void doClose() {
this.config = null;
this.cache = null;
}
/**
* Returns true if dynamic region factory is open; false if closed.
*/
public boolean isOpen() {
return getConfig() != null;
}
/**
* Returns true if this factory is open and can produce dynamic regions. Factories are only active
* after their cache has been created.
*/
public boolean isActive() {
return isOpen() && this.cache != null;
}
/**
* Returns true if dynamic region factory is closed.
*/
public boolean isClosed() {
return !isOpen();
}
/**
* Returns the configuration for this factory. Returns null if the factory is closed;
*/
public Config getConfig() {
if (this.config == null)
return null;
else
return new Config(this.config);
}
public static boolean regionIsDynamicRegionList(String regionPath) {
return regionPath != null && regionPath.equals('/' + DYNAMIC_REGION_LIST_NAME);
}
/**
* The method is for internal use only. It is called implicitly during cache creation.
* <p>
* This method is called internally during cache initialization at the correct time. Initialize
* the factory with a GemFire Cache. We create the metadata Region which holds all our dynamically
* created regions.
*
* @param theCache The GemFire {@code Cache}
*/
protected void doInternalInit(InternalCache theCache) throws CacheException {
if (isClosed()) {
// DynamicRegions are not enabled in this vm. Just return.
return;
}
try {
this.cache = theCache;
this.dynamicRegionList = theCache.getRegion(DYNAMIC_REGION_LIST_NAME);
final boolean isClient = this.config.getPoolName() != null;
if (this.dynamicRegionList == null) {
InternalRegionArguments ira = new InternalRegionArguments().setDestroyLockFlag(true)
.setInternalRegion(true).setSnapshotInputStream(null).setImageTarget(null);
AttributesFactory af = new AttributesFactory();
if (this.config.getPersistBackup()) {
af.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE);
af.setDiskWriteAttributes(new DiskWriteAttributesFactory().create());
if (this.config.getDiskDir() != null) {
af.setDiskDirs(new File[] {this.config.getDiskDir()});
}
}
if (isClient) {
af.setScope(Scope.LOCAL);
af.setDataPolicy(DataPolicy.NORMAL);
af.setStatisticsEnabled(true);
String cpName = this.config.getPoolName();
if (cpName != null) {
Pool cp = PoolManager.find(cpName);
if (cp == null) {
throw new IllegalStateException(
"Invalid pool name specified. This pool is not registered with the cache: "
+ cpName);
} else {
if (!cp.getSubscriptionEnabled()) {
throw new IllegalStateException(
"The client pool of a DynamicRegionFactory must be configured with queue-enabled set to true.");
}
af.setPoolName(cpName);
}
}
ira.setInternalMetaRegion(new LocalMetaRegion(af.create(), ira));
} else {
af.setScope(Scope.DISTRIBUTED_ACK);
af.setValueConstraint(DynamicRegionAttributes.class);
if (!this.config.getPersistBackup()) { // if persistBackup, the data policy has already
// been set
af.setDataPolicy(DataPolicy.REPLICATE);
}
for (GatewaySender gs : this.cache.getGatewaySenders()) {
if (!gs.isParallel())
af.addGatewaySenderId(gs.getId());
}
ira.setInternalMetaRegion(new DistributedMetaRegion(af.create())); // bug fix 35432
}
try {
this.dynamicRegionList =
theCache.createVMRegion(DYNAMIC_REGION_LIST_NAME, af.create(), ira);
} catch (IOException e) {
// only if loading snapshot, not here
throw new InternalGemFireError(
"unexpected exception", e);
} catch (ClassNotFoundException e) {
// only if loading snapshot, not here
throw new InternalGemFireError(
"unexpected exception", e);
}
if (isClient) {
this.dynamicRegionList.registerInterest("ALL_KEYS");
}
if (theCache.getLogger().fineEnabled()) {
theCache.getLogger().fine("Created dynamic region: " + this.dynamicRegionList);
}
} else {
if (theCache.getLogger().fineEnabled()) {
theCache.getLogger().fine("Retrieved dynamic region: " + this.dynamicRegionList);
}
}
createDefinedDynamicRegions();
} catch (CacheException e) {
theCache.getLogger().warning(
"Error initializing DynamicRegionFactory", e);
throw e;
}
}
/**
* This creates Dynamic Regions that already exist in other publishing processes
*/
private void createDefinedDynamicRegions() throws CacheException {
// TODO: perhaps add some logic here to avoid the possibility of synchronization issues
Set set = this.dynamicRegionList.entrySet(false);
Iterator iterator = set.iterator();
SortedMap sorted = new TreeMap();
// sort by region name before creating (bug 35528)
while (iterator.hasNext()) {
Region.Entry e = (Region.Entry) iterator.next();
DynamicRegionAttributes dda = (DynamicRegionAttributes) e.getValue();
sorted.put(dda.rootRegionName + '/' + dda.name, dda);
}
iterator = sorted.values().iterator();
while (iterator.hasNext()) {
DynamicRegionAttributes dda = (DynamicRegionAttributes) iterator.next();
doBeforeRegionCreated(dda.rootRegionName, dda.name);
Region region = createDynamicRegionImpl(dda.rootRegionName, dda.name, false);
doAfterRegionCreated(region, false, false, null);
}
}
/**
* Returns the {@code DynamicRegionFactory} singleton instance.
*
* @return the {@code DynamicRegionFactory} singleton instance
*/
public static DynamicRegionFactory get() {
return singleInstance;
}
/**
* Registers a {@code DynamicRegionListener} for callbacks.
*
* @param listener The {@code DynamicRegionListener} to be registered
*/
public void registerDynamicRegionListener(DynamicRegionListener listener) {
synchronized (regionListenerLock) {
List oldListeners = regionListeners;
if (!oldListeners.contains(listener)) {
List newListeners = new ArrayList(oldListeners);
newListeners.add(listener);
regionListeners = newListeners;
}
}
}
/**
* Unregisters a {@code DynamicRegionListener} for callbacks.
*
* @param listener The {@code DynamicRegionListener} to be unregistered
*/
public void unregisterDynamicRegionListener(DynamicRegionListener listener) {
synchronized (regionListenerLock) {
List oldListeners = regionListeners;
if (oldListeners.contains(listener)) {
List newListeners = new ArrayList(oldListeners);
if (newListeners.remove(listener)) {
regionListeners = newListeners;
}
}
}
}
private void doBeforeRegionCreated(String parentRegion, String regionName) {
for (Object regionListener : regionListeners) {
DynamicRegionListener listener = (DynamicRegionListener) regionListener;
try {
listener.beforeRegionCreate(parentRegion, regionName);
} catch (VirtualMachineError err) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
} catch (Throwable t) {
// Whenever you catch Error or Throwable, you must also
// catch VirtualMachineError (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
this.cache.getLogger().warning(
String.format("DynamicRegionListener %s threw exception on beforeRegionCreated",
listener),
t);
}
}
}
private void doAfterRegionCreated(Region region, boolean distributed, boolean isOriginRemote,
DistributedMember mbr) {
RegionEvent event =
new RegionEventImpl(region, Operation.REGION_CREATE, null, isOriginRemote, getMember(mbr));
for (Object regionListener : regionListeners) {
DynamicRegionListener listener = (DynamicRegionListener) regionListener;
try {
listener.afterRegionCreate(event /* region */);
} catch (VirtualMachineError err) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
} catch (Throwable t) {
// Whenever you catch Error or Throwable, you must also
// catch VirtualMachineError (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
this.cache.getLogger().warning(
String.format("DynamicRegionListener %s threw exception on afterRegionCreated",
listener),
t);
}
}
}
private void doBeforeRegionDestroyed(Region region, boolean distributed, boolean isOriginRemote,
boolean expiration, DistributedMember mbr) {
final Operation op;
if (!distributed && !isOriginRemote) {
op = expiration ? Operation.REGION_EXPIRE_LOCAL_DESTROY : Operation.REGION_LOCAL_DESTROY;
} else {
op = expiration ? Operation.REGION_EXPIRE_DESTROY : Operation.REGION_DESTROY;
}
RegionEvent event = new RegionEventImpl(region, op, null, isOriginRemote, getMember(mbr));
for (Object regionListener : regionListeners) {
DynamicRegionListener listener = (DynamicRegionListener) regionListener;
try {
listener.beforeRegionDestroy(event /* fullRegionName */);
} catch (VirtualMachineError err) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
} catch (Throwable t) {
// Whenever you catch Error or Throwable, you must also
// catch VirtualMachineError (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
this.cache.getLogger().warning(
String.format("DynamicRegionListener %s threw exception on beforeRegionDestroyed",
listener),
t);
}
}
}
private void doAfterRegionDestroyed(Region region, boolean distributed, boolean isOriginRemote,
boolean expiration, DistributedMember mbr) {
final Operation op;
if (!distributed && !isOriginRemote) {
op = expiration ? Operation.REGION_EXPIRE_LOCAL_DESTROY : Operation.REGION_LOCAL_DESTROY;
} else {
op = expiration ? Operation.REGION_EXPIRE_DESTROY : Operation.REGION_DESTROY;
}
RegionEvent event = new RegionEventImpl(region, op, null, isOriginRemote, getMember(mbr));
for (Object regionListener : regionListeners) {
DynamicRegionListener listener = (DynamicRegionListener) regionListener;
try {
listener.afterRegionDestroy(event /* fullRegionName */);
} catch (VirtualMachineError err) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
} catch (Throwable t) {
// Whenever you catch Error or Throwable, you must also
// catch VirtualMachineError (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
this.cache.getLogger().warning(
String.format("DynamicRegionListener %s threw exception on afterRegionDestroyed",
listener),
t);
}
}
}
/** return the argument, or if null the DistributedMember id of this vm */
private DistributedMember getMember(DistributedMember mbr) {
if (mbr == null) {
return this.cache.getInternalDistributedSystem().getDistributedMember();
} else {
return null;
}
}
/**
* Creates the dynamic Region in the local cache and distributes the creation to other caches.
*
* @param parentRegionName the new region is created as a subregion of the region having this path
* @param regionName the name of the new subregion
* @return the {@code Region} created
*/
public Region createDynamicRegion(String parentRegionName, String regionName)
throws CacheException {
if (isClosed()) {
throw new IllegalStateException("Dynamic region factory is closed");
}
doBeforeRegionCreated(parentRegionName, regionName);
Region region = createDynamicRegionImpl(parentRegionName, regionName, true);
doAfterRegionCreated(region, false, false, null);
return region;
}
/**
* Destroys the dynamic Region in the local cache and distributes the destruction to other caches.
*
* @param fullRegionName The full path of the {@code Region} to be dynamically destroyed
* @throws RegionDestroyedException if the dynamic region was never created or has already been
* destroyed
*/
public void destroyDynamicRegion(String fullRegionName) throws CacheException {
if (!this.dynamicRegionList.containsKey(fullRegionName)) {
throw new RegionDestroyedException(
String.format("Dynamic region %s has not been created.",
fullRegionName),
fullRegionName);
}
if (isClosed()) {
throw new IllegalStateException("Dynamic region factory is closed");
}
// Retrieve the region to destroy
Region region = this.cache.getRegion(fullRegionName);
if (region != null) {
DistributedMember mbr = getMember(null);
doBeforeRegionDestroyed(region, false, false, false, mbr);
// Locally destroy the region. Let the dynamicRegionList handle distributing
// the destroy.
region.localDestroyRegion();
destroyDynamicRegionImpl(fullRegionName);
doAfterRegionDestroyed(region, false, false, false, mbr);
} else {
// make sure meta region is cleaned up locally and remotely
destroyDynamicRegionImpl(fullRegionName);
}
}
private Region createDynamicRegionImpl(String parentRegionName, String newRegionName,
boolean addEntry) throws CacheException {
Region parentRegion = this.cache.getRegion(parentRegionName);
if (parentRegion == null) {
String errMsg =
String.format("Error -- Could not find a region named: '%s'",
parentRegionName);
RegionDestroyedException e = new RegionDestroyedException(errMsg, parentRegionName);
this.cache.getLogger().warning(
String.format("Error -- Could not find a region named: '%s'",
parentRegionName),
e);
throw e;
}
// Create RegionAttributes by inheriting from the parent
RegionAttributes rra = parentRegion.getAttributes();
AttributesFactory af = new AttributesFactory(rra);
EvictionAttributes ev = rra.getEvictionAttributes();
if (ev != null && ev.getAlgorithm().isLRU()) {
EvictionAttributes rev = new EvictionAttributesImpl(ev);
af.setEvictionAttributes(rev);
}
// for internal testing, until partitioned regions support subclasses or
// DynamicRegion implementation is redone to not inherit attrs from parent
// regions
if (newRegionName.endsWith("_PRTEST_")) {
af.setPartitionAttributes(new PartitionAttributesFactory().create());
}
RegionAttributes newRegionAttributes = af.create();
Region newRegion;
try {
newRegion = parentRegion.createSubregion(newRegionName, newRegionAttributes);
this.cache.getLogger().fine("Created dynamic region " + newRegion);
} catch (RegionExistsException ex) {
// a race condition exists that can cause this so just fine log it
this.cache.getLogger().fine(
"DynamicRegion " + newRegionName + " in parent " + parentRegionName + " already existed");
newRegion = ex.getRegion();
}
if (addEntry) {
DynamicRegionAttributes dra = new DynamicRegionAttributes();
dra.name = newRegionName;
dra.rootRegionName = parentRegion.getFullPath();
if (this.cache.getLogger().fineEnabled()) {
this.cache.getLogger()
.fine("Putting entry into dynamic region list at key: " + newRegion.getFullPath());
}
this.dynamicRegionList.put(newRegion.getFullPath(), dra);
}
if (this.config.getRegisterInterest()) {
ServerRegionProxy proxy = ((InternalRegion) newRegion).getServerProxy();
if (proxy != null) {
if (((Pool) proxy.getPool()).getSubscriptionEnabled()) {
try {
newRegion.registerInterest("ALL_KEYS");
} catch (GemFireSecurityException ex) {
// Ignore security exceptions here
this.cache.getSecurityLogger().warning(
String.format(
"Exception when registering interest for all keys in dynamic region [%s]. %s",
new Object[] {newRegion.getFullPath(), ex}));
}
}
}
}
if (regionCreateSleepMillis > 0) {
try {
Thread.sleep(regionCreateSleepMillis);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
}
}
if (this.cache.getLogger().fineEnabled()) {
this.cache.getLogger().fine("Created Dynamic Region " + newRegion.getFullPath());
}
return newRegion;
}
private void destroyDynamicRegionImpl(String fullRegionName) throws CacheException {
// Destroy the entry in the dynamicRegionList
try {
if (this.cache.getLogger().fineEnabled()) {
this.cache.getLogger()
.fine("Destroying entry from dynamic region list at key: " + fullRegionName);
}
this.dynamicRegionList.destroy(fullRegionName);
} catch (CacheException e) {
this.cache.getLogger().warning(
String.format("Error destroying Dynamic Region '%s'", fullRegionName),
e);
throw e;
}
if (this.cache.getLogger().fineEnabled()) {
this.cache.getLogger().fine("Destroyed Dynamic Region " + fullRegionName);
}
}
/**
* Configuration for dynamic region factory. The default attributes are:
* <ul>
* <li>diskDir: {@code null}
* <li>poolName: {@code null}
* <li>persistBackup: {@code true}
* <li>registerInterest: {@code true}
* </ul>
*
* @since GemFire 4.3
*/
public static class Config {
private static final boolean DISABLE_REGISTER_INTEREST =
Boolean.getBoolean("DynamicRegionFactory.disableRegisterInterest");
private static final boolean DISABLE_PERSIST_BACKUP =
Boolean.getBoolean("DynamicRegionFactory.disablePersistence");
/** Causes the factory to be persisted on disk. See {@link #diskDir} */
public final boolean persistBackup;
/** The directory where the factory's {@link #persistBackup} files are placed */
public final File diskDir;
/**
* Causes regions created by the factory to register interest in all keys in a corresponding
* server cache region
*/
public final boolean registerInterest;
/**
* The ${link Pool} to be used by a client factory to communicate with the server-side factory.
*/
public final String poolName;
/**
* Creates a configuration with the default attributes.
*/
public Config() {
this(null, null, !DISABLE_PERSIST_BACKUP, !DISABLE_REGISTER_INTEREST);
}
/**
* Creates a configuration with defaults and the given diskDir and poolName.
*/
public Config(File diskDir, String poolName) {
this(diskDir, poolName, !DISABLE_PERSIST_BACKUP, !DISABLE_REGISTER_INTEREST);
}
/**
* Creates a configuration with the given attributes
*/
public Config(File diskDir, String poolName, boolean persistBackup, boolean registerInterest) {
this.registerInterest = registerInterest;
this.persistBackup = persistBackup;
this.diskDir = diskDir;
this.poolName = poolName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((diskDir == null) ? 0 : diskDir.hashCode());
result = prime * result + (persistBackup ? 1231 : 1237);
result = prime * result + ((poolName == null) ? 0 : poolName.hashCode());
result = prime * result + (registerInterest ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Config other = (Config) obj;
if (diskDir == null) {
if (other.diskDir != null)
return false;
} else if (!diskDir.equals(other.diskDir))
return false;
if (persistBackup != other.persistBackup)
return false;
if (poolName == null) {
if (other.poolName != null)
return false;
} else if (!poolName.equals(other.poolName))
return false;
if (registerInterest != other.registerInterest)
return false;
return true;
}
/**
* Returns true if the factory is persisted to disk; false if not.
*/
public boolean getPersistBackup() {
return this.persistBackup;
}
/**
* Returns true if the region will register interest in all keys of a corresponding server cache
* region
*/
public boolean getRegisterInterest() {
return this.registerInterest;
}
/**
* Returns the disk directory that the dynamic region factory data will be written to. Returns
* null if no directory has been specified. The diskDir is only used if {@code persistBackup} is
* true.
*/
public File getDiskDir() {
return this.diskDir;
}
/**
* Returns the name of the {@link Pool} associated with the dynamic region factory. Returns null
* if there is no connection pool for dynamic regions.
*/
public String getPoolName() {
return this.poolName;
}
/** create a new Config with settings from another one */
Config(Config conf) {
this.diskDir = conf.diskDir;
this.persistBackup = conf.persistBackup;
this.registerInterest = conf.registerInterest;
this.poolName = conf.poolName;
}
}
protected void buildDynamicRegion(EntryEvent event) {
if (!DynamicRegionFactory.this.isOpen())
return;
// Ignore the callback if it originated in this process (because the region
// will already have been created) and the event is not a client event
if (!event.isOriginRemote() && !event.hasClientOrigin())
return;
//
DynamicRegionAttributes dra = (DynamicRegionAttributes) event.getNewValue();
String parentRegionName = dra.rootRegionName;
String newRegionName = dra.name;
try {
doBeforeRegionCreated(parentRegionName, newRegionName);
Region region = createDynamicRegionImpl(parentRegionName, newRegionName, false);
doAfterRegionCreated(region, true, true, event.getDistributedMember());
} catch (Exception e) {
cache.getLogger().warning(
String.format("Error attempting to locally create Dynamic Region: %s",
newRegionName),
e);
}
}
protected void razeDynamicRegion(EntryEvent event) {
if (!DynamicRegionFactory.this.isOpen())
return;
// Because CacheClientUpdater calls localDestroy we need to allow
// "local" events. If this is a true local then c.getRegion will return
// null and this code will do nothing.
// When bug 35644 fixed the following "if" can be uncommented.
// Ignore the callback if it originated in this process (because the region
// will already have been destroyed)
String fullRegionName = (String) event.getKey();
Region drRegion = cache.getRegion(fullRegionName);
if (drRegion != null) {
try {
doBeforeRegionDestroyed(drRegion, true, event.getOperation().isDistributed(),
event.getOperation().isExpiration(), event.getDistributedMember());
drRegion.localDestroyRegion();
doAfterRegionDestroyed(drRegion, true, event.getOperation().isDistributed(),
event.getOperation().isExpiration(), event.getDistributedMember());
} catch (Exception e) {
cache.getLogger().warning(
String.format("Error attempting to locally destroy Dynamic Region: %s",
fullRegionName),
e);
}
}
}
// Introduced to keep symmetry with DistributedMetaRegion and potentially provide improved control
// of
// the meta data
private class LocalMetaRegion extends LocalRegion {
protected LocalMetaRegion(RegionAttributes attrs, InternalRegionArguments ira) {
super(DYNAMIC_REGION_LIST_NAME, attrs, null, DynamicRegionFactory.this.cache, ira,
DynamicRegionFactory.this.cache.getStatisticsClock());
Assert.assertTrue(attrs.getScope().isLocal());
}
// This is an internal uses only region
@Override
public boolean isSecret() {
return true;
}
@Override
public boolean isCopyOnRead() {
return false;
}
// //@override event tracker not needed for this type of region
// void initEventTracker() {
// }
// while internal, its contents should be communicated with bridge clients
@Override
public boolean shouldNotifyBridgeClients() {
return getCache().getCacheServers().size() > 0;
}
// Over-ride the super behavior to perform the destruction of the dynamic region
@Override
public void invokeDestroyCallbacks(EnumListenerEvent eventType, EntryEventImpl event,
boolean callDispatchEventsCallback, boolean notifyGateways) {
Assert.assertTrue(eventType.equals(EnumListenerEvent.AFTER_DESTROY));
// Notify bridge clients (if this is a BridgeServer)
event.setEventType(eventType);
notifyBridgeClients(event);
// Notify GatewayHub (if this region participates in a Gateway)
if (notifyGateways) {
notifyGatewaySender(eventType, event);
}
// Destroy the dynamic region after the clients and gateways have been notified
razeDynamicRegion(event);
}
// Over-ride the super behavior to perform the creation of the dynamic region
@Override
public long basicPutPart2(EntryEventImpl event, RegionEntry entry, boolean isInitialized,
long lastModified, boolean clearConflict) {
boolean isCreate = event.getOperation().isCreate();
boolean set = false;
if (isCreate && !event.callbacksInvoked()) {
// don't notify clients until all peers have created the region so that
// their register-interest operations will be sure to find data
event.callbacksInvoked(true);
set = true;
}
long result = super.basicPutPart2(event, entry, isInitialized, lastModified, clearConflict);
if (set) {
event.callbacksInvoked(false);
}
if (isCreate) {
buildDynamicRegion(event);
}
return result;
}
// The dynamic-region meta-region needs to tell clients about the event
// after all servers have created the region so that register-interest
// will work correctly
@Override
public void basicPutPart3(EntryEventImpl event, RegionEntry entry, boolean isInitialized,
long lastModified, boolean invokeCallbacks, boolean ifNew, boolean ifOld,
Object expectedOldValue, boolean requireOldValue) {
super.basicPutPart3(event, entry, isInitialized, lastModified, invokeCallbacks, ifNew, ifOld,
expectedOldValue, requireOldValue);
// this code is copied from LocalRegion.basicPutPart2
invokeCallbacks &= !entry.isTombstone(); // put() is creating a tombstone
if (invokeCallbacks) {
boolean doCallback = false;
if (isInitialized) {
if (event.isGenerateCallbacks()) {
doCallback = true;
}
}
if (doCallback) {
notifyGatewaySender(event.getOperation().isUpdate() ? EnumListenerEvent.AFTER_UPDATE
: EnumListenerEvent.AFTER_CREATE, event);
// Notify listeners
if (!event.isBulkOpInProgress()) {
try {
entry.dispatchListenerEvents(event);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
getCancelCriterion().checkCancelInProgress(null);
}
}
}
}
}
}
// Part of the fix for bug 35432, which required a change to the
// distribution and notification order on the BridgeServer
private class DistributedMetaRegion extends DistributedRegion {
protected DistributedMetaRegion(RegionAttributes attrs) {
super(DYNAMIC_REGION_LIST_NAME, attrs, null, DynamicRegionFactory.this.cache,
new InternalRegionArguments(), DynamicRegionFactory.this.cache.getStatisticsClock());
}
// This is an internal uses only region
@Override
public boolean isSecret() {
return true;
}
// //@override event tracker not needed for this type of region
// void initEventTracker() {
// }
@Override
public boolean isCopyOnRead() {
return false;
}
// while internal, its contents should be communicated with bridge clients
@Override
public boolean shouldNotifyBridgeClients() {
return getCache().getCacheServers().size() > 0;
}
// Over-ride the super behavior to perform the destruction of the dynamic region
//
@Override
public void invokeDestroyCallbacks(EnumListenerEvent eventType, EntryEventImpl event,
boolean callDispatchEventsCallback, boolean notifyGateways) {
Assert.assertTrue(eventType.equals(EnumListenerEvent.AFTER_DESTROY));
// Notify bridge clients (if this is a BridgeServer)
event.setEventType(eventType);
notifyBridgeClients(event);
// Notify GatewayHub (if this region participates in a Gateway)
if (notifyGateways) {
notifyGatewaySender(eventType, event);
}
// Destroy the dynamic region after the clients and gateways have been notified
razeDynamicRegion(event);
}
@Override
public long basicPutPart2(EntryEventImpl event, RegionEntry entry, boolean isInitialized,
long lastModified, boolean clearConflict) {
boolean isCreate = event.getOperation().isCreate();
boolean set = false;
if (isCreate && !event.callbacksInvoked()) {
// don't notify clients until all peers have created the region so that
// their register-interest operations will be sure to find data
event.callbacksInvoked(true);
set = true;
}
long result = super.basicPutPart2(event, entry, isInitialized, lastModified, clearConflict);
if (set) {
event.callbacksInvoked(false);
}
if (isCreate) {
try {
InitialImageOperation.setInhibitStateFlush(true); // fix for bug 36175
buildDynamicRegion(event);
} finally {
InitialImageOperation.setInhibitStateFlush(false);
}
}
return result;
}
// The dynamic-region meta-region needs to tell clients about the event
// after all servers have created the region so that register-interest
// will work correctly
@Override
public void basicPutPart3(EntryEventImpl event, RegionEntry entry, boolean isInitialized,
long lastModified, boolean invokeCallbacks, boolean ifNew, boolean ifOld,
Object expectedOldValue, boolean requireOldValue) {
super.basicPutPart3(event, entry, isInitialized, lastModified, invokeCallbacks, ifNew, ifOld,
expectedOldValue, requireOldValue);
// this code is copied from LocalRegion.basicPutPart2
invokeCallbacks &= !entry.isTombstone(); // put() is creating a tombstone
if (invokeCallbacks) {
boolean doCallback = false;
if (isInitialized) {
if (event.isGenerateCallbacks()) {
doCallback = true;
}
}
if (doCallback) {
notifyGatewaySender(event.getOperation().isUpdate() ? EnumListenerEvent.AFTER_UPDATE
: EnumListenerEvent.AFTER_CREATE, event);
// Notify listeners
if (!event.isBulkOpInProgress()) {
try {
entry.dispatchListenerEvents(event);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
getCancelCriterion().checkCancelInProgress(null);
}
}
}
}
}
}
}