Application Plug-ins
The API provides the framework for application plug-ins with callback functions for the appropriate events. Your classes and functions can customize these for your application’s needs. When creating a region, specify these as part of the region’s attributes settings. For regions already in the cache, you can specify new CacheLoader
, CacheWriter
, and CacheListener
using the region’s AttributesMutator
. The PartitionResolver
is not mutable.
-
CacheLoader
: A data loader called when an entry get operation fails to find a value for a given key. A cache loader is generally used to retrieve data from an outside source such as a database, but it may perform any operation defined by the user. Loaders are invoked as part of the distributed loading activities for entry retrieval, described in Entry Retrieval. -
CacheWriter
: A synchronous event listener that receives callbacks before region events occur and has the ability to abort the operations. Writers are generally used to keep a back-end data source synchronized with the cache. -
CacheListener
: An asynchronous event listener for region events in the local cache. -
PartitionResolver
: Used for single-hop access to partitioned region entries on the server side. This resolver implementation must match that of thePartitionResolver
on the server side.
The following XML declaration specifies a cache loader for a region when the region is created.
<region-attributes>
<cache-loader library-name="appl-lib"
library-function-name ="createCacheLoader">
</cache-loader>
</region-attributes>
The rest of this section gives more detailed descriptions of these application plug-ins, followed by special considerations for plug-ins in distributed regions and some guidelines for writing callbacks.
CacheLoader
A cache loader is an application plug-in used to load data into the region. When an entry is requested that is unavailable in the region, a cache loader may be called upon to load it. Generally, you use a cache loader to retrieve the data from a database or another source outside the distributed system, but it may perform any operation defined by the user.
The CacheLoader
interface provides one function, load
, for customizing region entry loading. A distributed region may have cache loaders defined in any or all caches where the region is defined. When loading an entry value, a locally defined cache loader is always used before a remote loader. In distributed regions, loaders are available for remote entry retrieval.
CacheWriter
A cache writer is an application plug-in that synchronously handles changes to a region’s contents. It is generally used to keep back-end data sources synchronized with a cache region. A cache writer has callback functions to handle region destruction and entry creation, update, and destruction. These functions are all called before the modification has taken place and can abort the operation.
You can also use cache writers to store data that you want to make persistent.
CacheListener
A cache listener is an application plug-in that asynchronously handles changes to a region’s contents. A cache listener has callback functions to handle region destruction and invalidation, along with entry creation, update, invalidation, and destruction. These functions are called asynchronously after the modification has taken place.
This declarative XML example establishes a cache listener when a region is created:
<region name="region11">
<region-attributes>
<cache-listener library-name="appl-lib"
library-function-name ="createCacheListener" />
</region-attributes>
</region>
Unlike cache loaders and cache writers, cache listeners only receive events for entries to which the client has performed operations or registered interest.
When the listener is attached to a region with caching disabled, the old value is always NULL
.
Note:
Do not perform region operations inside the cache listener. Once you have configured a cache listener, the event supplies the new entry values to the application. Performing a get with a key from the EntryEvent
can result in distributed deadlock. For more about this, see the API documentation for EntryEvent
.
When a region disconnects from a cache listener, you can implement the afterRegionDisconnected
callback. This callback is only be invoked when using the pool
API and subscription
is enabled on the pool. For example:
class DisconnectCacheListener : public CacheListener
{
void afterRegionDisconnected( const RegionPtr& region )
{
printf("After Region Disconnected event received");
}
};
PartitionResolver
This section pertains to data access in server regions that have custom partitioning. Custom partitioning uses a Java PartitionResolver
to colocate like data in the same buckets. For the client, you can use a PartitionResolver
that matches the server’s implementation to access data in a single hop. With single-hop data access, the client pool maintains information on where a partitioned region’s data is hosted. When accessing a single entry, the client directly contacts the server that hosts the key–in a single hop.
Note:
Single hop is used for the following operations: put
, get
, destroy
, putAll
, getAll
, removeAll
and onRegion
function execution.
Implementing Single-Hop on a Partitioned Region
- Make sure the pool attribute,
pr-single-hop-enabled
, is set totrue
or not set. It istrue
by default. If the server uses a custom
PartitionResolver
install an implementation ofPartitionResolver
in the client region that returns, entry for entry, the same value as the server’s JavaPartitionResolver
implementation. The server uses the resolver to colocate like data within a partitioned region.If the server does not use a custom resolver, the default resolvers in client and server match, so single hop will work there by default.
Disable single hop behavior for a region by setting its pool attribute pr-single-hop-enabled
to false
.
See <client-cache> Element Reference in the server’s documentation for information on setting pr-single-hop-enabled
.
See the server documentation on Partitioned Regions for more information, including colocating like data within a partitioned region and how to get the best performance with PR single hop.
Implementing a PartitionResolver
See the server documentation on Custom-Partitioning and Colocating Data for information on custom-partitioning the server partitioned regions.
- Implement
PartitionResolver
in the same place that you did in the server–custom class, key, or cache callback argument. Program the resolver’s functions the same way you programmed them in the Java implementation. Your implementation must match the server’s.
Example of programming the
PartitionResolver
in C++:class TradeKeyResolver : public PartitionResolver { private: string m_tradeID; int m_month; int m_year; public: TradeKeyResolver() { } TradeKeyResolver(int month, int year) { m_month = month; m_year = year; } ~TradeKeyResolver() { } static PartitionResolverPtr createTradeKeyResolver() { PartitionResolverPtr tradeKeyResolver( new TradeKeyResolver()); return tradeKeyResolver; } const char* getName() { return "TradeKey"; } CacheableKeyPtr getRoutingObject(const EntryEvent& opDetails) { return CacheableKey::create(m_month + m_year); } };
Example of programming the
PartitionResolver
in C#:using System; using System.Threading; using Apache.Geode.Client; class TradeKeyResolver : IPartitionResolver { private int m_month = 0; private int m_year = 0; public static TradeKeyResolver CreateTradeKeyResolver() { return new TradeKeyResolver(); } public virtual ICacheableKey GetRoutingObject(EntryEvent entry) { return new CacheableInt32(m_month + m_year); } public virtual String GetName() { return "TradeKeyResolver"; } }
Install the resolver in the region. Use one of these methods:
XML partition resolver declaration:
<region name="trades" refid="CACHING_PROXY"> <region-attributes> <partition-resolver library-name="appl-lib" library-function-name= "createTradeKeyResolver"/> </region-attributes> </region> <pool free-connection-timeout="12345" idle-timeout="5555" load-conditioning-interval="23456" max-connections="7" min-connections="3" name="test_pool_1" ping-interval="12345" read-timeout="23456" retry-attempts="3" server-group="ServerGroup1" socket-buffer-size="32768" statistic-interval="10123" subscription-ack-interval="567" subscription-enabled="true" subscription-message-tracking-timeout="900123" subscription-redundancy="0" thread-local-connections="5" pr-single-hop-enabled="true" > <locator host="localhost" port="34756"/> </pool>
Programmatic partition resolver installation:
void setPartitionResolver() { CachePtr cachePtr = CacheFactory::createCacheFactory()->create(); PartitionResolverPtr resolver( new TradeKeyResolver()); RegionFactoryPtr regionFactory = cachePtr->createRegionFactory(PROXY) ->setClientNotificationEnabled(true) ->setPartitionResolver(resolver); RegionPtr regionPtr = regionFactory->create( "Trades" ); }
Your implementation of PartitionResolver
must match that of the server side.
Using AttributesMutator to Modify a Plug-In
A cache listener, cache loader or cache writer can be added to or removed from a region after the region is created by retrieving and running the Region
object’s AttributesMutator
. Mutable attributes define operations that are run from the client itself.
This example shows how to use AttributesMutator
to dynamically add a cache listener to an existing region.
void setListener(RegionPtr& region)
{
CacheListenerPtr regionListener = new TestCacheListener();
AttributesMutatorPtr regionAttributesMutator =
region->getAttributesMutator();
// Change cache listener for region.
regionAttributesMutator->setCacheListener(regionListener);
}
The plug-ins can also be implemented using a dynamically linked library. The class is not available to the application code in this case, so a factory
method is required by the set
function along with the name of the library.
This example shows how to use AttributesMutator
along with the setCacheListener
function to obtain a new cache listener object using the factory
function provided by the library. Next, the listener is set for the region.
void setListenerUsingFactory(RegionPtr& region)
{
AttributesMutatorPtr regionAttributesMutator =
region->getAttributesMutator();
// Change cache listener for region.
regionAttributesMutator->setCacheListener("Library", "createTestCacheListener");
}
To use AttributesMutator
to remove a plug-in from a region, set the plug-in’s value to NULLPTR
, as shown in the following example.
void removeListener(RegionPtr& region)
{
CacheListenerPtr nullListener = NULLPTR;
AttributesMutatorPtr regionAttributesMutator =
region->getAttributesMutator();
// Change cache listener for region to NULLPTR
regionAttributesMutator->setCacheListener(nullListener);
}
Considerations for Implementing Callbacks
Keep your callback implementations lightweight and prevent situations that might cause them to hang. For example, do not perform distribution operations or disconnects inside event handlers.
Your code should handle any exceptions that it generates. If not, GemFire handles them as well as possible. Because C++ has no standard for exceptions, in many cases GemFire can only print an unknown error
message.
Specifying Application Plug-In Attributes
The plug-in attributes allow you to customize client region behavior for loading, updating, deleting, and overflowing region data and for accessing data in server partitioned regions. All client plug-ins are available through the C++ and .NET API.
Application plug-ins for cache regions in clients can be declared either programmatically or in the cache.xml
file.
Figure: Where Application Plug-Ins Run