Object oriented programming based global registry system, method, and article of manufacture5822580Abstract A data-driven global registry method for use to extend a framework in an object oriented programming (OOP) based computer system is provided. The method includes providing a new class defined in a shared class library which has data members and member functions related to a particular task. In addition, a new class attributes file which specifies attributes associated with the new class is generated. The new class attributes file is placed in a global registry configuration directory in a computer system such that a plurality of client applications can access the global registry configuration directory to determine if the new class has been installed in the class library. In an alternative embodiment, an object-based global registry method is provided. In addition, a storage device readable by a computer system for implementing either OOP-based global registry method and OOP-based global registries themselves are provided. Claims What is claimed is: Description FIELD OF THE INVENTION
TABLE 1
______________________________________
class TObjectCreator : public MCollectible {
public:
MCollectibleDeclarationsMacro(TObjectCreator);
.about.TObjectCreator ();
// copy and streaming operators are not shown here
void*CreateObject() = 0;
protected:
TObjectCreator ();
};
class TMediaSequenceCreator: public TObjectCreator {
public:
MCollectibleDeclarationsMacro(TMediaSequenceCreator);
TMediaSequenceCreator();
.about.TMediaSequenceCreator();
// copy and streaming operators are not shown here
void* CreateObject(); // it delegates to CreateSequence()
// attribute querying methods
TMediaType GetMediaType();
protected:
TMediaSequence* CreateSequence();
};
______________________________________
FIG. 2 details how only the creator classes 202, 204 are flattened into and resurrected from a file 200 (i.e., registry store). Several items are stored persistently on disk, including: the registry store 200, shared libraries 206, 208 which contain creator classes, and shared libraries 210, 212 which contain the classes to be created. It should be noted that this object-based global registry mechanism is dependent on the streaming operations of each creator class, which may be written by different software developers. Any problems with flattening or resurrecting these creator classes 202, 204 could result in a corrupt registry store 200. The registry store 200 also needs to deal with versioning issues (i.e., backward and forward data compatibility has to be maintained). This object-based registry scheme may be less efficient, because meta information (i.e., attributes about the objects to be created) is contained in the creators and in order to query or display these attributes, the shared libraries 206, 208 containing the creator classes would first have to be loaded. To dynamically add a new class into the CommonPoint frameworks (i.e., without recompiling and rebuilding), the following steps may be performed: Instead of hard-coding the creation of the object, the framework uses the registry mechanism to look up the object. The developer subclasses TObjectCreator (overriding CreateObject to call its own CreateXYZ method and providing methods for querying attributes of the object). The developer writes the new class (usually a subclass of an existing class in the frameworks), and it provides an Initialize() method. Returning to the time/media example, a framework does not hard-code the creation of a movie player (i.e., fplayer=new TMovie). Instead, it asks the media registry, which in turn gets a movie creator from the global registry, to return an appropriate player for the movie media type. In this case, to add a new preferred embodiment TWhizzyPlayer to the framework, the developer has to provide two new classes: TWhizzyPlayerCreator and TWhizzyPlayer. Finally, the developer needs to install the new creator object by flattening it out to the registry store. This object-based global registry scheme has the advantage of being flexible (e.g., it can provide protocol to query attributes). However, the disadvantages are that a performance cost is incurred for loading shared libraries just for attribute lookup, it is vulnerable to streaming problems, and it requires developers to write more code. Another way that the global class registry service can be implemented is an alternative preferred embodiment data-driven approach. The data-driven approach has many advantages over the object-based approach described above and this will become clear from the following description. Some of the advantages include: a more robust implementation (i.e., no streaming of objects), easier to use (e.g., a user only needs to edit a text file to change the installation), less software code needs to be written (e.g., no creator classes need to be written), and more efficient use of processing resources (e.g., do not need to load shared libraries just to query attributes). These advantages are gained at the expense of flexibility (e.g., a record format much be defined which each class attribute description follows). Even though this approach may not be as flexible, it may be extendible so that new features can be added (e.g., the record length may not be fixed, but the first few fields may be set in the registry store file) . In a typical data-driven approach, the global class registry 300 includes a set of data files 302, 304 that describe new extensions (i.e., classes) to be added dynamically during runtime to the CommonPoint environment. Conceptually, these entries form a logical table, where each row of the table represents an entry 302, 304 in the registry 300 as illustrated in FIG. 3. It should be noted that the size of each entry may vary according to the number of attributes for each new class. A client application looks up an entry by specifying a set of attributes in a query. For example, the time/media framework can look up a view for an audio document from the global registry 300 by specifying attributes 306 like Domain=Toolbox, Category=View, Version=1.0, and Size=160.times.120, etc. as shown in entry 304 of FIG. 4. A data-driven mechanism for dynamically installing new classes into a framework may include two parts, including: the interface for writing (i.e., adding, removing, and editing entries) the global registry 300 and the interface for reading the registry 300. On the "writer" side, the one issue is to provide a portable solution for installing entries in the registry 300, which does not require the CommonPoint operating environment to be running during installation time. On the reader side, the global registry 300 will provide an Application Programming Interface (API) that allows client applications to retrieve entries from the registry 300. An attribute 302 is simply a string describing an interesting characteristic of a new class. The value of an attribute 302 indicates what the attribute holds, and can be one of four possible types: string, integer, array (e.g., a Tanything object in the CommonPoint operating environment), or binary object (e.g., wrapped by a TAnyExtension object in the CommonPoint operating environment). The attribute name and value character strings can be encoded as ASCII or Unicode. The size limit for an attribute preferably is 1024 characters. ASCII preferably is used as the default with Unicode strings being used, if necessary, to input foreign language characters. For example, the "domain" 308 in which the class belongs is an attribute and "Toolbox" 310 is a possible value for that attribute. The version number 312 of the class is another example of an attribute and its value 314 can be something like "1.0". There are four groups of attributes that a developer needs to provide for each new entry (i.e., subclass) to be added to the registry. They are the required attributes (defined by the global registry 300), optional attributes (domain owner), internal attributes (developer of the new class), and headers attributes (developer of the new class). The required and optional attributes describe some interesting characteristics of an entry (i.e., class) in the registry 300. They are both used in specifying a query for looking up the entry. If more than one entry satisfies the query, then all of the entries will be returned by the lookup. The required attributes preferably include Domain 308, Category 312, Version 316, while the optional attributes include ID (not shown), domain-specific attributes, and other attributes. The global registry 300 does not specify the policy for how the ID value should be generated. It is the responsibility of the domain owner to specify such a policy for that particular domain. Domain-specific attributes are specified by a particular domain owner, and these attributes may be required for that domain. A developer can add optional attributes to characterize the new class. For example, format and size 318 could be interesting attributes for a new time/media class component. The internal attributes are not used in the lookup process--instead, they are used only by the registry 300 for its internal operations. The internal attributes may include APIClass 320, APIPackage 322, ImpClass 324, and ImpPackage 326. These attributes must be provided by the developer. The headers attributes include all header files needed to compile the new classes (e.g., <Taligent/TimeMedia.h>). The attributes for each entry 302, 304 in the registry 300 are stored persistently in a file in a storage device. The attributes preferably are written out in a generic file format such as the TAnything file format. The Tanything file format consists of slot-name-and-value pairs as shown below in Table 2.
TABLE 2
______________________________________
/SlotName1 Value1
. . .
/SlotNameN ValueN
}
______________________________________
The file itself can be either ASCII or Unicode as noted above. The slot names (SlotName1 . . . SlotNameN) and values (Value1 . . . ValueN) can be either (1) ASCII characters with special %xxxx notation for Unicode characters, or (2) Unicode characters. Moreover, the value can be another TAnything (i.e., the structure can be recursive). The client application needs to create an attribute file in this format using whatever (ASCII or Unicode) text editor available on his or her platform. As an example, for a time/media player, the attribute file may look as shown in Table 3.
TABLE 3
______________________________________
/Domain "TimeMedia"
/Category "Player"
/Version "1.0"
/Optional {
/ID "83701349"
/Format "AVI"
}
/Internal {
/APIClass "TPlayer"
/APIPackage "CP"
/ImpClass "TAVIMovie"
/ImpPackage "OS/2"
}
/Headers {
/H0 "<Taligent/TimeMedia.h>"
/H1 "<Taligent/AVIMovie.h>"
}
}
______________________________________
A "domain" simply corresponds to a particular CommonPoint subsystem. For example, time/media, a workspace, a file system, or translators are possible domains. As mentioned above, the domain name is a required attribute. Initially, the domain name space is not fully hierarchical; it has only one level of hierarchy. Within each domain, the structure is flat--there is no subdomain. By making this design decision current clients of the global registry 300 do not need hierarchical domain names. In addition, this flat structure simplifies the global registry 300 API. Since the API does not need to provide methods to traverse the domain hierarchy, it is easier to port to other platforms. On other platforms, such as the Microsoft Windows NT platform, a hierarchy an be built on top of this flat structure (e.g., the domain name attribute can contain a pathname value instead of a simple name) This flat structure can be easily extended to a hierarchical structure in the future when client applications have such a need. The attribute file format supports internationalization by supporting Unicode. The attribute file (which preferably is in the TAnything format) can be either ASCII or Unicode. That is, the registry (more specifically, TAnything) supports both ASCII and Unicode encodings. Even in the ASCII case, TAnything supports Unicode by allowing both the slot name and its value to be Unicode characters using the special %xxxx notation, where x represents a one byte hex number. Also, it enables non-US developers to create these Unicode attribute files. Since TAnything supports Unicode, a Unicode text editor can be used to edit these Unicode characters. Further, it does not support any user-visible attributes (labels, images, icons, etc.), which should be stored in a higher-level user-data registry mechanism and localized using an archive mechanism. To dynamically add a new class to the frameworks, the developer writes the new class and creates a shared library for it. The developer then follows the following process to register the new class into CommonPoint operating environment: Generate the attribute file(s) for the new class(es). The developer needs to create an attribute file and enter the necessary attribute information in the above file format. This file preferably is portable across all platforms. Perform any necessary platform-specific conversion for the attribute file(s). For a particular platform, certain platform-specific conversion needs to be performed on the attribute file, and the result is a non-portable "thing" that will be installed into the registry. Installing the new class(es). To install the new class into the system, the developer needs to provide both the shared library 328 for the new class and the associated attribute file 304 as described in the above steps. The installation tools used in this step are platform specific. For example, for AIX, installation process is simply moving the shared library 328 into the library directory and the attribute file 304 into the configuration directory. For Microsoft Windows NT, installation involves running Setup.exe (which invokes an installer program that understands the format of the converted attribute file); it will register the new class into the Windows NT Registry. In order for this registration mechanism (i.e., the ability to dynamically plug in a new class or component) to work, the relevant frameworks have to use the global class registry 300 to create an instance of the class or component instead of hard-coding the creation of that class or component. The global registry 300 interface consists of three client-visible parts: global registry API, an attribute file, and shared library 328 containing the new class. The developer provides the shared library 328 and attribute file 304, while the global registry 300 provides a set of API classes. The interactions between the registry 300 and a calling client application can be summarized into the following steps: The calling client application (i.e., caller) generates a query based on a set of attributes, and it calls the registry lookup method, which returns back an iterator for a list of object descriptors. The caller can access attributes about the objects without having to load the shared library 328 containing the new classes. Given a set of preferences, the caller can further refine the selection by first examining the attributes of the objects (via the returned descriptors) before creating the objects (e.g., via the GlobalRegistryCreateObject global function). The selected object 330 is then initialized with the appropriate parameters by the caller in the calling context. The precise interface through which client applications can retrieve objects from the class registry is described in the following description. It contains detailed information about the API classes--their roles, their primary protocols, and some code samples. The preferred embodiment API consists of three major classes: TGlobalRegistryQuery, TGlobalRegistrylterator, and TGlobalRegistryObjectDescriptor. For the convenience of clients, the class TGlobalRegistryQuery provides a set of tokens for looking up the value of an attribute. It also captures the attributes describing the lookup query. The class TGlobalRegistryIterator provides a set of constructors for retrieving entries from the registry and to access the results of the query. Using the iterator, a client application can access the object descriptors one at a time, which are represented by the class TGlobalRegistryObjectDescriptor. The object descriptor contains information (such as the API class and package names, and other attributes) needed to create the objects. In the following class declarations, only part of the methods are shown, and implementation details are not included. The TAnything class is a flexible associative (key-value pair) array that is efficient for storing primitive types and other types via the TAnyExtensionOf<AType>object wrapper. Table 4 shows an example of the TGlobalRegistryQuery class.
TABLE 4
______________________________________
class TGlobalRegistryQuery {
public:
static const TSimpleUnicodeArray& kDomain;
static const TSimpleUnicodeArray& kCategory;
static const TSimpleUnicodeArray& kVersion;
static const TSimpleUnicodeArray& kOptional;
static const TSimpleUnicodeArray& kID; // global ID
static const TSimpleUnicodeArray& kInternal;
static const TSimpleUnicodeArray& kAPIClass;
static const TSimpleUnicodeArray& kAPIPackage;
static const TSimpleUnicodeArray& kImpClass;
static const TSimpleUnicodeArray& kImpPackage;
static const TSimpleUnicodeArray& kHeaders;
TGlobalRegistryQuery(const TAnything& attributeValuePairs);
TGlobalRegistryQuery();
TGlobalRegistryQuery(const TGlobalRegistryQuery&);
.about.TGlobalRegistryQuery();
// supports equality and streaming operations
bool operator==(const TGlobalRegistryQuery&) const;
TStream& operator>>=(Tstream&)const;
TStream& operator<<=(TStream&);
// called by TGlobalRegistryIterator
bool Match(const TAnything& attributesValues) const;
const TAnything* GetAttributes() const;
void SetAttributes(const TAnything& attributeValuePairs);
void RemoveAttributes(const TAnything& attributeValuePairs);
private:
// assignment operator is not supported.
TGlobalRegistryQuery& operator=(const TGlobalRegistryQuery&);
};
______________________________________
In addition to providing a set of slot name tokens that can be used as keys to lookup their corresponding values in a TAnything data structure, the TGlobalRegistryQuery class provides a protocol for specifying (and modifying) a set of attributes; it has the Match() method that the TGlobalRegistrylterator class can use to evaluate whether a particular entry matches the specified attributes. If the match fails, the SetAttributes() method can be used to modify the query and re-submit the query again. For example, if the initial query contains attributes Domain=TimeMedia, Category=Player, Version=3.0, Format=MPEG, etc. If there was no MPEG player version 3.0, the client application can modify the one or more attributes via the SetAttributes() method. The TAnything argument attributeValuePairs would contain the new attribute-value pairs (e.g., Version=2.0, Format=QuickTime). The GetAttributes() and RemoveAttributes() methods allow the client to modify the attributes in the query. Table 5 shows an example of the TGlobalRegistrylterator class.
TABLE 5
______________________________________
class TGlobalRegistryIterator : public
TIteratorOver<TGlobalRegistryObjectDescriptor> {
public:
TGlobalRegistryIterator(const TGlobalRegistryQuery*
queryToAdopt);
TGlobalRegistryIterator(const TUnicodeArray& domain, const
TUnicodeArray&
key);
TGlobalRegistryIterator(const TUnicodeArray& domain);
.about.TGlobalRegistryIterator();
// supports equality operation
bool operator==(const TGlobalRegistryIterator&) const;
const TGlobalRegistryObjectDescriptor* First();
const TGlobalRegistryObjectDescriptor* Next();
private;
// default & copy constructors, assignment operator, streaming
operators
// are not supported.
TGlobalRegistryIterator();
TGlobalRegistryIterator(const TGlobalRegistryIterator&);
TGlobalRegistryIterator& operator=(constTGlobalRegistryIterator&);
TStream& operator>>=(TStream&) const;
TStream& operator<<=(TStream&);
};
______________________________________
The TGlobalRegistrylterator class is the primary interface for the global registry 300; the constructors provide the protocol for retrieving "object descriptors" that are required for instantiating new classes. The first constructor takes a query as an argument. The resulting iterator can be used to iterate over a small list of object descriptors matching the attributes specified in the query. The second constructor is for client application's convenience, and takes the domain and key values as arguments. Because the key is a unique value within a particular domain, there is at most one descriptor that will be returned, and it can be accessed via the iterator's First() method. If there is no match, the method returns a NIL pointer. The last constructor is for client application's convenience. The iterator can be used to iterate over a list of object descriptors for a particular domain. If the search fails, the First() method returns a NIL pointer. The iterator manages the storage for the returned descriptors, so that the returned pointers are just aliases to descriptor objects owned by the iterator. Table 6 shows an example of the TGlobalRegistryObjectDescriptor class.
TABLE 6
______________________________________
class TGlobalRegistryObjectDescriptor {
public:
TaligentTypeExtensionDeclarationsMacro(TGlobalRegistryOb-
jectDescriptor)
TGlobalRegistryObjectDescriptor(const TAnything& attributes);
.about.TGlobalRegistryObjectDescriptor();
// supports hashing, equality, and streaming operations
long Hash() const;
bool operator==(const TGlobalRegistryObjectDescriptor&) const;
TStream& operator>>=(TStream&) const;
TStream& operator<<=(TStream&);
void GetAttributes(TAnything& attributeValues) const;
protected:
TGlobalRegistryObjectDescriptor();
private:
// copy constructor and assignment operator are not
supported.
TGlobalRegistryObjectDescriptor(const
TGlobalRegistryObjectDescriptor&);
TGlobalRegistryObjectDescriptor& operator=(const
TGlobalRegistryObjectDescriptor&);
};
______________________________________
The primary protocol provided by TGlobalRegistryObjectDescriptor is the GetAttributes() method for accessing the attributes of a new class. A client application should use the global function GlobalRegistryCreateObject (an example of which is shown in Table 7) to create an instance of the new class in a type-safe manner.
TABLE 7
______________________________________
template<class AType>
void GlobalRegistryCreateObject(AType*& theResultPtr,
const TGlobalRegistryObjectDescriptor&
objectType,
const TAllocationHeap& heap =
TAllocation::kDefaultHeap);
______________________________________
The arguments needed by this global function are a base type pointer, an object descriptor, and a heap object. Returning once again to the time/media registry example, the registry interacts with the global registry 300 to create an instance of a new class (e.g., TAVIMovie) in the following way. First, it provides a set of attributes and asks the global registry 300 to return a list of object descriptors that match these attributes. Some example ways to find this match are shown in Table 8, including: lookup by query, lookup by key, and iterate over domain. Second, the time/media registry will use a set of user defined preferences (set by its client application before the request to get a list of sequences) to select an appropriate sequence descriptor. It then creates an instance of the new player via the GlobalRegistryCreateObject global function using the returned object descriptor as an argument. At this point, the time/media registry will initialize the default-constructed player with the appropriate parameters, and then return it to its client application.
TABLE 8
______________________________________
1) Lookup by Query
TAnything attributes;
attributes›TGlobalRegistryQuery::kDomain! = "TimeMedia";
attributes›TGlobalRegistryQuery::kCategory! = "Player";
attributes›TGlobalRegistryQuery::kVersion! = "1.0";
attributes›"Optional"!›"Format"! = "QuickTime";
attributes›"Optional"!›"Size"! = "160.times.120";
TGlobalRegistryQuery query(attributes);
TGlobalRegistryIterator *iterator = new
TGlobalRegistryIterator(query); // create
on heap
if (iterator->First() == NIL) {
// modify attributes and try again
attributes›"Optional"!›"Format"! = "AVI"; // change
"Format" to new value
attributes›"Optional"!.Remove("Size"); // remove "Size" attribute
query.SetAttributes(attributes);
delete iterator;
iterator = new TGlobalRegistryIterator(query);
TPlayer* player = NIL;
TAnything returnedAttributes;
bool found = false;
for (const TGlobalRegistryObjectDescriptor *desc = iterator->First();
desc |= NIL && found == false;
desc = iterator->Next()) {
desc->GetAttributes(returnedAttributes);
if (returnedAttributes›TGlobalRegistry::kImpClass! ==
"TAVIMovie") {
found = true;
::GlobalRegistryCreateObject(player, *desc);
}
}
delete iterator;
if (player |= NIL) {
// success|
player->AdoptSequence(movieSequence);
// Do whatever you want with this new movie player
. . .
} // else we give up|
2) Lookup by Key
TSimpleUnicodeArray domain("Workspace");
TSimpleUnicodeArray key("84903936"); // this is a global ID
TGlobalRegistryIterator iterator(domain, key); // create local variable
on
stack
TGlobalRegistryObjectDescriptor* desc = iterator.First();
if (desc |= NIL) {
TPrinter* printer;
::GlobalRegistryCreateObject(printer, *desc);
// Do whatever you want with this new printer
} // else we exit here
3) Iterate over domain
TSimpleUnicodeArray domain("Translators");
TGlobalRegistryIterator iterator(domain); // create local variable on
stack
if (iterator.First() |= NIL) {
TTranslator *translator;
TAnything returnedAttributes;
for (const TGlobalRegistryObjectDescriptor *desc = iterator.First();
desc |= NIL && found == false;
desc = iterator.Next()) {
desc->GetAttributes(returnedAttributes);
// select one from the returned list
. . .
::GlobalRegistryCreateObject(translator, *desc);
// Do whatever you want with this new translator
} // else we exit here
______________________________________
One problem that may arise is when the returned list contains more than one entry from the global registry 300. Some possible ways to solve this problem are to use information from user preferences, if available, to pick the "best" candidate. For example, the TGlobalRegistryObjectDescriptor provides a protocol for accessing attributes of a new class, so that a client application can select the most appropriate descriptor based on some user specified preferences before using the descriptor to create an instance of the new class. Another solution is when there are multiple entries for different versions of the same type of object, then pick the one with the most recent version number. Finally, a simple solution if all else fails, then the first one returned by the iterator is picked by default. Now that the external design specification has been completed, some internal design specification will be discussed. The implementation of the registry 300 on AIX preferably uses a server because this approach is more efficient and makes the problem of updating the registry simpler (this is because the server is the only thing that knows about the data files in the configuration directory). The attributes of an entry 302, 304 preferably are be stored in a TAnything object. The registry's lookup table uses the following data structures: a dictionary of domain entries, where a domain is a dictionary of object entries. An object entry contains a TAnything, which encapsulates all the attributes associated with that object. The two level dictionary structure is needed to support the LookupByKey() method, while the domain dictionary structure supports the LookupByDomain() method. The domain dictionary data structure also efficiently supports the query mechanism where all the elements in a domain need to be compared sequentially against the specified attributes in the query. Several implementation tradeoffs exist. For example, whether to have a class server or to locally access an object. The global registry can be implemented as a server. Alternatively, clients could create a "local access object" to the global registry. The access object would scan the configuration directory and build an in-memory image of the installed classes when the object is being constructed. The some tradeoffs between a server and an access object are as follows: A server offers global access and arbitrates multiple accesses. A server has smaller client application delays during creation, because an access object needs to build up an internal data structure for all the entries in the registry. This operation may involve scan all the file in a directory. An access object does not have all the server-related system overhead. An access object is more reliable. Another implementation tradeoff is between using a query or a simple iterator. The tradeoff here is power and flexibility versus complexity. Having a query mechanism allows a client application to focus the lookup and get back a small list of candidates, while using an iterator approach leaves the task of selecting the returned values up to the client application. Of course, the iterator is a much simpler mechanism to implement because it simply gives access to all the objects in a particular domain to the client application. To satisfy the needs of different client applications, the global registry preferably provides support for both types of retrieval. Another implementation tradeoff is between having a polymorphic or monomorphic TGlobalRegistryQuery class. The major benefit of making TGlobalRegistryQuery polymorphic is that common "queries" (e.g., lookup by key, iterator for a domain) can be organized into a hierarchy of query subclasses, while a monomorphic query class approach would express these common queries as explicit methods in another class (e.g., either as TGlobalRegistry Lookup() methods or TGlobalRegistrylterator constructors). Because the query object will be streamed across to an object or class server, TGlobalRegistryQuery class preferably is designed to be a monomorphic class for efficiency and reliability reasons. The internal architecture preferably includes several internal support classes for the global registry 300 API. Examples of these classes are TDomainEntry, TObjectEntry, TGlobalRegistryCaller, and TGlobalRegistryDispatcher. The TDomainEntry's constructor, shown below in Table 9, takes a character string, a Unicode array, or a standard text as an argument for a domain name, so that it could support the direct lookup of the domain. The Hash() and operator==are needed to support the comparison operation being performed during a lookup in the "dictionary". For the time/media domain, TGlobalRegistryDispatcher can lookup the domain entry as follows: fDomainLookupTable›TDomainEntry("TimeMedia")!. The TDomainEntry, in turn, has a lookup table for all the objects in the domain. This lookup table is may be a "dictionary" data structure implemented using a TSetOf, because this is a more efficient approach. Also, because these are internal support classes used only by the TGlobalRegistryDispatcher in its implementation, the data structure is made public for efficient access (so there is no need to provide accessor methods for the data members).
TABLE 9
______________________________________
class TDomainEntry {
public:
TDomainEntry(const char domainName›!);
TDomainEntry(const TSimpleUnicodeArray&);
TDomainEntry(const TStandardText&);
.about.TDomainEntry();
long Hash() const;
bool operator==(const TDomainEntry &) const;
TSetOf<TObjectEntry> fObjectLookupTable;
TSimpleUnicodeArray fName;
};
______________________________________
Similarly, the TObjectEntry 's constructor, shown below in Table 10, takes a character string, a Unicode array, or a standard text as an argument for a global ID, so that it could support the direct lookup of an object entry. The Hash() and operator==are needed to support the comparison operation being performed during a lookup in the "dictionary". For the time/media domain, the TGlobalRegistryDispatcher can lookup the player object as follows: domainEntry.fObjectLookupTable›TObjectEntry("0003")!. The TObjectEntry constructor contains a TAnything, which consists of a list of attribute-value pairs.
TABLE 10
______________________________________
class TObjectEntry {
public:
TObjectEntry(const char globalID›!);
TObjectEntry(const TSimpleUnicodeArray&);
TObjectEntry(const TStandardText&);
.about.TObjectEntry();
long Hash() const;
bool operator==(const TObjectEntry&) const;
TAnything fObjectAttributes;
TSimpleUnicodeArray fID;
};
______________________________________
The TGlobalRegistryCaller class, shown in Table 11, is used by the TGlobalRegistrylterator to make an RPC to the dispatcher. The primary methods are LookupByQuery(), LookupByKey(), LookupByDomain(). These methods invoke the corresponding dispatcher methods to perform the lookup and return the results. The ShutDown() method is used to send a request to the registry server to shut down itself.
TABLE 11
______________________________________
class TGlobalRegistryCaller : protected MRemoteCaller {
public:
TGlobalRegistryCaller();
.about.TGlobalRegistryCaller();
TCollectionOf<TGlobalRegistryObjectDescriptor<*
LookupByQuery(const TGlobalRegistryQuery& query);
TGlobalRegistryObjectDescriptor*
LookupByKey(const TUnicodeArray& domainName, const
TUnicodeArray&
key);
TCollectionOf<TGlobalRegistryObjectDescriptor>*
LookupByDomain(const TUnicodeArray& domainName);
void ShutDown();
};
______________________________________
Table 12 shows an example TGlobalRegistryDispatcher class.
TABLE 12
______________________________________
class TGlobalRegistryDispatcher : public MRemoteDispatcher {
public:
static const THostSpecificPathName kConfigurationDirectory;
enum EGlobalRegistryRequest {
kLookupByKey,
kLookupByQuery,
kLookupByDomain,
kShutdown,
kMaxRequest = kShutdown
};
TGlobalRegistryDispatcher();
.about.TGlobalRegistryDispatcher();
TCollectionOf<TGlobalRegistryObjectDescriptor>*
LookupByQuery(const TGlobalRegistryQuery& query);
TGlobalRegistryObjectDescriptor*
LookupByKey(const TUnicodeArray& domainName, const
TUnicodeArray&
key);
TCollectionOf<TGlobalRegistryObjectDescriptor>*
LookupByDomain(const TUnicodeArray& domainName);
protected:
void UpdateRegistry();
void Initialize();
};
______________________________________
An UpdateRegistry() function preferably is used to scan the configuration directory and import all the attribute data into corresponding TAnything objects from the files in the directory; it also builds up the two-level dictionary data structure as described above. An Initialize() function finds the configuration directory and then calls an UpdateRegistry() function to set up the internal data structure. The LookupByKey, LookupByQuery, and LookupByDomain functions are used to implement the semantics of the corresponding TGlobalRegistryCaller class functions described above. The present invention can be summarized in reference to FIG. 5 which is a flowchart of the preferred embodiment object oriented programming based data-driven global registry method for use by a client application and a server on a computer system. This method is performed by device-implemented steps in a series of distinct processing steps 500-518 that can be implemented in one or more processors. A new class defined in a shared class library on the server is provided 502 which has data members and member functions related to a particular task. A new class attributes file is generated 504 which specifies attributes associated with the new class. The new class attributes file is placed 506 in a global registry configuration directory on the server such that a plurality of client applications can access the global registry configuration directory to determine if the new class has been installed in the class library. Subsequently, the global registry configuration directory is scanned 508 for class attribute files to generate a lookup table containing an entry for each class and the associated attributes in the class attribute files. A list of classes is retrieved 510 which match a client applications attribute query based on contents of a plurality class attributes files in the global registry configuration directory. This set of information may be collected in the lookup table to facilitate searching for classes matching the query. A particular class is selected 512 from the retrieved list of classes based on a predetermined preference set. An instance of the selected class is generated 514 as a new object and the new object is initialized 516 with client application specified parameters. The new object may be initialized with default client application specified parameters unless particular client application specified parameters are obtained from the client application. In addition, the present invention can be summarized in reference to FIG. 6 which is a flowchart of another preferred embodiment object oriented programming based global registry method for use by a client application and a server on a computer system which is object-based. This method is performed by device-implemented steps in a series of distinct processing steps 600-616 that can be implemented in one or more processors. A new class defined in a shared class library on the server is provided 602 which has data members and member functions related to a particular task. In addition, an object factory class for the new class, defined in the shared class library, is provided 604 which has data members and member functions related to creating instances of the new class and related to attributes associated with the new class such that a plurality of client applications can access the class library to determine if the new class has been installed in the class library by accessing the provided object factory class. The object factory class for the new class preferably is stored 606 along with a plurality of other object factory classes in a global registry file. Subsequently, a list of object factory classes which match a client application attribute query is retrieved 608 based on contents of a plurality object factory classes in the class library. Only those object factory classes which match a client applications attribute query need to be resurrected from the global registry file to form the list of object factory classes. However, other classes may be resurrected without modifying the schema of this global registry process. A particular object factory class is selected 610 from the retrieved list of object factory classes based on a predetermined preference set. An instance of the selected class is generated 612 as a new object with the selected object factory class and the new object is initialized 614 with client application specified parameters. The new object may be initialized with default client application specified parameters unless particular client application specified parameters are obtained from the client application. Either global registry process may be implemented in a computer system by storing the classes and files in a storage device 120 and using a processor 110 in conjunction with RAM 114 and/or ROM to generate objects from the stored classes. A communications adapter 134 may communicate with a server, operatively coupled to the computer system by a communication network, to obtain information from a global registry file or directory the computer system and other computer systems. In addition, a program storage device may be created which is readable by a computer system tangibly embodying a program of instructions executable by the computer system. This program of instructions would perform one or more parts of either of the object oriented programming based global registry methods described above. It is to be understood that even though numerous characteristics and advantages of various embodiments of the present invention have been set forth in the foregoing description, together with details of the structure and function of various embodiments of the invention, this disclosure is illustrative only, and changes may be made in detail, especially in matters of structure and arrangement of parts within the principles of the present invention to the full extent indicated by the broad general meaning of the terms in which the appended claims are expressed. For example, the actual names or division or functions may be changed between the OOP classes and objects, detailed above, while maintaining substantially the same functionality without departing from the scope and spirit of the present invention.
|
Same subclass Same class Consider this |
||||||||||
