Method and apparatus for enabling a persistent metastate for objects in an object oriented environment5870753Abstract A method and apparatus for enabling the maintenance of multiple metastates for a persistent object without increasing the size of the object reference. An object reference data structure is provided in memory and persistent storage to carry a key which contains a universal unique identification (UUID) of an object and other generic common information of the object. A table, maintained by a server process in memory and persistent storage, contains tuples consisting of a key and metastates for persistent objects. A technique is also provided for interacting with the object for the exchange of metastate and the handling of lifecycle issues such as object creation, passivation (removing from memory), reactivation (restoring to memory) and destruction. Additional types of metastates may be added without reimplementation of the server which manages the persistent objects. Claims What we claim is: Description FIELD OF THE INVENTION
TABLE A
______________________________________
Structure key.sub.-- structure {
unsigned long maximum;
unsigned long length;
struct element.sub.-- structure {
unsigned long offset;
/*Offset to the next element
in bytes from the beginning
of the first element
structure. */
TCKind element.sub.-- id.sub.-- type;
unsigned short version.sub.-- major;
unsigned short version.sub.-- minor;
unsigned long length;
<element.sub.-- id.sub.-- type> element.sub.-- id ›<element.length>!;
} element ›<key.length>!;
string class.sub.-- name;
} key;
/* Notice that <element.length> and <element.sub.-- id.sub.-- type>
make
the element.sub.-- structure variable length. Likewise,
<key.length> make the overall key variable length. You
should use the key.element ›n!. offset+&key.element ›0!
to determine the position of the next element structure
overlay in memory. */
An example of a server key using the above structure looks
like:
key.maximum=2;
key.length=2;
key.element ›0! .offset=0+sizeof (long) +sizeof (Typecode) +
sizeof (short) *2+sizeof (long) +key.element ›0! .length;
key.element ›0! .element_id_type=tk.sub.-- string;
key.element ›0! .version_major=3;
key.element ›0! .version.sub.-- minor=0;
key.element ›0! .length=9;
key.element ›0! .element_id="myServer"
key.element ›1! .offset=key.element ›0! .offset+sizeof (long) +
sizeof (Typecode) +sizeof (short) *2+sizeof (long) +key.element
›1! .length;
key.element ›1! .element_id_type=tk.sub.-- octet;
key.element ›1! .version_major=3;
key.element ›1! .version.sub.-- minor=0;
key.element ›1! .length=16;
key.element ›1! .element.sub.-- id=<UUID>;
key.class.sub.-- name=<class name>;
______________________________________
As previously described, the invention allows the server to maintain multiple metastates for a persistent object without increasing the size of the object reference. In addition, a server which supports persistent objects must provide mechanisms to register objects as persistent, create proper object references for persistent objects, and manage the persistent objects. In order to manage persistent objects, the server object and persistent objects must collaborate. This collaboration is achieved by providing standard interfaces for the server and persistent objects. TABLE B provides an example server class that contains the methods to achieve the basic collaboration between the server object and persistent objects. These methods can be divided into three categories:
______________________________________
(1) methods make.sub.-- persistent (),
delete.sub.-- persistence (), and is.sub.-- persistent () to register
an object as a persistent object, delete the
persistent aspect of an object, and query whether the
object is persistent.
(2) methods such as store.sub.-- individual.sub.-- metastate (),
store.sub.-- entire.sub.-- metastate (), and restore.sub.-- metastate ()
to store and restore metastates of persistent
objects.
(3) methods such as passivate.sub.-- object () and
passivate.sub.-- all.sub.-- objects () to remove in-memory
objects without losing the persistence of
objects
______________________________________
The interface definition language (IDL) for the example server defined in the invention is shown in TABLE B.
TABLE B
______________________________________
Interface Server : BaseServer {
Object make.sub.-- persistent (in Object referenced.sub.-- object);
/* The input referenced.sub.-- object is made persistent
and a new reference for this persistent object is
returned */
void delete.sub.-- persistence (in Object referenced.sub.-- object);
/* The persistence of the referenced.sub.-- object is
removed. */
boolean is.sub.-- persistent (in Object referenced.sub.-- object);
/* Query whether the referenced.sub.-- object is
persistent. */
void store.sub.-- individual.sub.-- metastate
(in Object referenced.sub.-- object,
in class.sub.-- id.sub.-- e class.sub.-- id,
in any individual.sub.-- metadata);
/* Store an individual component of the metastate
individual.sub.-- metadata corresponding to class.sub.-- id for
the referenced.sub.-- object. */
void store.sub.-- entire.sub.-- metastate (in Object referenced.sub.--
object);
/* Store the entire metastate for the
referenced.sub.-- object object. */
void restore.sub.-- metastate (in Object referenced.sub.-- object);
/* Restore the object using the metastate on the
persistent store. */
void passivate.sub.-- object (in Object referenced.sub.-- object);
/* Remove the in-memory image of the persistent
object. */
void passivate.sub.-- all.sub.-- objects ();
/* Remove in-memory images of all persistent
objects */
};
______________________________________
As shown in TABLE B, the Server inherits from the BaseServer interface. The BaseServer interface defines methods, among many others, for importing and exporting object references which must be overridden in the Server interface to construct proper object references for persistent objects. An example of the BaseServer is in the Basic Object Adapter in CORBA specification, if implemented as a server. Turning now to FIG. 10, a flow chart for the make.sub.-- persistent.sub.-- ref() method for registering an object with a server object is shown. An object must be specifically registered with the server to become a persistent object. Prior to registering an object with the server, the object is merely a transient object, which is lost forever if certain operations occur, such as the normal or abnormal termination of the server process. Returning to FIG. 10, the procedure starts at block 100, with an in-memory object as its input, and proceeds immediately to block 102, where it is determined whether the object is persistent. The object is persistent if an entry for it exists in the reference-data-table of the server. If YES, at block 102, the procedure immediately ends. If NO, at block 102, the procedure manufactures a key at block 104, and utilizes the key to make a new entry in the reference data table using the key as shown in block 106. At block 108, the pointer to the in-memory object is put into the transient portion of the reference data table. An entry is then created in persistent storage for the persistent reference as shown in block 110, and the procedure ends at block 112. One skilled in the art will appreciate that no metastate data is actually obtained from the object during the registration process, because that data is obtained only when the object is stored or passivated. Turning now to FIG. 11, a procedure is shown for the delete.sub.-- persistence() method in the Server, which removes the persistent aspect of a persistent object. The procedure starts at block 120, using the pointer to an object as its input. At block 122, the procedure finds an entry for the object in the reference data table using the pointer. At block 124, a check is done to determine if an entry is found. If NO, the procedure ends at block 129. If YES, at block 126, a key is obtained for the input object from the entry in the reference data table that was found at block 122. At block 128, the procedure removes the metastate of the object from the persistent storage using the key, and ends at block 129. Turning now to FIG. 12, the is.sub.-- persistent() method in the Server for determining whether the input object is persistent is shown. The procedure starts at block 130, with the pointer to an object as input. At block 132, the procedure searches for an entry for the object in the reference data table using the pointer. A check is conducted at block 134, to determine if an entry has been found. If NO, at block 144, the procedure returns FALSE. If YES, at block 136, the key for the object is extracted from its entry in the reference data table. At block 138, the procedure searches for an entry in persistent storage using the key. A check is carried out at block 140, to determine if an entry is found. If YES, at block 142, it returns TRUE. Else, at block 144, it returns FALSE. As mentioned above, the BaseServer interface defines methods, for importing and exporting object references which must be overridden in the Server to construct proper object references for persistent objects. An object is exported by a server process when the object is handled outside of the server process. For example, when a new object is created in the server and is returned to the client as a return value of a method call, or as an OUT argument of a method, it is necessary to create its object reference. Turning now to FIG. 13, there is shown a procedure for exporting objects using the invention. This procedure is called when an object is passed outside of the server process as an argument to the invocation of a method on a remote object, or a return value, or OUT parameter of a method invocation on an object in the server process. The procedure begins at block 150, with the pointer to an object as its input, and proceeds immediately to block 152, which determines if the pointer exists in the reference data table. If YES, at block 154, the server looks up the key and creates an object reference containing the key as shown in block 156. If NO, at block 153, the method of the parent BaseServer class is used to generate the reference. At block 158, the server returns the object reference and the procedure ends at block 159. Turning now to FIG. 14, a procedure is shown for importing objects to a server. The procedure is invoked for object that has been received as an "IN" or "INOUT" argument to a method request from a remote object. The procedure begins at block 160, and proceeds immediately to block 162, which determines if the object reference contains a valid key which indicates that it is a valid object reference. If NO, an error flag is raised at block 164. If YES, at block 166, the procedure determines if the object reference is a persistent object reference by examining the structure of the key. If NO, at block 170, the procedure calls the method of importing objects that is defined in the parent class BaseServer. If the object reference is a persistent object, at block 168, the procedure extracts the key portion from the object reference. At block 172, the procedure determines if the key exists in the reference data table. If NO, an error flag is raised at block 173. If YES at block 172, at block 174, if a pointer associated with the key in the reference data table is not null, the pointer is returned in block 175. If the pointer is null at block 174, the procedure proceeds to block 176, and searches for the record containing the metastate of the object in persistent storage using the key. At block 178, a check is conducted to determine if the metastate was found. If NO, at block 180, the procedures generates an error indication. Else, at block 182, the uninitialized object is created. At block 184, the procedure invokes init-for-object-reactivation on the newly created object. The procedure proceeds to block 186, where it invokes reinit on the object and passes the metastate. At block 188, the procedure adds the object pointer to the reference data table and ends at block 190. In addition to supporting the methods concerning creating, deleting, and querying the persistence of an object, the server needs to support mechanisms for storing and restoring metastates. The architecture of the metastate allows any parent class to create its own metastate and add it to the entire metastate of the object, as well as to find the metastate that belongs to the class. In order to allow collaborative processing on the metastate structure, the architecture allows each of the parent classes to identify their own state and operate on that without affecting the state belonging to any other parent class of an object. Such a collaboration can be achieved, for example, if the server passes the entire metastate back and forth. The server does not operate on the metastate itself other than to write it out to the persistent storage or read it into memory as needed. Many architectures are possible for the metastate. It can be represented as a sequence of structures or as a list of structures. By defining the metastate as list of structures or by providing a special registration interface, the metastate can be dynamically configured. This makes it possible to design a server that does not need to be configured based on the a priori knowledge of the parent classes of persistent objects. Table C shows an illustrative representation of the metastate as a sequence of structures when the upper bound on the number of parents is small and known a priori. It is assumed that parent and ancestor classes of a persistent object have a fixed number which is known to the class.
TABLE C
______________________________________
enum {
Naming;
Events;
LifeCycle;
Persistence;
SSecurity;
ObjectIdentity;
Transactions;
Concurrency;
Externalization;
Attribute Persistence;
} service.sub.-- id.sub.-- e;
struct metastate.sub.-- struct {
service.sub.-- id.sub.-- e service.sub.-- id
unsigned short version.sub.-- major;
unsigned short version.sub.-- minor;
any service.sub.-- metastate;
} metastate.sub.-- struct.sub.-- t;
typedef sequence<metastate.sub.-- struct.sub.-- t> metastate.sub.--
______________________________________
t;
The example Server class defined in this invention provides three methods of exchanging metastate with it: (1) store.sub.-- individual.sub.-- metastate() which allows an individual parent class to store its metastate, (2) store.sub.-- entire.sub.-- metastate() which stores the metastate of all parent classes of an object, and (3) restore.sub.-- metastate() which reinitializes the object based on the previously stored metastate in the persistent storage. Turning now to FIG. 15, a procedure is shown for storing individual metastates using the invention. The procedure starts at block 200, with an input representing a class-id, individual metastate, and a pointer to an object. At block 202, the procedure searches for an entry for the object in the reference data table using the pointer. A check is carried out at block 204, to determine if an entry is found. If NO, at block 216, and error indication is generated. If YES, the procedure proceeds to block 206, to get the key of the object from the reference data table. At block 208, the procedure searches for the metastate from the persistent storage using the key, and modifies the metastate using the input individual metastate as shown in block 210. At block 212, the metastate is stored in persistent storage and the procedure ends at block 214. Referring now to FIG. 16, a procedure is shown for storing the entire metastate of an object. The procedure starts at block 220, with an input consisting of the pointer to the object. At block 222, a search for the key for the object is conducted in the reference data table. A check is performed at block 224, to determine if the key is found. If NO, at block 223, the procedure generates an error indication. Else, at block 226, the entire metastate for the object is obtained by calling capture() on the object. At block 228, the metastate is stored and the procedure ends at block 229. As explained below, the capture() method is available on every persistent object to obtain the entire metastate of the object. Referring to FIG. 17, a procedure is shown for restoring the metastate for an object. The procedure starts at block 230, using the pointer to the object as an input. At block 232, a search is carried out to find the key for the object in the reference data table using the pointer. A check is conducted at block 234, to determine if the key is found. If NO, at block 233, and error indication is generated. Else, at block 236, the procedure finds the metastate from persistent storage using the key. The procedure calls reinit() on the object at block 238, passes the metastate, and ends at block 239. As explained below, the reinit() method is available on every persistent object to reinitialize the object using the metastate. The last category of methods for the example Server class are concerned with the fact that an in-memory object and an entry in the persistent store are associated with a persistent object. In certain circumstances, it is necessary to remove the in-memory object, for example, when the reference-data-table gets too large, or when the server is temporarily shutdown. Two methods passivate.sub.-- object() and passivate.sub.-- all.sub.-- objects() are supported by the server. Referring now to FIG. 18, a procedure is shown for passivating an object. The procedure starts at block 240, using the pointer to the object as input. At block 241, the procedure searches for the key for the object in the reference data table using the pointer. A check is conducted at block 242, to determine if the key has been found. If NO, the procedure generates an error indication at block 243. If YES, at block 244, the procedure calls the capture() method on the object to get its metastate. At block 246, the procedure stores the metastate in the persistent storage, and gives an opportunity to the object to perform suitable un-initialization by calling uninit.sub.-- for.sub.-- object.sub.-- passivation() on it at block 248. At block 249, the procedure destroys the in-memory object. Note that the passivate.sub.-- object() method only destroys the in-memory object. The object can be reactivated in the future based on the metastate stored in the persistent storage. The passivate.sub.-- all.sub.-- objects() simply loops through all the objects registered with the server and passivates each one of them. Now, we turn to the obligations of persistent objects that make it possible for them to collaborate with the server object. This invention provides a new base class, referred to as ServiceBase, for all persistent objects. The ServiceBase class contains two categories of methods to facilitate exchange of metastate with a persistent object and to properly initialize and un-initialize it during the object lifecycles. The interface definition language (IDL) for the sample ServiceBase interface defined in the invention is shown in TABLE D.
TABLE D
______________________________________
interface ServiceBase : Object {
enum {
Naming;
Events;
LifeCycle;
Persistence;
Security;
ObjectIdentity;
Transactions;
Concurrency;
Externalization;
AttributePersistence;
} service.sub.-- id.sub.-- e;
strut metastate.sub.-- struct {
service.sub.-- id.sub.-- e service.sub.-- id;
unsigned short version.sub.-- major;
unsigned short version.sub.-- minor;
any service.sub.-- metastate;
} metastate.sub.-- struct.sub.-- t;
typedef sequence<metastate.sub.-- struct.sub.-- t> metastate.sub.-- t;
void reinit (in metastate.sub.-- t metastate);
void capture (inout metastate.sub.-- t metastate);
Object init.sub.-- for.sub.-- object.sub.-- creation ();
Object init.sub.-- for.sub.-- object.sub.-- reactivation ();
void uninit.sub.-- for.sub.-- object.sub.-- passivation ()
void uninit.sub.-- for.sub.-- object.sub.-- destruction ();
};
______________________________________
One form of collaboration required by a persistent object is that the server must be able to obtain metastate from it and supply metastate to it for reinitialization. One way to achieve this is to define a base class which has reinit() and capture() methods. The parent classes of a persistent object must inherit from the base class and override reinit() to perform its special re-initialization using the metastate passed in as an argument. Similarly, it must supply metastate in the INOUT argument of the capture() method. Recall that the server may invoke reinit() when an object reference is imported and invoke capture() when an object is passivated. In both of these methods, it is preferred that the server pass the entire metastate as an argument. However, other schemes are conceivable. For example, the server object can make use of proprietary methods for each individual class to obtain/supply the metastate. This requires the server object to examine the type of the object, and from that, deduce which method to use. Another form of collaboration required by persistent objects is that it must be possible to initialize and un-initialize an object in different ways during its lifecycle. Objects that have persistent states, at least on two-level store systems where the first level is RAM (14 in FIG. 2) and second level is persistent store (20 in FIG. 2), have two distinct lifecycles: the lifecycle of the object's persistent state, and the lifecycle of the in-memory object through which its state can be manipulated. Objects will undergo different types of initialization and un-initialization depending on the condition under which the object is being created or destroyed. One skilled in the art will appreciate that when the object is created for the first time, the object may have to allocate memory for itself to contain instance variables that are too complicated to declare statically. It may be necessary to register the object with various frameworks to which it is subject, including registering it with the server process involved. Integration with the frameworks or applications are referred to as "functional initialization". This kind of initialization is done in init.sub.-- for.sub.-- object.sub.-- creation() method of the ServiceBase class. On the other hand an object that has already been registered in the appropriate frameworks only needs to perform operational initialization (e.g., allocating any memory that it requires to contain its state and the like). This kind of initialization is done in init.sub.-- for.sub.-- object.sub.-- reactivation() method of the ServiceBase class. Whenever it reactivates an object, the server invokes a generic class initialization without any object state initialization. The server process then invokes "init.sub.-- for.sub.-- object.sub.-- reactivation" to give the object a chance to allocate the memory it needs. The server process invokes "reinit" on the object to restore the object's metastate. Likewise, a similar conclusion can be made about un-initialization during object destruction. In some cases, just the in-memory object is being destroyed, so that the object can be moved to a new location. In other cases, the in-memory object is being destroyed simply to passivate it (e.g., its persistent state remains valid). Finally, the object may be destroyed entirely because it no longer belongs in the system. Whenever the server passivates an object, the server first invokes "capture" to obtain the object's metastate. It then invokes "uninit.sub.-- for.sub.-- object.sub.-- passivation" followed by the generic object destruction method. When a client wants to entirely destroy the object, it first invokes "uninit.sub.-- for.sub.-- object.sub.-- destruction" followed by the generic object destruction method. Each of the class whose instance are persistent objects provide their own specialization(s) of ServiceBase for any classes they offer as mix-ins that introduce metastate, or any framework that introduces metastates. Such a specialization must override "reinit" and "capture" to exchange their own purposes. It must be noted that each method of the ServiceBase class is what is commonly known as an "initializer" or "destructor" method. This means that when an object multiply inherits from two or more classes, the implementation of these methods in a class performs housekeeping that is necessary at its own level and also makes calls to corresponding methods in each of its parent class. It is understood by those skilled in the art that a class must call its parents initializers before doing its own initialization and must call its parents un-initializers (destructors) after doing its own un-initialization. Next we explain two scenarios which show how various initialization and un-initialization methods can be used. Referring now to FIG. 19 for the first scenario, a flow diagram is shown for creating an object. One skilled in the art will appreciate that object creation is normally performed by a local or remote object factory. Local object factories create new objects using the procedure in FIG. 19. The procedure starts at block 250, and move immediately to block 252, where it is determined that a new object is to be created. If YES, at block 254, the local object factory locates the object's class and invokes the generic initializer on the class as shown in block 256. If NO, at block 254, the procedure ends at block 259. At block 258, the procedure invokes a specific initializer, namely "init.sub.-- for.sub.-- object.sub.-- creation". In general, any initializer involved in initial object creation that introduce persistent objects should invoke "make.sub.-- persistent.sub.-- ref" on the server to register the object with a persistent reference. Turning now to FIG. 20 for a second scenario, a flow diagram is shown for activating an object. The server automatically reactivates any objects with persistent references. If a method is invoked from another process, the object adapter will request the server to resolve the object reference to a local object pointer. With particular reference to FIG. 20, the procedure starts at block 260, and proceeds immediately to block 262, where the server identifies the object reference and determines if the object is present in memory as shown in block 264. If the object is not in memory, the server locates the class-object for the referenced object as shown in block 266. If the object is in memory, the procedure stops at block 261. At block 268, the server invokes the generic class initializer on the class to create a new in-memory instance. The server then invokes "init.sub.-- for.sub.-- object.sub.-- reactivation" on the object to permit object initialization as shown in block 270. At block 272, the server invokes "reinit" on the newly created object to load the metastate for the object. It will be appreciated by those skilled in the art that any subclass of ServiceBase that manages object persistence may override "reinit" to also restore the object's persistent state at this time. While the invention has been described with respect to a preferred embodiment thereof, it will be understood by those skilled in the art that various changes in detail may be made therein without departing form the spirit, scope, and teaching of the invention. Accordingly, the herein disclosed invention is to be limited only as specified in the following claims.
|
Same subclass Same class Consider this |
||||||||||
