The IBM Director Distributed Configuration service allows a block of data to be stored on the IBM Director Server and associated with one or more managed objects. This block of data is then sent to the systems represented by the associated managed objects. As updates occur to the data block, the new changes are automatically sent to the associated systems, providing a simple yet powerful tool for distributing common data to a set of systems, and insuring that modifications to that data are propagated to those systems in a timely fashion. Furthermore, if the Distributed Configuration service determines that a given target system is unavailable (for example, is offline), it will send the updates to that system at the earliest opportunity (for example, when it determines that the system is online again).
The Distributed Configuration service also provides support for data to be stored on the client where the agent resides and pushed up to the server when modified. The agent might register with the server indicating a agent-owned block of data. As the data is updated on the system, updates are communicated to the server automatically. In combination, the use of both of these facilities allows for relatively nonvolatile data to be synchronized between a server and a set of client systems with a minimum of network traffic.
Records have the capability to be associated with group objects
(TWGFilter
objects). The Distributed Configuration service
responds to changes in group membership; thus, records associated with a group
can be sent automatically to new systems when new members are added to a group,
and can be automatically deleted from a target system when a member is removed
from a group.
Each data block is a record in the Distributed Configuration service.
Records might be either server-based or client-based. This determines which record is assumed to be the master record when a mismatch occurs. When the Distributed Configuration manager determines that the records are out of sync between the server and the client, the master record is used as the template to update the out-of-date record.
The privacy of a record might be set at the time of the record creation. A private record might only be modified by the defining application, while public records might be freely modified by other tasks.
Since the distribution of data to group objects might not be appropriate for some operations, it is possible to specify a flag that indicates that the association of a record with a group object is not allowed.
Each record has an associated record ID which uniquely identifies that record (hierarchical names, such as "Application|Subtask|ID" are suggested, to minimize the possibility of name overlap).
The first step is to create a
TWGDistConfigRecordDef
object. Since
TWGDistConfigRecordDef
is a
persistent object, it only needs
to be created once. Typically, this will be done during
extension initialization in the
class
instance initialization phase.
public class DistCfgExtension extends TWGExtension { // ... final public static String RECORD_KEY = "UniqueRecordKey"; public void InitClassInstances() throws TWGExtensionInitException { TWGDistConfigRecordDef rec = TWGDistConfigRecordDef.findByIDString(RECORD_KEY); if(rec == null) { try { // Create public server-based record definition which supports // all managed object classes. Data is stored in file // DistCfgData.dcf on server. rec = new TWGDistConfigRecordDef(RECORD_KEY, null, true, true, true, "DistCfgData", false); rec.save(); } catch (TWGPersistentObjectSaveException posx) { } catch (DuplicateObjectIDException doidx) { } } } }
Both server and client-mastered records must be created before they can be used. Client-mastered records which have not been registered (created) on the server will be ignored by the Distributed Configuration service.
Once a record has been created, it is modified by sending update commands to the local service manager.
The following special subclasses of Command
are provided to
query and update server-mastered records:
There are currently no specialized helper command classes to modify client-mastered records, so the command parameters must be assembled manually. The input parameter for the command has the following format:
DistConfigRecord structure
| Record ID (UTF8 format) | Record data |
The code to assemble the modification command might look like this:
ULONG length = 0; Str *record_key = NULL; LPBYTE buf = NULL; Command *cmd = NULL; DistConfigRecord *drec = NULL; // calculate buffer length length += sizeof(DistConfigRecord); record_key = new Str("UniqueRecordKey"); record_key->LocalToCompUnicode(); record_key->CompUnicodeToUTF8(); length += record_key->Length() + 1; length += data_length_in_bytes; // allocate buffer and fill in appropriate structure members buf = (LPBYTE)Command::AllocateParm(length); if ( buffer != NIL ) { ULONG end_of_record = sizeof(DistConfigRecord); // use to calculate values for offset fields memset(buf, 0, length); drec = (DistConfigRecord *)buf; drec->object_id = 0; drec->flags = DCFGMGR_FLAGS_ISSYSTEM | DCFGMGR_FLAGS_ISAGENTOWNED; if (data_length_in_bytes == 0) { // if no data, assume this is a delete request drec->flags |= DCFGMGR_FLAGS_ISDELETED; } memset(drec->server_uid, 0, sizeof(drec->server_uid)); drec->off_rec_id = end_of_record; memcpy((LPSTR)drec + end_of_record, (LPSTR)*record_key, record_key->Length() + 1); end_of_record += record_key->Length() + 1; drec->off_rec_data = end_of_record; drec->len_rec_data = data_length_in_bytes; memcpy((LPSTR)drec + end_of_record, data_buffer, data_length_in_bytes); cmd = new Command(SVCMGR_ADD_UPDATE_DEL_DCFGMGR_REC); cmd->SetDestinationAddress("SvcMgr"); cmd->AttachInputParm(buf, length); serviceNode->SendCommand(*cmd); if (cmd->ReturnCode() == 0) { // successful } else { // handle error }
The next step is to handle record update notifications. The methods to accomplish this differ based on whether the processing is being handled at the server or the client.
registerChangeListener()
and
deregisterChangeListener()
methods of the
TWGDistConfigRecordDef
class with an argument of type
TWGDistConfigRecordChangeListener
.
Alternately, a subclass of TWGDistConfigRecordDef
can be created
and the callback routines
distConfigAgentRecordDeleted()
,
distConfigAgentRecordUpdated()
, and
handleUnsupportedDistConfigAgent()
overridden. If a subclass TWGDistConfigRecordDef
is created in
order to override the callback routines, it is necessary to
register
the class names during the class initialization phase of
extension initialization:
public class DistCfgExtension extends TWGExtension { // ... public void InitClassRegistration() throws TWGExtensionInitException { try { String className = FULLY_QUALIFIED_SUBCLASS_CLASS_NAME; RegisterClass(className); } catch (ClassNotFoundException cnfx) { } } }
#include "dcfgmgr.hpp" DistConfigMgrSubscribeCmd(char *rec_id, long callback_cmd_code); DistConfigMgrUnsubscribeCmd(char *rec_id, long callback_cmd_code);
Subscription to record change notifications uses Director's
interprocess communication
system to send a registration message to the client's local service manager.
The client uses these subclasses of the Command
class to register
and deregister for which records it would like to be notified when changes
occur. The record to be registered is identified by its record ID, which was
specified when the record definition was originally created. The client also
specifies a callback command code, which is a user-defined command
code which will be sent by the server when notifying the client of a record
change. After creating an instance of the registration or deregistration
command, the client should send the command using its local service node
(the constructors for these commands set the correct destination address
automatically). When changes occur to the record, a new command using the
specified callback command code will be sent to the local service node, where
it can be processed as needed.
On a successful subscription request, the
DistConfigMgrSubscribeCmd
returns with the current record data,
if available. The DistConfigMgrSubscribeCmd
class contains
methods to access this data:
ULONG numRecordsReturned() /* Get number of records returned */ ULONG getObjectID(ULONG index) /* Get object ID (System or group ID) of record 'index' */ UCHAR *getServerUID(ULONG index) /* Get server unique ID of record 'index' : returns pointer to UCHAR[8] */ char *getRecordID(ULONG index) /* Get record ID of record 'index' : returns pointer to char * (UTF8) */ BOOL isManagedObjectID(ULONG index) /* Test to see if record 'index' object ID is system/managed object ID */ BOOL isGroupID(ULONG index) /* Test to see if record 'index' object ID is group/TWGFilter object ID */ BOOL isDeletedRecord(ULONG index) /* Test to see if record 'index' has been deleted (as opposed to updated/added) */ UCHAR *getDataHashcode(ULONG index) /* Get data hashcode of record 'index' : returns pointer to UCHAR[16] */ ULONG getDataLength(ULONG index) /* Get data length of record 'index' */ void *getDataPointer(ULONG index) /* Get pointer to data of record 'index' */
The next step is to process record update notifications. Again, the process differs based on whether the handling is being done on the server or a client.
distConfigAgentRecordDeleted()
and
distConfigAgentRecordUpdated()
callbacks are each passed an
object of class
TWGManagedObject
and a long
value. The
TWGManagedObject
reference is the managed object instance which
was associated with the record. The long
value is an object ID
(either a managed object ID or a group ID) which indicates which subscription
instance was responsible for triggering this notification. This allows a task
registered to receive update notification to unambiguously identify whether
a particular notification occurred via a registration for a specific managed
object, or whether the notification was received because the managed object
was a member of a group which registered to receive the record.
The client receives a Command
whose command code is the one
specified when the subscription command was
sent to register the agent for update notification.
BOOL ServiceNodeSubclass::CommandReceived(Command &cmd) { DistConfigMgrSubscribeNotice *notice = NULL; switch (cmd.CommandCode()) { case COMMAND_CODE_SPECIFIED_IN_DIST_CONFIG_MGR_SUBSCRIBE_CMD: notice = new DistConfigMgrSubscribeNotice(cmd); if (notice != NULL && notice->numRecordsReturned() > 0) { for (int i = 0; i < notice->numRecordsReturned(); i++) { // process initial configuration records } } break; // handle other commands } return TRUE; }
The DistConfigMgrSubscribeNotice
is a utility class which
wraps the command sent by the server in response to a record modification.
The constructor for the DistConfigMgrSubscribeNotice
class accepts
a Command
object as a parameter, which should be the command
object returned by the server. DistConfigMgrSubscribeNotice
is
defined in dcfgmgr.hpp
:
class DistConfigMgrSubscribeNotice { public: ULONG numRecordsReturned() /* Get number of records returned */ ULONG getObjectID(ULONG index) /* Get object ID (System or group ID) of record 'index' */ UCHAR *getServerUID(ULONG index) /* Get server unique ID of record 'index' : returns pointer to UCHAR[8] */ char *getRecordID(ULONG index) /* Get record ID of record 'index' : returns pointer to char * (UTF8) */ BOOL isManagedObjectID(ULONG index) /* Test to see if record 'index' object ID is system/managed object ID */ BOOL isGroupID(ULONG index) /* Test to see if record 'index' object ID is group/TWGFilter object ID */ BOOL isDeletedRecord(ULONG index) /* Test to see if record 'index' has been deleted (as opposed to updated/added) */ UCHAR *getDataHashcode(ULONG index) /* Get data hashcode of record 'index' : returns pointer to UCHAR[16] */ ULONG getDataLength(ULONG index) /* Get data length of record 'index' */ void *getDataPointer(ULONG index) /* Get pointer to data of record 'index' */ };
The notify command will contain one or more record notifications; the
count is obtained by using the numRecordsReturned
method. Each
record notification contains status information such as the object ID that
this record was associated with, the unique server ID of the server which
posted this notification, the tag used to refer to this record, and the
record data itself. The status information for each record notification is
accessed by passing a zero-based index parameter to the methods provided by the
DistConfigMgrSubscribeNotice
class.