All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.cache.DynamicRegionFactory Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */

package com.gemstone.gemfire.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.TreeMap;

import com.gemstone.gemfire.InternalGemFireError;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.cache.client.Pool;
import com.gemstone.gemfire.cache.client.PoolManager;
import com.gemstone.gemfire.cache.client.internal.ServerRegionProxy;
import com.gemstone.gemfire.cache.util.BridgeWriter;
import com.gemstone.gemfire.cache.wan.GatewaySender;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.cache.DistributedRegion;
import com.gemstone.gemfire.internal.cache.DynamicRegionAttributes;
import com.gemstone.gemfire.internal.cache.DynamicRegionFactoryImpl;
import com.gemstone.gemfire.internal.cache.EntryEventImpl;
import com.gemstone.gemfire.internal.cache.EnumListenerEvent;
import com.gemstone.gemfire.internal.cache.EvictionAttributesImpl;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.internal.cache.InitialImageOperation;
import com.gemstone.gemfire.internal.cache.InternalRegionArguments;
import com.gemstone.gemfire.internal.cache.LocalRegion;
import com.gemstone.gemfire.internal.cache.RegionEntry;
import com.gemstone.gemfire.internal.cache.RegionEventImpl;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.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.
 * 

* Instructions for Use:
*

    *
  • 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 or specify it * in a dynamic-region-factory element in your cache.xml. * *
  • Before you've created a GemFire Cache in your application, add a * line of code as follows:
    *
      DynamicRegionFactory factory = DynamicRegionFactory.get();
     *  factory.open(config);
    *
      DynamicRegionFactory myFactoryHandle = DynamicRegionFactory.get().open(config);
    * or just use a dynamic-region-factory element in the cache.xml. * *
  • 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. * *
  • 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. * *

    All other instances of GemFire across the distributed system that * instantiate and open a DynamicRegionFactory will also get the dynamic distributed Regions. * *

  • 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. * *
  • 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. * *
*

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. *

* 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. * *

* Notes: *

    *
  • DynamicRegionFactories in non-client VMs must not be configured with a BridgeWriter. *
  • 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. * *
  • 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 BridgeLoader, BridgeWriter, and all LRUAlgorithm instances will * be cloned so that each dynamic Region has its own callback. * *
  • 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. *
