Using the C++ Client API
This section covers connecting to, working with, and searching your BigMemory data. For the complete class library documentation, refer to the API documentation in the /apis/csharp/apidoc-cpp.zip directory of the kit.
Connecting with the CL Connector
Your application will communicate with the CL Connector via the CacheManager interface.
Begin by creating a Configuration object. For a deployment where the BigMemory Client is located on the same machine as the CL Connector, use a configuration with the Nirvana Shared Memory (SHM) transport:
Configuration("/dev/shm", 16);
For a deployment where the BigMemory Client and the CL Connector are located on different machines, use a configuration with the Nirvana socket transport. For example, here we create a configuration that connects to the local host at port 8199, and provisions a pool of 16 connections to share:
Configuration("localhost", 8199, 16);
Notice that configurations with different transport types require different parameters:
Nirvana Shared Memory (SHM) -
Configuration("SHMLocation", clientPoolSize) - generally used when the BigMemory Client is located on the same machine as the CL Connector.
Nirvana socket -
Configuration("hostName", portNumber, clientPoolSize) - used when the CL Connector and the BigMemory Client are located on different machines.
Once you have your Configuration instance ready, create a CacheManager using the XPlatform static method:
CacheManager cacheManager = XPlatform::createCacheManager(config);
Shutting the connection down
The CacheManager needs to be closed in order for it to release all resources (like connections). Close it by invoking the destructor:
delete cacheManager;
Accessing a Cache
Once you've obtained a reference to your CacheManager, it is now connected to your CL Connector and will let you access any Cache known by the CL Connector.
To retrieve a thread-safe instance of a Cache, you will need its name, the type of the key, the value, and a serializer for those types.
Serializers
The protocol recognizes the following types:
bool: A boolean value (true or false), one byte
byte: A signed byte
int: An 8-bit signed integer
i16: A 16-bit signed integer
i32: A 32-bit signed integer
i64: A 64-bit signed integer
double: A 64-bit floating point number
byte[]: An array of bytes
string: Encoding agnostic text or binary string
If you want to send complex types to the CL Connector, you will have to provide a serializer that can serialize your complex key and value types for a Cache.
Say we have the following Class, which is our Value type:
class User{
private: long Id;
std::string Login;
std::string Email;
public: int getID() { return Id; }
void setID(int id) { Id = id; }
string getEmail() { return Email; }
void setEmail(int email) { Email = email; }
string getLogin() { return Login; }
void setLogin(string name) { Login = name; }
};
We store these keyed to the login, as a string. We now need to provide a serializer like this:
class UserCacheSerializer : public CacheSerializer<std::string, User>
{
ValueObject serializeKey(std::string &deserializedKey)
{
return ValueObject::stringValue(deserializedKey);
}
std::string deserializeKey(ValueObject &serializedKey)
{
return serializedKey.getString();
}
CacheValue serializeValue(User objectValue)
{
std::map<std::string, ValueObject> nvpairs;
nvpairs["id"] = ValueObject::int8Value(objectValue.Id);
nvpairs["email"] = ValueObject::stringValue(objectValue.Email);
CacheValue value = CacheValue();
value.Value = ValueObject::stringValue(objectValue.Login);
value.setNvPairs(nvpairs);
return value;
}
User deserializeValue(CacheValue serializedValue)
{
User user;
user.Login = serializedValue.getValue().getString();
user.Id = serializedValue.getNvPairs()["id"].getInt8();
user.Email = serializedValue.serializedValue.getNvPairs()["email"].getString();
return user;
}
}
Note that both serialize and deserialize methods (whether for key or value) are kept symmetric.
CacheValue is composed of the actual ValueObject (in this example, the string representation of the user's login). CacheValue can hold entire object graphs serialized with whatever serialization strategies fits your needs. It also holds an arbitrary amount of name/value pairs. The names are std::string, while the values are of type ValueObject. You can use these name-value pairs to store whatever you want. Note that these name-value pairs also become indexable and searchable using the search API.
A byte array is not indexable, however it can be used within name-value pairs. In the above example, both user id and email are stored as byte arrays. Even though byte arrays are not indexable, search can be enabled on the attributes. Since we use the login as the key in our use case, storing that as a binary representation within the "default unindexed value" is good enough.
Note: The RawCache class, available directly from the CacheManager, allows you to access raw data, i.e., ValueObject as keys, and CacheValue as values. RawCache can be used when your data does not need to be serialized, and it allows you to create a cache without a serializer.
Key-based store and retrieve
Now that we have defined a CacheSerializer for our cache, we can retrieve the cache from the CacheManager. The example below assumes that we have defined a cache named "userCache" within the ehcache.xml file used to configure the CL Connector.
// create a serializer
CacheSerializer<string, User> *serializer = new userCacheSerializer();
// get a cache
Cache<string, User> users = cacheManager->getCache("userCache", *serializer);
// make a user
User user1;
user1.Id = 1;
user1.Email = "someone@somewhere";
user1.Login = "secretpasswd";
// put the user
users->put(user1.Login, user1, type);
The following is an example for the Recipe code sample (example03). Recipe is the class defining the domain object, and RecipeProtoBufSerializer() is the serializer class.
Cache<string, Recipe> *cache = cacheManager->getCache("TestCache",
new RecipeProtoBufSerializer());
cache->put(rec.Name, rec, type);
The
type attribute calls the consistency, configured as either strong or eventual, with which you expect the CL Connector to execute the operation. For more information, refer to
Consistency.
To retrieve by key, invoke the get method:
User someUser = userCache->get(someLogin, type);
An example of retrieving, using the Recipe demo:
Recipe rec = (Recipe)cache->get(args[1], type);
Compare and Swap (CAS) Operations
The following CAS operations are provided in C++:
putIfAbsent(key_type_ref key, value_type_ref cacheValue) - Puts the specified key/value pair into the cache only if the key has no currently assigned value. Unlike put, which can replace an existing key/value pair, putIfAbsent creates the key/value pair only if it is not present in the cache.
remove(key_type_ref key, value_type_ref value) - Conditionally removes the specified key, if it is mapped to the specified value.
replace(key_type_ref key, value_type_ref value) - Maps the specified key to the specified value, if the key is currently mapped to some value.
replace(key_type_ref key, value_type_ref oldValue, value_type_ref newValue) - Conditionally replaces the specified key's old value with the new value, if the currently existing value matches the old value.
For more information about atomic operations and cache consistency, refer to
Consistency.
Search
This section provides an overview of how to do cross-language searches of PNF data stored within BigMemory. More detailed information may be found in "Searching a Cache" in the BigMemory Max Developer Guide.
In order to search BigMemory data, you first need to add the <searchable/> tag to the cache configuration in your ehcache.xml file.
<ehcache>
(...)
<cache name="userCache">
(...)
<searchable/>
</cache>
</ehcache>
This configuration will scan all keys and values in the cache and, if they are of supported search types, add them as search attributes. Values of the Name/Value pairs in the PNF's ValueObject sent to the CL Connector, other than of type byte[], can all be indexed and made searchable. (Continuing with the example of the previous section, this would mean id and email.)
You can also add search attributes using the provided attributeExtractor class, for example:
<ehcache>
(...)
<cache name="userCache">
(...)
<searchable>
<searchAttribute name="email" class="net.sf.ehcache.xplatform.search.Indexer"/>
</searchable>
</cache>
</ehcache>
To perform a query, use the Cache.query(String) method, providing a BigMemory SQL query string, for example:
SearchResults<string, User> result = userCache->query("select * from userCache
where email ilike '%@terracottatech.com'");
For more information about how to construct BigMemory SQL queries, see "Searching with BigMemory SQL" in the BigMemory Max Developer Guide.
Note: Java (POJO) data stored within BigMemory can also be searched using the native Ehcache Search API, which is documented in the BigMemory Max Developer Guide.