Integrated three-tier application framework with automated class and table generation6085198Abstract An integrated three-tier application framework with automated class and database table generation. Schema information in the form of metadata structures is used to generate data classes for the client tier and the application tier. Corresponding client tier and application tier data classes implement a common interface that supports generalized access by other system components. Based on the schema information, factory classes are automatically generated for the client tier and application tier which permit instantiation of the generated data classes. Also, database configuration is automated by the generation of database table creation commands from the schema information. In one embodiment, a framework of management components are provided for both the client and application tiers to handle inter-tier communication, transparent caching of data objects in a public store, handling of changes to data via change objects, handling of updates in response to data changes, and resolution of query objects into database queries. Common methods are generated within each data class which recognize the use of a public store and the application of a change object scheme. Further, methods and attributes are inherited from framework superclasses that identify and interface with a public store, and that confer the concept of identity on a data class, as well as the ability to discover the attributes of the data class. Claims We claim: Description BACKGROUND OF THE INVENTION
______________________________________
CLASS <classname> (ABSTRACT) (INTERFACE) (PERSISTENT)
(VERSION/"<versioninfo>") (SUPER/<superclassname>)
(INTER/<intefacename>) {
IMPORTS {
<importstring>,
<importstring>,
ATTRIBUTES {
<attributename> <datatype> (DEFAULT/<defaultvalue>),
<attributename> <datatype> (DEFAULT/<defaultvalue>)
}
METHODS {
<methodname> <datatype> (COMMENT/"<commentstring>")
(THROW/<exceptionname>) (PRIVATE/<privateflagvalue>) {
(PARAMETERS {
<parametername> <datatype>,
<parametername> <datatype>
})
(BODY {
"<methodstring>",
"<methodstring>"
})
}
<methodname> <datatype> (COMMENT/"commentstring")
(THROW/<exceptionname>) (PRIVATE/<privateflagvalue>) {
(PARAMETERS {
. . .
})
(BODY {
. . .
})
}
}
}
______________________________________
A parsing program may be used to extract the schema metadata from the above text file. The metadata thus extracted may, for example, be stored in metadata structures and applied to class and table generation processes within a generator mechanism. The above text schema format may be expanded to include other metadata entries not illustrated in the example, such as constants or relationships with other classes. The schema may also be provided in a coded format where the schema file is implemented as a series of commands or method calls with the schema data provided as command or method parameters. For example, methods such as newClass(), newAttribute(), newMethod() and newParameter() could be used to specify the entry of the corresponding element. Those methods may then be defined to facilitate the setting of metadata attribute values and flags in metadata structures. By compiling the schema file and executing the method calls, the schema metadata may be extracted and loaded directly into the desired data structures, with the desired flags automatically set in accordance with the specified methods. An example of a coded schema file for a sample class is provided below. The individualized schema entries are shown within "< >" symbols.
__________________________________________________________________________
newClass = newCoreClass("<classname>", <superclass>);
(1)
newClass.setClassVersion("<versioninfo>");
(2)
addImport("<importstring>"); (3)
newAttribute( ).private( ).persistent( ).nonNull( ).string.sub.-- (
).name("<attribut (4)
ename>");
newAttribute( ).private( ).persistent( ).nonNull( ).object.sub.--
("<classname>").n (5)
ame("attributename>");
newMethod( ).public.sub.-- ( ).object.sub.-- ("<classname>").name("<method
name>"); (6)
newParameter( ).object.sub.-- ("<classname>").name("<parametername>");
(7)
newParameter( ).int.sub.-- ( ).name("<parametername>");
(8)
addBody("<methodstring>"); (9)
addBody("<methodstring>"); (10)
__________________________________________________________________________
Line (1) obtains a new metaclass instance and assigns class name and superclass values to metaattributes within a new metaclass. Line (2) sets the value of a class version metaattribute in the new metaclass to <versioninfo>. Line (3) writes a string containing import information into an import data structure such as a Vector. A "Vector" is a data type of the Java programming language that comprises an array of elements that may be of any data type and of any length. Data types other than the Vector data type may also be used to store metadata. Line (4) establishes a new metaattribute within the metaclass to describe an attribute within the current class. Boolean flags within the metaattribute are set to specify that the attribute is private, persistent and non-null. The attribute is a string data type with the name <attributename>. The sequence of method calls coupled by "." is typically evaluated from left to right. Each method call returns an object that acts as the implementor of the next method call in the sequence. For example, in line (4), the method call newAttribute() returns a metaattribute instance. The private() method of the metaattribute instance is then called which sets a private flag of the metaattribute and returns the metaattribute for the next method call. The persistent() method similarly sets a persistent flag of the metaattribute and returns the metaattribute, and likewise with the nonNull() method. The string.sub.-- () method associates a metadatatype of type string with the current metaattribute and returns the metaattribute. The name() method sets a name string in the metaattribute to the value of "attributename". In line (5), a second metaattribute is established to describe a second attribute. Again, flags are set to specify that the attribute is private, persistent and non-null. The data type specified within the metaattribute is an object data type of the class <classname>. The name of the attribute is <attributename>. Line (6) establishes a metamethod to describe a method of the current class. A flag is set to specify that the class is public. The method returns an object data type of class <classname>, and the methods name is <methodname>. In lines (7) and (8), metaparameters are associated with the metamethod of line (6). Line (7) specifies an object data type parameter of class <classname>, and having a parameter name of <parametername>. Line (8) specifies an integer data type parameter having a parameter name of <parametername>. Lines (9) and (10) write individual lines of the method to a method body data structure (such as a Vector) as data strings. In the schema examples above, standard methods such as constructors and accessors are not required to be specified. Those standard methods are automatically generated in a standardized manner when class definitions are created from the metadata. Further, reflection methods, whereby other objects are able to access the attributes of an object by specifying an attribute ID to a general method, may also be automatically generated for each class. Simple method calls or flag indicators similar to those shown above may be added to the schema description to trigger the automatic generation of other standardized methods. For example, certain classes may wish to instantiate other classes to fill an object data type attribute. In this instance, for example, an offerNewMethod () call may be added to the attribute command sequence to specify the automatic creation of "New" methods during class generation. These "New" methods may use a standard "try-catch" method format to "try" to obtain a new instance of a data class from a class factory, and to "catch" any exception generated by a failure to obtain the desired class instance. FIGS. 5A and 5B illustrate embodiments of metadata structures for storing metadata in accordance with an embodiment of the invention. FIG. 5A comprises MetaSchema 500, MetaClass 501, MetaMember 502, MetaAttribute 503 and MetaMethod 504. FIG. 5B comprises MetaParameter 505 and MetaDataType 506. One or more values within the metadata structures may be null, depending upon the class or class element being described by the metadata structures. MetaSchema 500 comprises a string data structure, "mySchemaVersion," that stores schema version information such as a version date. Also, MetaSchema 500 comprises a Vector data structure, "myCoreClasses," containing the set of MetaClasses describing the data classes of the system. Further Vector structures may be defined for metaclasses associated with other system classes aside from data classes, and for the entire set of system classes including data classes and non-data classes (i.e., those classes associated with system components 302A-305A, 302B-305B and 306-308). A reference ("myFactoryClass") to the generated factory class is also contained within MetaSchema 500. MetaClass 501 comprises a reference, "mySchema," to its associated MetaSchema, and a reference, "mySuperClass," to the MetaClass instance describing the superclass of the current class. String data structures, "myClassName," "myClassVersion" and "myChangeObject," store the name of the class, the version of the class and the name of the class's change object, respectively. Boolean flags, "myAbstractFlag," "myPersistentFlag" and "myInterfaceFlag," indicate whether the described class is abstract, persistent and/or an interface, respectively. The embodiment of MetaClass 501 shown comprises six Vector data structures: "myAttributes," "myMethods," "myImports," "myCode," "myInterfaceCode" and "myPassedMethods," "myAttributes" is a Vector of MetaAttribute instances describing the attributes of the current class. Similarly, "myMethods" is a Vector of MetaMethod instances describing the methods of the current class. "myImports" is a Vector of string instances containing class filenames for import into the current class. "myCode" is a Vector of string instances describing the lines of programming code for inclusion in the current class. "myInterfaceCode" is a Vector of string instances describing lines of programming code for inclusion in an interface for the current class. "myPassedMethods" is a Vector of MetaMethod instances passed by other MetaClasses to the current MetaClass instance. A "passed method" refers to the case where an originating class contains a method that invokes a similarly named method of a subsequent class in which the desired method steps are implemented. The originating class is said to "pass" the method to the subsequent class. MetaMember 502 is, in this embodiment, a superclass of MetaAttribute 503 and MetaMethod 504. Thus, the attributes of MetaMember 502 are included in MetaAttribute 503 and MetaMethod 504 by extension, as are those methods of the superclass not overridden by the subclasses. In other embodiments, the elements of the MetaMember class may be explicitly defined for MetaAttribute 503 and MetaMethod 504 without the use of a MetaMember superclass. MetaMember 502 comprises an integer data structure containing "myPrivateFlag." "myPrivateFlag" describes the private and protected state of the class element described by MetaMember 502. A reference, "myDataType," is stored to a MetaDataType instance describing characteristics of the data type associated with the current class element. A string structure, "myName," contains the name of the element as a character string. A reference, "myOwningClass," refers to the MetaClass instance describing the class of which the current element is a part. Also, a Vector of string data structures, "myComment," is maintained which stores, as character strings, lines of comments to be associated with the current element. MetaAttribute 503 comprises boolean structures "myPersistentFlag," "myNullFlag," "myOfferNewMethodFlag," "myPrimaryKeyFlag" and "myIndexFlag." "myPersistentFlag" indicates whether the current attribute is persistent, requiring storage in the database. "myNullFlag" specifies whether the current attribute is allowed to have a null value. "myOfferNew-MethodFlag" specifies whether a "New" method is to be generated for obtaining a new instance of the current attribute's data type from, for example, a class factory. "myPrimaryKeyFlag" specifies during database table generation that the current attribute is a primary key for the table. "myIndexFlag" may also be used during database table generation. MetaAttribute 503 further comprises the following string data structures: "myDefaultValue" specifying a default attribute value as a character string, "myMirrorCollectionClassName" specifying the class name of the elements contained in the Vector represented by the current attribute (if applicable), "myMirrorAttributeName" specifying the name of a mirror attribute in each element of the Vector represented by the current attribute (if applicable), and "myChangeObject" specifying the name of the change object class associated with the attribute. A reference, "myMirrorCollectionClass," refers to the MetaClass instance describing the associated mirror collection element class. Also, a reference, "myMirrorAttribute," refers to the MetaAttribute instance describing the associated mirror attribute. A mirror collection refers to a collection or set of elements where those elements are instances of a class (i.e., the "mirror collection class"). Each class instance within the mirror collection may contain an attribute (i.e., the "mirror attribute") that is a reference to an owning class of the mirror collection. For example, a scheduling application may include an appointment class to represent meetings and an invitation class to represent an individual invited to attend a given appointment. Each appointment instance may contain a collection (e.g., an "invitations" Vector) of invitation instances representing the individuals invited to the given appointment. The invitation instances, in turn, contain a reference (i.e, a "mirror attribute") to the appointment instance containing the "invitations" Vector of which they are a part. In accordance with an embodiment of the invention, respective MetaClass instances are used to describe the appointment class and the invitation class. Further, a "mirror collection" MetaAttribute instance within the appointment MetaClass instance is used to describe the invitations Vector. A "mirror attribute" MetaAttribute instance within the invitation MetaClass instance refers to the appointment MetaClass instance. MetaMethod 504 comprises a string structure, "myThrowsType," that specifies the exception type, if any, thrown by the current method being described. MetaMethod 504 comprises two Vector data structures, "myParameters" and "myBodyParts." "myParameters" is a Vector of MetaParameter instances describing the individual parameters of the current method. "myBodyParts" is a Vector of string structures for storing lines of method code as individual character strings. A reference, "myPassToAttribute," refers to the MetaAttribute instance describing an attribute to forward the method call to, if applicable. A boolean structure, "myPassThisFlag," specifies whether to include "this" as a parameter in a forwarded method. MetaParameter 505 comprises a string structure, "myName," specifying the parameter name as a character string. MetaParameter 505 further comprises a reference, "myDataType," to a MetaDataType instance describing the data type of the parameter. MetaDataType 506 comprises a Vector of MetaDataType instances labeled "ourTypes." The MetaDataType class has a method for populating "ourTypes" with a number of MetaDataType instances with stored metadata corresponding to different data types. "ourTypes" may be used as a library of information on data types, with methods for accessing a specific MetaDataType instance by token value. The integer data structure, "myToken," stores the token value assigned to a given MetaDataType instance. A string data structure, "myTokenName," stores the character string representing the token name assigned to the given MetaDataType instance. String structure "myJavaDeclaration" specifies a character string for declaring, in the Java programming language, the data type described by the MetaDataType instance. The string structure, "myJavaInterfaceDeclaration," specifies a similar character string for declaring the current data type if the data type is an interface type. Other programming language declarations could be similarly represented, either alternatively or additionally. String structures "myDatabaseDeclaration" and "myDatabaseLength" are used in the construction of database tables. "myDatabaseDeclaration" specifies the character string for the database data type, and "myDatabaseLength" specifies the character string for the length of the data type in the database. MetaDataType 506 further comprises a reference, "myReferenceType," to a MetaClass Instance, if the MetaDataType instance corresponds to an object class described by a MetaClass. String structure "myObjectifier" specifies an objectifier for the MetaDataType instance, whereas string structure "myDeobjectifier" specifies a deobjectifier. Objectifiers and deobjectifiers are used respectively to convert certain non-class data types into corresponding class data types, and to convert class data types into non-class data types. For example, an objectifier may be used to convert an integer variable into an Integer class instance containing the integer variable value, as well as certain methods for operating on the integer variable value. Boolean structure "myPassByCopyFlag" whether the represented data type is passed directly, or whether the data type is first duplicated with the copy being passed. String structure "myCollectionContentName" specifies, for a data type containing a collection of objects, what the class name is for the elements in the collection. String structure "myNullValueString" stores the null value for the current data type. Examples of null values include "null", "-1", etc. Generation of Classes and Common Interface FIG. 6A is a flow diagram of a process, in accordance with an embodiment of the invention, that generates the class and interface definitions referred to previously with reference to step 402 of FIG. 4. In step 600, the schema metadata is obtained to facilitate the automated class generation. The metadata may be, for example, provided in form of the metadata structures described with reference to FIGS. 5A and 5B. In step 601, the first metaclass is obtained from the schema metadata. The metaclass comprises metadata descriptions of the properties of the class, its attributes, and its methods. Steps 602-604 involve building the class definition files for the application and client tiers, as well as building the shared interface file, and may be performed in any order. In step 602, a class building operation is performed to write the class definition for the application tier. The class building operation uses the metadata to provide standard declarations, and to generate standard methods, such as constructors, accessors, "new" methods and reflection methods. The class definition is typically written to a new file in a location within the file system used for storing code associated with the application server. In one embodiment, an additional template class definition is generated that is a substantially empty subclass of the first class (or "base class") definition (i.e., the template contains little or no code in the form of additional attributes or methods). In this case, the base class may be declared as an abstract class. A developer can easily expand the capabilities of the respective base class by, for example, filling in the template with further attributes and methods as desired. In step 603, a class building operation is performed to write the class definition for the client tier. As with step 602, the class building operation uses the metadata to provide standard declarations, and to generate standard methods, such as constructors, accessors, "new" methods and reflection methods. The class definition is typically written to a new file in a location within the file system used for storing code associated with a client application. Similar to the application tier, an additional template class definition may also be generated for the addition of further client specific code. The class definitions for the application and client tiers share the same common interface, though the implementation of the interface may differ between the tiers. The interface is generated in step 604, and comprises, for example, a list of import classes, a series of constant declarations (e.g., for token values assigned to the individual class attributes), and a series of public method declarations. The interface is written to a location in the file system containing code files common to the application tier and the client tier. The location of the interface file is included as an import in the class definitions generated in steps 602 and 603. In step 605, if there are no further metaclasses, the process ends (step 607). However, if further metaclasses remain in the schema metadata, the next metaclass is obtained in step 606, and the process returns to step 602 to generate class definition code from the next metaclass. FIG. 6B illustrates, in accordance with an embodiment of the invention, the process of the class building operation carried out in each of steps 602-604 of FIG. 6A. For the following description, character strings for building the class definitions are depicted within quotes. Each string may be written to the respective class file using known print commands. Elements inside of "< >" are evaluated to fill the character string. As an example, an interface declaration and one embodiment for writing the interface declaration to a file are provided below. The print command uses a tabprintln() method of a print stream instance (ps). MetaClass access methods, getName() and getSuperClass(), are used to obtain the interface and superclass names to provide evaluation of elements within "< >".
______________________________________
"public interface <classname>Def extends <superclass>Def"
ps.tabprintln("public interface" + getName( ) + "Def extends" +
getSuperClass( ).getName( ) + "Def");
______________________________________
In one embodiment of the invention, generated methods for each class implement a "try-catch" format or construct. The try-catch format appears as:
__________________________________________________________________________
try {
<"try" sequence of operations>
} catch (Exception e) {
<"catch" sequence of operations to be performed if an exception is
caught>
__________________________________________________________________________
In the above construct, the "try" sequence of operations (or a single operation) is executed. If one of the "try" sequence operations throws an exception, the "catch" sequence is executed. The try-catch format is advantageous because it allows for problems to be identified at runtime through the issuance of exceptions, and it provides the opportunity for action to be taken to handle those exceptions thrown (e.g., display an appropriate error message to a user based on the type of exception thrown). In certain examples, case statements are described for directing executional flow based on the result of a test, such as the matching of a token value. Other forms of conditional statements, such as "If" statements, may be similarly applied to control executional flow. In the embodiment described, a class file can include some or all of the following: a class header, declarations (e.g., constant and variable declarations), program code (e.g., general code, methods, and constructor methods) and/or a footer. In step 608 of FIG. 6B, the class header is written. The class header may comprise elements such as a header comment, a package declaration, a list of import declarations, class comments and a class declaration. In step 609 of FIG. 6B, any constant declarations are written to the class definition file. To begin any of steps 609-613, a comment preface to the section (e.g., "//constants", etc.) may be written to the file to indicate what follows for easier reading of the class definition. To facilitate the use of reflection methods, integer attribute tokens may be assigned to each attribute. Access may then be made to each attribute using a general reflection method which includes an attribute token value as a parameter. In one embodiment, the integer attribute tokens are declared as constants in the common interface during step 609. To account for all attributes inherited from superclasses, as well as those explicitly declared in the current class, a chain of classes is formed from the highest superclass in the hierarchy (i.e., the superclass that does not itself have a superclass specified, or the highest superclass that is not a general framework class) to the current class. Each attribute in the chain of classes is enumerated and assigned an attribute token that is declared as a constant. To assign attribute tokens, each attribute is given a token name, for example, by changing the attribute name to all capital letters (for constant naming conventions), and appending "ATTRIBUTE.sub.-- TOKEN.sub.-- " as a prefix. Each attribute token is then assigned a different token value by incrementing the token value after each attribute token assignment. The list of constants may be written to the common interface in the following form:
__________________________________________________________________________
"public final static int ATTRIBUTE.sub.-- TOKEN.sub.-- <ATTRIBUTENAME1> =
0;"
"public final static int ATTRIBUTE.sub.-- TOKEN.sub.-- <ATTRIBUTENAME2> =
1;"
"public final static int ATTRIBUTE.sub.-- TOKEN.sub.-- <ATTRIBUTENAME3> =
2;"
etc.
__________________________________________________________________________
In step 610 of FIG. 6B, the variable declarations are written based on the schema attribute metadata for the current class (e.g., the Vector of MetaAttribute structures). Each MetaAttribute is enumerated and its MetaDataType queried to determine the corresponding declaration (e.g., "myJavaDeclaration") for the given programming language. The variable name may be generated by, for example, adding the prefix "my" to the attribute name. An example of a variable declaration string for a single attribute is: "private<myJavaDeclaration>my<attributename>;" The variable declarations are generated in the same manner for the client and server base classes. In accordance with an embodiment of the invention, variable declarations are not automatically generated for the common interface and the template classes. In step 611, general code is written to the class file in an automated fashion. For the client and server base classes, the general code comprises the character string elements of the Vector "myCode" for the current MetaClass. For the common interface, the general code comprises the character string elements of the Vector "myInterfaceCode" for the current MetaClass. Each string element is enumerated from the respective Vector, and written to the class file. In accordance with an embodiment of the invention, general code is not written to the template classes. In step 612, for the client and server base classes, a standard constructor is written to the class file. No constructor is necessary for the common interface or template classes. The automated constructor may include setting default values for the variables (attributes). The default values may be determined by enumerating the MetaAttributes of the MetaClass, and determining a default value from the MetaAttribute metadata and the corresponding MetaDataType. An example of a constructor is as follows:
______________________________________
"public <classname>( );"
"{"
" super( );"
" <attributename> = <defaultvalue>;"
" <attributename> = <defaultvalue>;"
etc.
"}"
______________________________________
In step 613, the methods of the class file are written. No methods are written to the template classes because the methods are inherited from the base classes. Only the method declarations are written to the common interface. "New" method code is generated for those MetaAttributes whose "myOfferNewMethodFlag" is true. Method code is generated for each of the methods specified in the Vector "myMethods" of MetaMethod elements. Accessor methods are generated for the class variables (i.e., for the attributes described by the MetaAttributes in the Vector "myAttributes"). Also, reflection methods are generated that allow for the attributes to be accessed by attribute token. In step 614, the footer is generated for the base classes, template classes and the common interface. In one embodiment, the footer comprises a close bracket symbol "}" that corresponds to the open bracket symbol generated in the class header. Further description is provided below with respect to the automated generation of code for the class header and method portions of the generated class files and common interface. Class Header As stated above with reference to FIG. 6B, the class header comprises a header comment, a package declaration, a series of import declarations, class comments, and a class declaration. The header comment may include, for example, a standard copyright notice and/or the date of class generation. The automated code generation writes the comment to the file (with appropriate comment line symbols, e.g., "/*" or "//"), for example, using known file print commands. The class comments may comprise more class-specific comments, and may be written to the class file in the same manner as the header comment. The package declaration typically corresponds to the target directory for the class file which depends on whether the class definition is being generated for the client tier ("client") or application tier ("server"), and whether the class definition is a base class, a template, or the shared interface. A case statement may be used to write the appropriate package declaration based on the target, where the target is one of the following: client base class, client template class, server base class, server template class, or common interface. An example set of packages for class targets is given below:
______________________________________
target package declaration
______________________________________
client base class
"package com.cmpny.group.app.client.core.base;"
client template class
"package com.cmpny.group.app.client.core;"
server base class
"package com.cmpny.group.app.server.core.base;"
server template class
"package com.cmpny.group.app.server.core;"
common interface
"package com.cmpny.group.app.common.core;"
______________________________________
The imports for the current class are determined by target. For example, for the client base class and the server base class, the path name for the package containing the interface is declared as an import. The path names for other elements of the three-tier framework that may be integrated with the generated class, such as change objects, may be declared as imports as well. For the client base class, the application server base class and the common interface, the string elements of the import Vector "myImports" are enumerated to form import declarations that are added to those already written to the class file. The template class files may forego the addition of imports. The class declaration is also target dependent. For the common interface, the interface name may be generated by, for example, appending a standard interface suffix, such as "Def," to the class name specified in the metadata. If there is a superclass specified in the metadata, an "extends" phrase may be added to the declaration which specifies a superclass interface name. The superclass interface name is generated by appending the interface suffix to the superclass name. If there is no superclass, then the extends phrase is omitted. The declaration for the interface is as follows:
__________________________________________________________________________
superclass:
"public interface <classname>Def extends <superclass>Def"
no superclass:
"public interface <classname>Def"
__________________________________________________________________________
For the client and server base classes, the superclass is assumed to be "Object" if no superclass is specified. The class declaration for the client and server base classes is, for example:
______________________________________
"public abstract class <classname> extends <superclass>
implements <classname>Def"
"{"
______________________________________
For the client and server template classes, the superclass is the respective client or server base class with the pathname (as given by the target packages) for the base class appended as a prefix to the class name. Thus, for the template classes, the respective declarations are:
__________________________________________________________________________
client:
"public class <classname> extends <clientBasePackage>.<classname>"
"{"
server:
"public class <classname> extends <serverBasePackage>.<classname>"
"{"
__________________________________________________________________________
"New" Methods "New" methods ("new<attributeName>," not "new <constructor>") are provided for those attribute types, such as Vectors, that contain objects as elements. The "new" method provides a mechanism to obtain new instances of the object class forming each element of the Vector, and, in one embodiment, to initialize the mirror attribute of the new instance to refer to the class instance containing the Vector. The new class instances are obtained, for example, from the public store by invoking a method of the object representing the public store which returns a new instance for a specified class ID. The public store object calls a factory method of a class factory to construct the new class instance. To generate a "new" method, the element "myCollectionContent-Name" of a MetaAttribute's associated MetaDataType is queried to determine the class name of the desired class instance to be created by the "new" method. The method declaration can then be generated as illustrated below, in terms of the desired class's corresponding interface (generally "<classname>Def"). In one embodiment, the method name is generated by appending "new" as a prefix to the desired class name. "public<desiredClassName>Def new<desiredClassName>()" (e.g., "public SampleObjectDef newSampleObject()") For the common interface, a semicolon ";" is appended to the above method declaration. For the client and server base classes, a method body is generated in a try-catch format, for example, to obtain a new instance or to catch an exception in the event a new instance is not obtained. Typically, the new instance is obtained by calling a method from a system component that handles the instantiation of the desired class within the context of the database. The following example of a "new" method body is provided in accordance with an embodiment of the invention. The method body incorporates the use of the factory class (e.g., from "myFactoryClass") for lookup of class token values, and assumes the base class is a subclass of a "StoredObject" class that supports the use of a public store (e.g., in the form of an object cache component) that performs instantiation. The example form of the method body is:
__________________________________________________________________________
"{"
" try {"
" SchemaInfoDef schema = getIdentity( ).getClassIdentity( ).getSchema(
);"
" ClassIdentityDef classID = schema.getClassForToken(<factory ClassNa
me>.CLASS.sub.-- TOKEN.sub.-- <desiredClassName>);"
" <desiredClassName>Def newInstance = (<desiredClassName>Def) (ge
tPublicStore( ).newInstanceForClassIdentity(classID));"
" newInstance.set<mirrorAttributeName>(this);"
" return newInstance;"
" } catch (exception e)
{"
" getPublicStore( ).handleException(e);"
" return null;"
" }"
"{"
__________________________________________________________________________
Specified Methods For the specified methods, i.e., those methods described by the MetaMethod elements of the Vectors "myMethods" and "myPassedMethods," method declarations are added to the common interface. Those method declarations may include the comment strings specified in the respective string Vector "myComments" of the respective MetaMethod structure. The Java (or other programming language) declaration for the method's return data type is obtained from the data type metadata (e.g., from the MetaDataType structure "myDataType" of the MetaMethod), and the name of the method is obtained directly from the method metadata (e.g., "myName"). The parameter data types and names are obtained by enumerating the MetaParameters of the MetaMethod and querying the associated MetaDataType of each MetaParameter. If the string "myThrowsType" is not null, a "throws" phrase is added to the declaration, including the specified data type. Example declarations in accordance with an embodiment of the invention are provided below:
__________________________________________________________________________
(myThrowsType==null): "public <returnDataType> <methodName>(<parameter
DataType> <parameterName>, <parameterDataType> <parameterName> <, ...>)"
(myThrowsType!=null): "public <returnDataType> <methodName>(<parameter
DataType> <parameterName>, <parameterDataType> <parameterName> <, ...>)
throws <myThrowType>"
__________________________________________________________________________
The client and server base classes receive the same general method declarations as shown above. Also, if the "myPassToAttribute" structure of the MetaMethod is null, the string elements of the Vector "myBodyParts" are enumerated and printed to the class file to complete the body of the respective method. If the "myPassToAttribute" is not null, meaning that the processing of the method is to be carried out by an identically named "passed" method of another class designated by "myPassToAttribute," the method body may be constructed, for example, as shown below, to make a further call to the other class and to return the result. The parameters of the new call include the same parameters as the current method with the addition of "this" to specify the current class as the caller, if "myPassThisFlag" is true. For example:
______________________________________
(myPassThisFlag):
"{"
" return get<attributeName>( ).<methodName>(this, <parameter>,
<parameter>, <etc.>);"
"}"
(!myPassThisFlag):
"{"
" return get<attributeName>( ).<methodName>(<parameter>,
<parameter>, <etc.>);"
"}"
______________________________________
Passed methods are generated in similar fashion to the specified methods described above. However, if "myPassThisFlag" is true, the parameter list in the method declaration includes an entry for the calling class, such as by adding the corresponding class type or interface type of the calling class (e.g., "<className>Def") followed by "caller" as the standard parameter name for the calling class. Accessor Methods Accessor methods are automatically generated for each attribute described in a MetaClass instance. As with the other described methods, the common interface receives the method declarations for the "Get" and "Set" accessor methods, whereas the client and server base classes receive the complete methods, including declaration and body. A further protected accessor method, "doSet," is generated in one embodiment for use by certain instances of the "Set" method, as well as by the reflection methods. The protected "doSet" method is not included in the public interface of the class. The "Get" declaration is determined from the data type of the attribute and its corresponding Java (or other programming language) interface declaration. An example "Get" declaration is as follows: "public<dataType.JavaInterfaceDeclaration>get<attributeName>()" The "Get" accessor method is designed to return the value of the variable targeted by the method, or its corresponding null value if the variable value cannot be obtained. For nonpersistent attributes, i.e., those attributes not saved in the database, the "Get" accessor may simply return the variable value using the phrase "return <variable name>" within the try-catch format. For persistent attributes, method calls may be embedded in the generated code to access the "public store" for the variable value. In one embodiment, the following two "Get" method forms are used, the first being used if "myMirrorCollectionClass" is null (i.e., the attribute is not a collection or set, such as a Vector of classes), and the second being used if "myMirrorCollectionClass" is not null.
__________________________________________________________________________
(myMirrorCollectionClass = null):
"{"
" try {"
" if(getNeedsToBeReadFromStore( )) readFromStore( );"
" return <variableName>;"
" } catch (Exception e) }"
" getPublicStore( ).handleException(e);"
" return <nullValueString>;"
" }"
"}"
(myMirrorCollectionClass != null):
"{"
" try {"
" if (<variableName> == null) {"
" <variableName> = new RelationshipAwareLiveSet(getPublicStore( ),
getClassInfo( ).getAttributeForName("<attributeName>"), this);"
" }"
" return <variableName>"
" } catch (Exception e) {"
" getPublicStore( ).handleException(e);"
" return <nullValueString>;"
" }"
"}"
__________________________________________________________________________
In the above examples, for the datatype specified for the attribute, if "myPassByCopyFlag" is true, the return phrase "return <variableName>" is replaced with the return phrase "return new <dataTypeJavaDeclaration> (<variableName>);" to return a copy of the variable of the data type specified for the attribute. The "Set" accessor method is designed to write a value to the given variable (attribute), with the new value specified as a method parameter. In one embodiment, for collection attributes, such as Vectors of classes (i.e., those attributes whose "myMirrorCollectionClass" is not null), no "Set" method is generated. The declaration of the "Set" method may be generated as follows:
______________________________________
"public void set<attributeName>(<dataTypeJavaInterfaceDeclaration>
<attributeName>)"
______________________________________
The "Set" declaration alone is written to the common interface. The declaration and method body are written to the client and server base classes. In one embodiment, if the attribute is persistent and has a designated change object, the "Set" method initiates a transaction with the public store, obtains a change object with the appropriate attribute changes, performs the change using the change object, and ends the transaction with the public store. If the attribute is not persistent or does not have an assigned change object, the "Set" method calls the "doSet" method. "Set" method body examples are provided below, with the first corresponding to a persistent attribute with a change object and the second corresponding to a nonpersistent attribute or one without an assigned change object. (Note: the attribute's "owning" class is the class containing the attribute.)
__________________________________________________________________________
"{"
" try {"
" getPublicStore( ).beginTransaction( );"
" <changeObjectName> changeObject = new <changeObjectName>(this,
<attribute'sOwningClass'sInterfaceName>.ATTRIBUTE.sub.-- TOKEN.sub.--
<attributeName(all
caps)>, <attributeName>);"
" getPublicStore( ).performChange(changeObject);"
" getPublicStore( ).endTransaction( );"
" } catch (Exception e) {"
" getPublicStore( ).handleException(e);"
" }"
"}"
"{"
" doSet<attributeName>(<attributeName>);"
"}"
__________________________________________________________________________
Similar to the "Set" methods, "doSet" methods are not generated for collection attributes, such as Vectors of classes (i.e., those attributes whose "myMirrorCollectionClass" is not null). The "doSet" accessor methods are written to the client and server base classes, but, as stated previously, no declaration is written to the common interface for these protected methods. The "doSet" method is an internal method. Any transaction-based activities are handled by the caller. An example of a "doSet" method is provided below in accordance with an embodiment of the invention.
__________________________________________________________________________
"protected void doSet<attributeName>(<dataTypeJavaInterfaceDeclaration>
<attributeName>)"
"{"
" try {"
" if (getNeedsToBeReadFromStore( )) readFromStore( );"
" <variableName> = <attributeName>;"
" } catch (Exception e) {"
" getPublicStore( ).handleException(e);"
" }"
"}"
__________________________________________________________________________
If "myMirrorAttribute" is not null and "myPassByCopyFlag" for the attribute's data type is true, the string "<variableName>=<attributeName>;" may be replaced by:
______________________________________
" if (<variableName> == null)"
" <variableName> = new <dataTypeJavaDeclaration>( );"
" <variableName>.copy(<attributeName>);"
______________________________________
Reflection Methods With respect to reflection methods, the common interface receives the declarations, and the client and server base classes receive the full methods. The reflection methods provide access to the attributes of a given class, including those inherited from superclasses, through the specification of an AttributeInfo structure that contains the attribute token of the desired attribute. A case statement is used, for example, to call either the "Get" or "doSet" accessor method for the desired attribute, as appropriate, based on which reflection method is invoked and the resolution of the attribute token. Examples of "Get" related reflection methods in accordance with an embodiment of the invention are provided below.
__________________________________________________________________________
"public Object getValueOrReferenceForAttribute(AttributeInfoDef
attribute)"
"{"
" return getValueForAttribute(attribute, true);"
"}"
"public Object getValueForAttribute(AttributeInfoDef attribute)"
"{"
" return getValueForAttribute(attribute, false);"
"}"
"public Object getValueForAttribute(AttributeInfoDef attribute, boolean
idOnly)"
"{"
" try {"
" Object returnValue = null;"
" switch(attribute.getAttributeToken( )) {"
" <case 1>"
" <case 2>"
" <etc.>"
" }"
" return returnValue;"
" } catch (exception e) {"
" getPublicStore( ).handleException(e);"
" return null;"
" }"
"}"
__________________________________________________________________________
For attributes that reference user classes, in one embodiment, the case code "<case #>" is:
__________________________________________________________________________
" case <attribute'sOwningClass'sInterfaceName>.ATTRIBUTE.sub.-- TOKEN
.sub.-- <attributeName(all caps)>:"
" if (idOnly) {"
" if (get<attributeName>( ) == null)"
" returnValue = null;"
" else"
" returnValue = get<attributeName>( ).getIdentity( );"
" } else
{"
" returnValue = get<attributeName>( );"
" }"
" break;"
__________________________________________________________________________
For attributes that do not reference user classes, the case code is:
__________________________________________________________________________
" case <attribute'sOwningClass'sInterfaceName>.ATTRIBUTE.sub.-- TOKEN
.sub.-- <attributeName(all caps)>:"
" returnValue = get<attributeName>( ):"
" break;
__________________________________________________________________________
The "Set" related reflection method also uses a case statement to resolve attribute tokens for invocation of the appropriate accessor, in this case, the "doSet" method. Those attributes that represent a collection (e.g., those attributes for which "myMirrorClassCollection" is not null) are not included in the method. An example of a "Set" related reflection method is provided below.
__________________________________________________________________________
"public void setValueForAttribute(AttributeInforDef attribute, Object
value)"
"{"
" try {"
" switch(attribute.getAttributeToken( )) {"
" <case 1>"
" <case 2>"
" <etc.>"
" }"
" } catch (Exception e) {"
" getPublicStore( ).handleException(e);"
" }"
"}"
__________________________________________________________________________
The case code ("case #") takes the form of:
__________________________________________________________________________
" case <attribute'sOwningClass'sInterfaceName>.ATTRIBUTE.sub.-- TOKEN
.sub.-- <attributeName(all caps)>:"
" doSet<attributeName>((<dataTypeJavaDeclaration>)value);"
" break;"
__________________________________________________________________________
Thus, user class files and interfaces are automatically generated from the schema metadata to reduce redundant programming effort, and to provide automated integration of the user class files with other elements of the three-tier framework. Generation of Factory Classes As referred to previously with respect to step 403 of FIG. 4, a factory object class is generated from the schema metadata. Classes that are part of the framework (e.g., non-data objects) are imported into the factory class, and a standard constructor is generated. Class tokens are generated as constant declarations by enumerating the MetaClass Vector "myCoreClasses" and individually assigning token names and token values. The token value is incremented between each new MetaClass. The token names may be assigned by adding a token prefix to each class name. For example, each token assignment may appear as a constant declaration as follows: "public final static int CLASS.sub.-- TOKEN.sub.-- <className(all caps)>=<tokenNumber>;" A factory method is generated that will receive a token value (or some other reference that is transformed into a token value) and resolve a case statement based on the specified token value to instantiate the desired class. Abstract classes need not be included in the factory method as abstract methods are not instantiated. If the factory class is generated for the server, the server template package is appended to the class name as a prefix for the class constructor. For the factory class generated for the client, the client template package is similarly appended to the constructor. In one embodiment of the invention, the factory method is configured to assign an "identity" class instance to the newly instantiated data class (or "core" class). The identity instance is set with a class identity value and a serial number. Further, the public store is set for the new class instance before returning the class instance to the caller of the factory method. An example factory method is shown below:
__________________________________________________________________________
"public IdentifiableDef newInstanceForReference(ReferenceDef ref)"
"throws Factory Exception"
" StoredObject newObject = null;"
" try {"
" ClassIndentityDef classId = null;"
" Identity newIdentity = null;"
" int classToken = ref.getClassIdentity( ).getClassToken( );"
" switch (class Token) {"
" case CLASS.sub.-- TOKEN.sub.-- <className(all caps)>:"
" newObject = new <(server/client)templatePakcage>.<class
Name>( );"
" newIdentity = new Identity( );"
" classId = getSchema( ).getClassForToken(CLASS.sub.-- TOKEN.sub.-
-
<className(all caps)>);"
" newIdentity.setClassIdentity(classId);"
" newIdentity.setSerialNumber(ref.getSerialNumber( ));"
" newIdentity.setIdentitySpace(ref.getIdentitySpace( ));"
" newObject.setIdentity(newIdentity);"
" break;"
" <further case statements - one for each non-abstract class>"
" default:"
" break;"
" }"
" if(newObject == null) {"
" throw new FactoryException( );"
" } else
{"
" newObject.setNeedsToBeReadFromStore(false);"
" newObject.setPublicStore(getPublicStore( ));"
" AttributeInfoDef aid = newObject.getClassInfo( ).getAttributeFor
Token(CoreObjectDef.ATTRIBUTE.sub.-- TOKEN.sub.-- HASBEENDELETED);"
" newObject.setValueForAttribute(aid, new Boolean(false));"
" }"
" } catch (SchemaException e) { }"
" return newObject;"
"}"
__________________________________________________________________________
The factory method above takes advantage of inherited methods and attributes of the new object instance to set public store information (e.g., provide a reference to the respective object cache component), as well as to set identity information on the new object instance. In one embodiment, each data object is a subclass of the framework "StoredObject" class which is a subclass of a framework "AwareObject" class. The StoredObject class provides the inherited methods for communicating with the public store. The AwareObject class implements an IdentifiableDef interface that provides methods for accessing class information such as class ID and serial number (object ID). The AwareObject class also provides methods for obtaining a list of class attributes and token values, and for accessing attribute information via token value, for example. Generation of Database Tables FIGS. 7A and 7B are flow diagrams illustrating a process for generating database tables from schema metadata in accordance with an embodiment of the invention. The processes of FIGS. 7A and 7B may be implemented, for example, within schema class 907 of FIG. 9, and executed by an instance of the schema class at run time during initial start-up. The database tables are generated through a "create table" command that specifies the table name (or relationship) and the attributes that make up the table. The following description refers to how to generate the a "create table" command from schema metadata. The embodiment described generates a separate create table command, and therefore a separate database table, for each MetaClass instance. Other embodiments may, for example, generate one database table for multiple MetaClass instances, or multiple database tables for a single MetaClass instance. An example of a create table command, in accordance with an embodiment of the invention, is provided below.
______________________________________
CREATE TABLE <className> (
<attributeDatabaseDeclaration>,
<attributeDatabaseDeclaration>,
<etc.>
)
______________________________________
The create table commands may be included together in a script format and submitted in a "create database" command set as shown below, for example.
______________________________________
CREATE DATABASE <databaseName>
USE <databaseName>
CREATE TABLE <className1> (
<attributeDatabaseDeclaration>,
<attributeDatabaseDeclaration>,
<etc.>
)
CREATE TABLE <className2> (
<attributeDatabaseDeclaration>,
<attributeDatabaseDeclaration>,
<etc.>
)
<etc.>
______________________________________
In FIG. 7A, the process begins by obtaining the schema metadata in step 700. In step 701, the first MetaClass instance is obtained from the schema metadata, for example, using an enumeration process on the Vector of MetaClasses "myCoreClasses." In step 702, a determination is made whether the current MetaClass is persistent, not abstract, and not an interface. If the MetaClass is abstract, is an interface or is not persistent, the process continues at step 710, bypassing generation of a "create table" command for the current MetaClass. If, however, the MetaClass is persistent, not abstract, and not an interface, the process continues at step 703, where the "create table" command is initiated, specifying the MetaClass class name as the relation or table name. In step 704, a determination is made whether the current MetaClass has a superclass specified. If there is no superclass specified (mySuperClass==null), the process continues at step 707 in which the MetaAttributes ("myAttributes") are obtained for the current MetaClass instance. In step 708, the database declarations for each attribute are added to the "create table" command. The process of step 708 is further described in FIG. 7B. If, at step 704, there is a superclass specified (mySuperClass!=null), then the MetaClass instance for the specified superclass is obtained in step 705. In Step 706, the superclass MetaClass is processed as described in steps 704-708 in a nested process ("A"). Nested process "A" can be executed recursively to process each superclass level in the class hierarchy. Step 706 ensures that attributes inherited from the superclass (and its superclass, and so on) are added to the "create table" command. After step 706, the process continues at step 707. In step 709, the "create table" command is closed (e.g., by appending a close parenthesis ")"), and the command is submitted, via the data store management component, to the database management system to configure the database. If more MetaClasses remain, as determined in step 710, the next MetaClass is obtained in step 711, and the process returns to step 702. If there are no more MetaClasses in step 710, the process is completed. Submission of the finished "create table" commands may also be performed as a set, as described above for a "create database" command set. FIG. 7B further describes the process of adding attribute database declarations to the "create table" command. In accordance with an embodiment of the invention, the attribute database declaration has the following form:
______________________________________
<attributeName> <attributeDatabaseType> <(<databaseLength>)>
<"PRIMARY KEY"/"NULL"/"NOT NULL">
______________________________________
In step 712 of FIG. 7B, the first MetaAttribute instance is obtained from the MetaAttributes for the current MetaClass, for example, through an enumeration of Vector "myAttributes." In step 713, a determination is made whether the current attribute is persistent (myPersistentFlag==true). If the current attribute is not persistent, the process continues at step 724, bypassing the current MetaAttribute. If the current attribute is persistent in step 713, the attribute name is added to the "create table" command in step 714 to specify a column name in the table. In step 715, the MetaDataType instance of the current MetaAttribute instance is queried to determine the database type ("myDatabaseDeclaration") of the current attribute. In step 716, the database type is added to the "create table" command to specify the column type. In step 717, if the MetaDataType database length string "myDatabaseLength" is not "0," in step 718, the database length is added to the "create table" command in the form "(<myDatabaseLength>)". Step 718 proceeds to step 719. If "myDatabaseLength" is "0," the process proceeds from step 717 to step 719. Steps 719-723 handle the setting of the database null declaration. In step 719, if "myPrimaryKeyFlag" is true, "PRIMARY KEY" is added to the "create table" command in step 720, and processing continues at step 724. If, in step 719, "myPrimaryKeyFlag" is false, processing continues at step 721. In step 721, if "myNullFlag" is true, "NULL" is added to the "create table" command in step 722, and processing continues at step 724. If, in step 721, "myNullFlag" is false, "NOT NULL" is added to the "create table" command in step 723 before proceeding to step 724. In step 724, if any MetaAttribute instances remain for the current MetaClass instance, the next MetaAttribute instance is obtained in step 725, and the process returns to step 713. Otherwise, the process of entering attribute database declarations for the current "create table" command is complete. Thus, an integrated three-tier application framework with automated class and table generation has been described in conjunction with one or more specific embodiments. The invention is defined by the claims and their full scope of equivalents.
| ||||||||||