* @since 4.3 * @author Gideon Low * @author Darrel Schneider * @author Bruce Schuchardt * */ @SuppressWarnings("deprecation") public abstract class DynamicRegionFactory { public static final String dynamicRegionListName = "__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).longValue(); private static DynamicRegionFactory singleInstance = new DynamicRegionFactoryImpl ( ); GemFireCacheImpl c = null; Config config = null; /** The region listeners registered on this DynamicRegionFactory */ private static volatile List regionListeners = Collections.EMPTY_LIST; 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 _close() { this.config = null; this.c = 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.c != 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('/' + dynamicRegionListName); } /** * The method is for internal use only. It is called implicitly during cache creation. * @param theCache The GemFire Cache * @throws CacheException */ protected void _internalInit ( GemFireCacheImpl theCache ) throws CacheException { if (isClosed()) { // DynamicRegions are not enabled in this vm. Just return. return; } /** * 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. */ try { this.c = theCache; this.dynamicRegionList = theCache.getRegion(dynamicRegionListName); final boolean isClient = this.config.getBridgeWriter() != null || this.config.getPoolName()!=null; if (this.dynamicRegionList == null) { InternalRegionArguments ira = new InternalRegionArguments() .setDestroyLockFlag(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) { // BRIDGE CLIENT af.setScope(Scope.LOCAL); af.setDataPolicy(DataPolicy.NORMAL); //MirrorType(MirrorType.NONE); 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); } } else { BridgeWriter bw = this.config.getBridgeWriter(); if (!bw.hasEstablishCallbackConnection()) { throw new IllegalStateException(LocalizedStrings.DynamicRegionFactory_THE_CLIENT_POOL_OF_A_DYNAMICREGIONFACTORY_MUST_BE_CONFIGURED_WITH_ESTABLISHCALLBACKCONNECTION_SET_TO_TRUE.toLocalizedString()); } af.setCacheWriter(bw); } ira.setInternalMetaRegion(new LocalMetaRegion(af.create(), ira)); } else { af.setScope(Scope.DISTRIBUTED_ACK); if (!this.config.getPersistBackup()) { // if persistBackup, the data policy has already been set af.setDataPolicy(DataPolicy.REPLICATE); //setMirrorType(MirrorType.KEYS_VALUES); } af.setEnableGateway(true); for (GatewaySender gs : c.getGatewaySenders()) { if (!gs.isParallel()) af.addGatewaySenderId(gs.getId()); } ira.setInternalMetaRegion(new DistributedMetaRegion(af.create())); // bug fix 35432 } try { dynamicRegionList = theCache.createVMRegion(dynamicRegionListName, af.create(), ira); } catch (IOException e) { // only if loading snapshot, not here InternalGemFireError assErr = new InternalGemFireError(LocalizedStrings.DynamicRegionFactory_UNEXPECTED_EXCEPTION.toLocalizedString()); assErr.initCause(e); throw assErr; } catch (ClassNotFoundException e) { // only if loading snapshot, not here InternalGemFireError assErr = new InternalGemFireError(LocalizedStrings.DynamicRegionFactory_UNEXPECTED_EXCEPTION.toLocalizedString()); assErr.initCause(e); throw assErr; } if (isClient) { dynamicRegionList.registerInterest("ALL_KEYS"); } if (theCache.getLoggerI18n().fineEnabled()) { theCache.getLoggerI18n().fine("Created dynamic region: " + dynamicRegionList); } } else { if (theCache.getLoggerI18n().fineEnabled()) { theCache.getLoggerI18n().fine("Retrieved dynamic region: " + dynamicRegionList); } } createDefinedDynamicRegions ( ); } catch ( CacheException e ) { // theCache.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_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 possiblity of synchronization issues . . . . Set s = dynamicRegionList.entrySet( false ); Iterator i = s.iterator(); TreeMap sorted = new TreeMap(); // sort by region name before creating (bug 35528) while ( i.hasNext() ) { Region.Entry e = (Region.Entry)i.next(); DynamicRegionAttributes dda = (DynamicRegionAttributes)e.getValue(); sorted.put(dda.rootRegionName + "/" + dda.name, dda); } i = sorted.values().iterator(); while ( i.hasNext() ) { DynamicRegionAttributes dda = (DynamicRegionAttributes)i.next(); doBeforeRegionCreated ( dda.rootRegionName, dda.name, null ); Region region = createDynamicRegionImpl ( dda.rootRegionName, dda.name, false ); doAfterRegionCreated ( region, false, false, null ); } } /** * Returns the DynamicRegionFactory singleton instance. * @return the DynamicRegionFactory singleton instance */ public static DynamicRegionFactory get() { return singleInstance; } /** * Registers a DynamicRegionListener for callbacks. * @param listener The 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 DynamicRegionListener for callbacks. * @param listener The 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, DistributedMember mbr ) { for ( Iterator i = regionListeners.iterator(); i.hasNext(); ) { DynamicRegionListener listener = ( DynamicRegionListener ) i.next(); try { listener.beforeRegionCreate( parentRegion, regionName ); } catch (Throwable t) { Error err; if (t instanceof Error && SystemFailure.isJVMFailureError( err = (Error)t)) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } // Whenever you catch Error or Throwable, you must also // check for fatal JVM error (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.c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_DYNAMICREGIONLISTENER__0__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 ( Iterator i = regionListeners.iterator(); i.hasNext(); ) { DynamicRegionListener listener = ( DynamicRegionListener ) i.next(); try { listener.afterRegionCreate( event /*region*/ ); } catch (Throwable t) { Error err; if (t instanceof Error && SystemFailure.isJVMFailureError( err = (Error)t)) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } // Whenever you catch Error or Throwable, you must also // check for fatal JVM error (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.c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_DYNAMICREGIONLISTENER__0__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 ( Iterator i = regionListeners.iterator(); i.hasNext(); ) { DynamicRegionListener listener = ( DynamicRegionListener ) i.next(); try { listener.beforeRegionDestroy( event /*fullRegionName*/ ); } catch (Throwable t) { Error err; if (t instanceof Error && SystemFailure.isJVMFailureError( err = (Error)t)) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } // Whenever you catch Error or Throwable, you must also // check for fatal JVM error (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.c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_DYNAMICREGIONLISTENER__0__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 ( Iterator i = regionListeners.iterator(); i.hasNext(); ) { DynamicRegionListener listener = ( DynamicRegionListener ) i.next(); try { listener.afterRegionDestroy( event /*fullRegionName*/ ); } catch (Throwable t) { Error err; if (t instanceof Error && SystemFailure.isJVMFailureError( err = (Error)t)) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned // now, so don't let this thread continue. throw err; } // Whenever you catch Error or Throwable, you must also // check for fatal JVM error (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.c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_DYNAMICREGIONLISTENER__0__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 InternalDistributedSystem.getAnyInstance().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 Region created * @throws CacheException */ public Region createDynamicRegion ( String parentRegionName, String regionName ) throws CacheException { if (isClosed()) { throw new IllegalStateException("Dynamic region factory is closed"); } doBeforeRegionCreated ( parentRegionName, regionName, null ); 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 Region to be * dynamically destroyed * @throws CacheException * @throws RegionDestroyedException if the dynamic region was never created * or has already been destroyed */ public void destroyDynamicRegion ( String fullRegionName ) throws CacheException { if (!dynamicRegionList.containsKey(fullRegionName)) { throw new RegionDestroyedException(LocalizedStrings.DynamicRegionFactory_DYNAMIC_REGION_0_HAS_NOT_BEEN_CREATED.toLocalizedString(fullRegionName), fullRegionName); } if (isClosed()) { throw new IllegalStateException("Dynamic region factory is closed"); } // Retrieve the region to destroy Region region = c.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 = c.getRegion ( parentRegionName ); Region newRegion = null; if ( parentRegion == null ) { String errMsg = LocalizedStrings.DynamicRegionFactory_ERROR__COULD_NOT_FIND_A_REGION_NAMED___0_.toLocalizedString(parentRegionName); RegionDestroyedException e = new RegionDestroyedException(errMsg, parentRegionName); c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_ERROR__COULD_NOT_FIND_A_REGION_NAMED___0_, parentRegionName, e); throw e; } // Create RegionAttributes by inheriting from the parent RegionAttributes rra = parentRegion.getAttributes(); RegionAttributes newRegionAttributes = null; AttributesFactory af = new AttributesFactory( rra ); { EvictionAttributes ev = rra.getEvictionAttributes(); if (ev != null && ev.getAlgorithm().isLRU()) { EvictionAttributes rev = new EvictionAttributesImpl( (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 [bruce] if (newRegionName.endsWith("_PRTEST_")) { af.setPartitionAttributes((new PartitionAttributesFactory()).create()); } newRegionAttributes = af.create(); try { newRegion = parentRegion.createSubregion( newRegionName, newRegionAttributes ); c.getLoggerI18n().fine("Created dynamic region " + newRegion); } catch (RegionExistsException ex) { // a race condition exists that can cause this so just fine log it c.getLoggerI18n().fine("DynamicRegion " + newRegionName + " in parent " + parentRegionName + " already existed"); newRegion = ex.getRegion(); // } catch ( CacheException e ) { // c.getLoggerI18n().warning ( "Error creating new Dynamic Region '" + newRegionName, e ); // throw e; } if (addEntry) { DynamicRegionAttributes dra = new DynamicRegionAttributes ( ); dra.name = newRegionName; dra.rootRegionName = parentRegion.getFullPath(); if (c.getLoggerI18n().fineEnabled()) { c.getLoggerI18n().fine ("Putting entry into dynamic region list at key: " + newRegion.getFullPath()); } dynamicRegionList.put ( newRegion.getFullPath(), dra ); } if (config.getRegisterInterest()) { ServerRegionProxy proxy = ((LocalRegion)newRegion).getServerProxy(); if (proxy != null) { if (((Pool)proxy.getPool()).getSubscriptionEnabled()) { try { newRegion.registerInterest("ALL_KEYS"); } catch (GemFireSecurityException ex) { // Ignore security exceptions here c.getSecurityLoggerI18n().warning( LocalizedStrings.DynamicRegionFactory_EXCEPTION_WHEN_REGISTERING_INTEREST_FOR_ALL_KEYS_IN_DYNAMIC_REGION_0_1, new Object[] {newRegion.getFullPath(), ex}); } } } } if (regionCreateSleepMillis > 0) { try { Thread.sleep( regionCreateSleepMillis ); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); } } if (c.getLoggerI18n().fineEnabled()) { c.getLoggerI18n().fine ( "Created Dynamic Region " + newRegion.getFullPath() ); } return newRegion; } private void destroyDynamicRegionImpl(String fullRegionName) throws CacheException { // Destroy the entry in the dynamicRegionList try { if (c.getLoggerI18n().fineEnabled()) { c.getLoggerI18n().fine ("Destroying entry from dynamic region list at key: " + fullRegionName); } dynamicRegionList.destroy ( fullRegionName ); } catch (CacheException e) { c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_ERROR_DESTROYING_DYNAMIC_REGION__0, fullRegionName, e); throw e; } if (c.getLoggerI18n().fineEnabled()) { c.getLoggerI18n().fine ( "Destroyed Dynamic Region " + fullRegionName ); } } /** * Configuration for dynamic region factory. * The default attributes are: *
    *
  • diskDir: null *
  • bridgeWriter: null *
  • persistBackup: true *
  • registerInterest: true *
* @since 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 BridgeWriter} to be used by the factory to communicate with * the factory in its server. * Client factories must configure a BridgeWriter for their factory * and it must be configured to establish a callback connection. */ public final BridgeWriter bridgeWriter; /** * The ${link Pool} to be used by the factory to communicate with * the server-side factory. Client factories may use this instead of a BridgeWriter */ public final String poolName; /** * Creates a configuration with the default attributes. */ public Config() { this(null, null, !DISABLE_PERSIST_BACKUP); } /** * Creates a configuration with the given attributes and defaults for other attributes. * @deprecated use a pool name instead of a bridge writer */ @Deprecated public Config(File diskDir, BridgeWriter bridgeWriter) { this(diskDir, bridgeWriter, !DISABLE_PERSIST_BACKUP); } /** * Creates a configuration with the given attributes and defaults for other attributes. * @deprecated use a pool name instead of a bridge writer */ @Deprecated public Config(File diskDir, BridgeWriter bridgeWriter, boolean persistBackup) { this(diskDir, bridgeWriter, persistBackup, !DISABLE_REGISTER_INTEREST); } /** * Creates a configuration with the given attributes * @deprecated use a pool name instead of a bridge writer */ @Deprecated public Config( File diskDir, BridgeWriter bridgeWriter, boolean persistBackup, boolean registerInterest) { this.registerInterest = registerInterest; this.persistBackup = persistBackup; this.diskDir = diskDir; this.bridgeWriter = bridgeWriter; this.poolName = null; } /** * 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; this.bridgeWriter = null; } /** * 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 persistBackup is true. */ public File getDiskDir() { return this.diskDir; } /** * Returns the {@link BridgeWriter} associated with the dynamic region factory. * Returns null if there is no cache writer for dynamic regions. * A cache writer will only exist if this is a client and the cache writer connects to a server. */ public BridgeWriter getBridgeWriter() { return this.bridgeWriter; } /** * 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.bridgeWriter = conf.bridgeWriter; 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 bridge event if ( !event.isOriginRemote() && !event.isBridgeEvent() ) return; // DynamicRegionAttributes dra = (DynamicRegionAttributes)event.getNewValue(); String parentRegionName = dra.rootRegionName; String newRegionName = dra.name; try { doBeforeRegionCreated ( parentRegionName, newRegionName, event.getDistributedMember() ); Region region = createDynamicRegionImpl ( parentRegionName, newRegionName, false); doAfterRegionCreated ( region, true, true, event.getDistributedMember() ); } catch ( Exception e ) { c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_ERROR_ATTEMPTING_TO_LOCALLY_CREATE_DYNAMIC_REGION__0, 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) // if ( !event.isOriginRemote() && !(event instanceof BridgeEntryEventImpl)) return; String fullRegionName = ( String ) event.getKey(); Region drRegion = c.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 ) { c.getLoggerI18n().warning(LocalizedStrings.DynamicRegionFactory_ERROR_ATTEMPTING_TO_LOCALLY_DESTROY_DYNAMIC_REGION__0, fullRegionName, e); } } } // private class DRListener implements CacheListener { // public void afterCreate(EntryEvent arg0) { // buildDynamicRegion(arg0); // } // // public void afterDestroy(EntryEvent arg0) { // razeDynamicRegion(arg0); // } // // public void afterInvalidate(EntryEvent arg0) { // // Stub, nothing to do. // } // // public void afterRegionDestroy(RegionEvent arg0) { // // Stub, nothing to do. // } // // public void afterRegionInvalidate(RegionEvent arg0) { // // Stub, nothing to do. // } // // public void afterUpdate(EntryEvent arg0) { // // Stub, nothing to do. // } // // public void close() { // // Stub, nothing to do. // } // } // 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(dynamicRegionListName, attrs, null, DynamicRegionFactory.this.c, ira); Assert.assertTrue(attrs.getScope().isLocal()); } // This is an internal uses only region @Override public boolean isSecret() { return true; } @Override protected 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 protected boolean shouldNotifyBridgeClients() { return getCache().getBridgeServers().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) { notifyGatewayHubs(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 protected 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) { notifyGatewayHubs(event.getOperation().isUpdate()? EnumListenerEvent.AFTER_UPDATE : EnumListenerEvent.AFTER_CREATE, event); // Notify listeners if (event.getPutAllOperation() == null) { try { entry.dispatchListenerEvents(event); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); stopper.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(dynamicRegionListName, attrs, null, DynamicRegionFactory.this.c, new InternalRegionArguments()); } // 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 protected boolean isCopyOnRead() { return false; } // while internal, its contents should be communicated with bridge clients @Override final public boolean shouldNotifyBridgeClients() { return getCache().getBridgeServers().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) { notifyGatewayHubs(eventType, event); } // Destroy the dynamic region after the clients and gateways have been notified razeDynamicRegion(event); } @Override protected 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) { notifyGatewayHubs(event.getOperation().isUpdate()? EnumListenerEvent.AFTER_UPDATE : EnumListenerEvent.AFTER_CREATE, event); // Notify listeners if (event.getPutAllOperation() == null) { try { entry.dispatchListenerEvents(event); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); stopper.checkCancelInProgress(null); } } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy