Index: openacs-4/packages/acs-core-docs/www/object-system-design.adp =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/object-system-design.adp,v diff -u -r1.1.2.2 -r1.1.2.3 --- openacs-4/packages/acs-core-docs/www/object-system-design.adp 9 Jun 2016 08:44:50 -0000 1.1.2.2 +++ openacs-4/packages/acs-core-docs/www/object-system-design.adp 23 Jun 2016 08:32:45 -0000 1.1.2.3 @@ -44,15 +44,16 @@ of application objects include:
forum messages
A user home page
A ticket in the ticket tracker
In the past, developers had to use ad-hoc and inconsistent -schemes to interface to various "general" services. OpenACS 4 -defines a central data model that keeps track of the application -objects that we wish to manage, and serves as a primary store of -metadata. By metadata, we mean data stored on behalf -of an application outside of -the application's data model in order to enable certain central -services. The OpenACS 4 Object Model (or object system) manages -several different kinds of data and metadata to allow us to provide -general services to applications:
Every application object is given a unique identifier in the system. This identifier can be used to find all data related to a @@ -103,15 +104,15 @@ relation.
Also, in OpenACS 3.x many utility modules exist that do nothing more than attach some extra attributes to existing application data. For example, general comments maintains a table that maps -application "page" data (static or dynamic pages on the website) to -one or more user comments on that page. It does so by constructing -a unique identifier for each page, usually a combination of the -table in which the data is stored, and the value of the primary key -value for the particular page. This idiom is referred to as the -"(on_which_table + on_what_id)" method for identifying application -data. In particular, general comments stores its map from pages to -comments using a "(on_which_table + on_what_id)" key plus the ID of -the comment itself.
All of these composite key constructions are implicit object +application "page" data (static or dynamic pages on the +website) to one or more user comments on that page. It does so by +constructing a unique identifier for each page, usually a +combination of the table in which the data is stored, and the value +of the primary key value for the particular page. This idiom is +referred to as the "(on_which_table + on_what_id)" method +for identifying application data. In particular, general comments +stores its map from pages to comments using a "(on_which_table ++ on_what_id)" key plus the ID of the comment itself.
All of these composite key constructions are implicit object
identifiers - they build a unique ID out of other pieces of the
data model. The problem is that their definition and use is ad-hoc
and inconsistent, making the construction of generic
@@ -128,8 +129,8 @@
new API to make sure every object the system is to manage is
associated with a row in acs_objects
. More importantly, if they do
this, new services like general comments can be created without
-requiring existing applications to "hook into" them via new
-metadata.
+requiring existing applications to "hook into" them via +new metadata.
Note: Object
identifiers are a good example of metadata in the new system. Each
row in acs_objects
stores
@@ -142,9 +143,9 @@
Object Context and Access
Control
Until the implementation of the general permissions system, every OpenACS application had to manage access control to its data -separately. Later on, a notion of "scoping" was introduced into the -core data model.
"Scope" is a term best explained by example. Consider some
-hypothetical rows in the address_book
table:
... | public | ... | -
The first row represents an entry in User 123's personal address -book, the second row represents an entry in User Group 456's shared -address book, and the third row represents an entry in the site's -public address book.
In this way, the scoping columns identify the security context +
The first row represents an entry in User 123's personal +address book, the second row represents an entry in User Group +456's shared address book, and the third row represents an +entry in the site's public address book.
In this way, the scoping columns identify the security context in which a given object belongs, where each context is either a person or a group of people or the general public (itself a group of people).
In OpenACS 4, rather than breaking the world into a limited set
of scopes, every object lives in a single context. A context is just an abstract
@@ -172,12 +173,12 @@
forum topic as its context, and a forum topic might list a subsite
as its context. Thus, contexts make it easier to break the site up
into security domains according to its natural structure. An
-object's context is stored in the context_id
column of the acs_objects
table.
We use an object's context to provide a default answer to
+object's context is stored in the context_id
column of the acs_objects
table.
We use an object's context to provide a default answer to questions regarding access control. Whenever we ask a question of -the form "can user X perform action Y on object Z", the OpenACS -security model will defer to an object's context if there is no -information about user X's permission to perform action Y on object -Z.
The context system forms the basis for the rest of the OpenACS +the form "can user X perform action Y on object Z", the +OpenACS security model will defer to an object's context if +there is no information about user X's permission to perform +action Y on object Z.
The context system forms the basis for the rest of the OpenACS access control system, which is described in in two separate documents: one for the permissions system and another for the party groups system. @@ -192,9 +193,9 @@ 3.x
The user/group system allowed developers to define group types along with attributes to be
stored with each instance of a group type. Each group type could
define a helper table that stored attributes on each instance of
-the group type. This table was called the "_info
" table because the name was generated
-by appending _info
to the name
-of the group type.
The user/groups data model also provided the user_group_type_member_fields
and
+the group type. This table was called the "_info
" table because the name was
+generated by appending _info
to
+the name of the group type.
The user/groups data model also provided the user_group_type_member_fields
and
user_group_member_fields
tables
to define attributes for members of groups of a specific type and
for members of a specific group, respectively. The user_group_member_field_map
table stored
@@ -218,16 +219,16 @@
be more extensible. In OpenACS 3.x, many applications extended the
core data models by directly adding more columns, in order to
provide convenient access to new information. This resulted in core
-data tables that were too "fat", containing a hodge podge of
-unrelated information that should have been normalized away. The
+data tables that were too "fat", containing a hodge podge
+of unrelated information that should have been normalized away. The
canonical example of this is the explosion of the users
table in OpenACS 3.x. In addition to
being sloppy technically, these fat tables have a couple of other
problems:
They degrade performance.
Denormalization can make it hard to maintain consistency constraints on the data.
Object subtypes provide a way to factor the data model while
still keeping track of the fact that each member of a subtype (i.e.
-for each row in the subtype's table), is also a member of the
+for each row in the subtype's table), is also a member of the
parent type (i.e. there is a corresponding row in the parent type
table). Therefore, applications an use this mechanism without
worrying about this bookkeeping themselves, and we avoid having
@@ -247,18 +248,20 @@
attributes for catalog products, and the ec_custom_product_field_values
table stores
values for those attributes.
In the Photo DB data model, the ph_custom_photo_fields
table defines
attributes for the photographs owned by a specific user, and tables
-named according to the convention "ph_user_<user_id>_custom_info
" are
-used to store values for those attributes.
ph_user_<user_id>_custom_info
"
+are used to store values for those attributes.
In addition, there are some instances where we are not using
this model but should, e.g.
the users_preferences
table,
which stores preferences for registered users in columns such as
prefer_text_only_p
and
-dont_spam_me_p
. The "standard"
-way for an OpenACS 3.x-based application to add to the list of user
-preferences is to add a column to the users_preferences
table (exactly the kind
-of data model change that has historically complicated the process
-of upgrading to a more recent OpenACS version).
The Objet Model generalizes the scheme used in the old OpenACS
+dont_spam_me_p
. The
+"standard" way for an OpenACS 3.x-based application to
+add to the list of user preferences is to add a column to the
+users_preferences
table
+(exactly the kind of data model change that has historically
+complicated the process of upgrading to a more recent OpenACS
+version).
The Objet Model generalizes the scheme used in the old OpenACS
3.x user/groups system. It defines a table called acs_attributes
that record what attributes
belong to which object types, and how the attributes are stored. As
before, attributes can either be stored in helper tables, or in a
@@ -294,25 +297,25 @@
with extra attributes that store constraints on the relation, and
the types of objects the relation actually maps. In turn, each
instance of a relation type is an object that represents a single
-fact of the form "the object t of type T is related to the object r
-of type R." That is, each instance of a relation type is
-essentially just a pair of objects.
Relation types generalize mapping tables. For example, the 3.x +fact of the form "the object t of type T is related to the +object r of type R." That is, each instance of a relation type +is essentially just a pair of objects.
Relation types generalize mapping tables. For example, the 3.x user/groups data model can be largely duplicated using a single -relation type describing the "group membership" relation. Group -types would then be subtypes of this membership relation type. -Group type attributes would be attached to the relation type +relation type describing the "group membership" relation. +Group types would then be subtypes of this membership relation +type. Group type attributes would be attached to the relation type itself. Group member attributes would be attached to instances of the membership relation. Finally, the mapping table would be replaced by a central skinny table that the relation type system defines.
Relation types should be used when you want to be able to attach -data to the "fact" that object X and object Y are related to each -other. On the face of it, they seem like a redundant mechanism -however, since one could easily create a mapping table to do the -same thing. The advantage of registering this table as a relation -type is that in principle the OpenACS 4 object system could use the -meta data in the types table to do useful things in a generic way -on all relation types. But this mechanism doesn't really exist -yet.
Relation types are a somewhat abstract idea. To get a better +data to the "fact" that object X and object Y are related +to each other. On the face of it, they seem like a redundant +mechanism however, since one could easily create a mapping table to +do the same thing. The advantage of registering this table as a +relation type is that in principle the OpenACS 4 object system +could use the meta data in the types table to do useful things in a +generic way on all relation types. But this mechanism doesn't +really exist yet.
Relation types are a somewhat abstract idea. To get a better feel for them, you should just skip to the data model.
@@ -325,19 +328,20 @@ metadata:The presence of a framework for subtyping and inheritance always -brings up the question of why we don't just use an object database. -The main reason is that all of the major object database vendors -ship products that are effectively tied to some set of object -oriented programming languages. Their idea is to provide tight -language-level integration to lower the "impedance mismatch" -between the database and the language. Therefore, database objects -and types are generally directly modeled on language level objects -and types. Of course, this makes it nearly impossible to interact -with the database from a language that does not have this tight -coupling, and it limits the data models that we can write to ideas -that are expressible in the host language. In particular, we lose -many of the best features of the relational database model. This is -a disaster from an ease of use standpoint.
The "Object relational" systems provide an interesting +brings up the question of why we don't just use an object +database. The main reason is that all of the major object database +vendors ship products that are effectively tied to some set of +object oriented programming languages. Their idea is to provide +tight language-level integration to lower the "impedance +mismatch" between the database and the language. Therefore, +database objects and types are generally directly modeled on +language level objects and types. Of course, this makes it nearly +impossible to interact with the database from a language that does +not have this tight coupling, and it limits the data models that we +can write to ideas that are expressible in the host language. In +particular, we lose many of the best features of the relational +database model. This is a disaster from an ease of use +standpoint.
The "Object relational" systems provide an interesting alternative. Here, some notion of subtyping is embedded into an existing SQL or SQL-like database engine. Examples of systems like this include the new Informix, PostgreSQL 7, and Oracle has @@ -358,7 +362,7 @@ object oriented features we need most.
In the context of OpenACS 4, this means using the object model to make our data models more flexible, so that new modules can easily gain access to generic features. However, while the API -itself doesn't enforce the idea that applications only use the +itself doesn't enforce the idea that applications only use the object model for metadata, it is also the case that the data model is not designed to scale to large type hierarchies. In the more limited domain of the metadata model, this is acceptable since the @@ -404,21 +408,21 @@ The key things to note about this table are:
For every type, we store metadata for how to display this type
in certain contexts (pretty_name
and pretty_plural
).
If the type is a subtype, then its parent type is stored in the
-column supertype
.
We support a notion of "abstract" types that contain no
-instances (as of 9/2000 this is not actually used). These types
+column supertype
.
We support a notion of "abstract" types that contain +no instances (as of 9/2000 this is not actually used). These types exist only to be subtyped. An example might be a type representing -"shapes" that contains common characteristics of all shapes, but -which is only used to create subtypes that represent real, concrete -shapes like circles, squares, and so on.
Every type defines a table in which one can find one row for +"shapes" that contains common characteristics of all +shapes, but which is only used to create subtypes that represent +real, concrete shapes like circles, squares, and so on.
Every type defines a table in which one can find one row for
every instance of this type (table_name
, id_column
).
type_extension_table
is for
naming a table that stores extra generic attributes.
The second table we use to describe types is acs_attributes
. Each row in this table
represents a single attribute on a specific object type (e.g. the
-"password" attribute of the "user" type). Again, here is an
-abbreviated version of what this table looks like. The actual table
-used in the implementation is somewhat different and is discussed
-in a separate document.
create table acs_attributes (
+"password" attribute of the "user" type).
+Again, here is an abbreviated version of what this table looks
+like. The actual table used in the implementation is somewhat
+different and is discussed in a separate document.create table acs_attributes (
attribute_id integer not null primary key
object_type not null references acs_object_types (object_type),
attribute_name varchar(100) not null,
@@ -439,16 +443,17 @@
(pretty_name
, sort_order
).The data_type
column stores
type information on this attribute. This is not the SQL type of the
attribute; it is just a human readable name for the type of data we
-think the attribute holds (e.g. "String", or "Money"). This might
-be used later to generate a user interface.
The sort_order
column stores
-information about how to sort the attribute values.
Attributes can either be stored explicitly in a table ("type
-specific storage") or in a skinny table ("generic storage"). In
-most cases, an attribute maps directly to a column in the table
-identified by the table_name
of
-the corresponding object type, although, as mentioned above, we
-sometimes store attribute values as key-value pairs in a "skinny"
-table. However, when you ask the question "What are the attributes
-of this type of object?", you don't really care about how the
+think the attribute holds (e.g. "String", or
+"Money"). This might be used later to generate a user
+interface.
The sort_order
column stores
+information about how to sort the attribute values.
Attributes can either be stored explicitly in a table
+("type specific storage") or in a skinny table
+("generic storage"). In most cases, an attribute maps
+directly to a column in the table identified by the table_name
of the corresponding object
+type, although, as mentioned above, we sometimes store attribute
+values as key-value pairs in a "skinny" table. However,
+when you ask the question "What are the attributes of this
+type of object?", you don't really care about how the
values for each attribute are stored (in a column or as key-value
pairs); you expect to receive the complete list of all
attributes.
The max_n_values
and
@@ -463,8 +468,8 @@
used to generalize the 3.x notion of group member fields. These were fields
that a developer could store on each member of a group, but which
were contextualized to the membership relation. That is, they were
-really "attached" to the fact that a user was a member of a
-particular group, and not really attached to the user. This is a
+really "attached" to the fact that a user was a member of
+a particular group, and not really attached to the user. This is a
subtle but important distinction, because it allowed the 3.x system
to store multiple sets of attributes on a given user, one set for
each group membership relation in which they participated.
In OpenACS 4, this sort of data can be stored as a relationship
@@ -490,8 +495,8 @@
each instance of this relation type will be a pair of objects of
the appropriate types.
The role
columns store human
readable names for the roles played by each object in the relation
-(e.g. "employee" and "employer"). Each role must appear in the
-acs_rel_roles
.
The min_n_rels_one
column,
+(e.g. "employee" and "employer"). Each role
+must appear in the acs_rel_roles
.
The min_n_rels_one
column,
and its three friends allow the programmer to specify constraints
on how many objects any given object can be related to on either
side of the relation.
@@ -509,10 +514,10 @@
The operational level data model centers around the acs_objects
table. This table contains a
-single row for every instance of the type acs_object
. The table contains the object's
-unique identifier, a reference to its type, security information,
-and generic auditing information. Here is what the table looks
-like:
create table acs_objects (
+single row for every instance of the type acs_object
. The table contains the
+object's unique identifier, a reference to its type, security
+information, and generic auditing information. Here is what the
+table looks like:create table acs_objects (
object_id integer not null,
object_type not null
references acs_object_types (object_type),
@@ -532,9 +537,10 @@
stores the context hierarchy.Other tables in the core data model store additional information
related to objects. The table acs_attribute_values
and acs_static_attr_values
are used to store
attribute values that are not stored in a helper table associated
-with the object's type. The former is used for instance attributes
-while the latter is used for class-wide "static" values. These
-tables have the same basic form, so we'll only show the first:
create table acs_attribute_values (
+with the object's type. The former is used for instance
+attributes while the latter is used for class-wide
+"static" values. These tables have the same basic form,
+so we'll only show the first:create table acs_attribute_values (
object_id not null
references acs_objects (object_id) on delete cascade,
attribute_id not null
@@ -565,11 +571,11 @@
relation type to which this object belongs.The next two object IDs are the IDs of the objects being
mapped.
All this table does is store one row for every pair of objects -that we'd like to attach with a relation. Any additional attributes -that we'd like to attach to this pair of objects is specified in -the attributes of the relation type, and could be stored in any -number of places. As in the 3.x user/groups system, these places -include helper tables or generic skinny tables.
This table, along with acs_attributes
and acs_attribute_values
generalize the old
+that we'd like to attach with a relation. Any additional
+attributes that we'd like to attach to this pair of objects is
+specified in the attributes of the relation type, and could be
+stored in any number of places. As in the 3.x user/groups system,
+these places include helper tables or generic skinny tables.
This table, along with acs_attributes
and acs_attribute_values
generalize the old
user/group tables user_group_map
, user_group_member_fields_map
and
user_group_member_fields
.
acs_rels
table has an analogous role in
-storing information on relations.These are all the tables that we'll discuss in this document. -The rest of the Kernel data model is described in the documents for -subsites, the permissions system and for the groups +storing information on relations.
These are all the tables that we'll discuss in this +document. The rest of the Kernel data model is described in the +documents for subsites, the permissions system and for the groups system.
Some examples of how these tables are used in the system can be found in the discussion of the API, which comes next.
Now we'll examine each piece of the API in detail. Bear in mind -that the Object Model API is defined primarily through PL/SQL +API
Now we'll examine each piece of the API in detail. Bear in +mind that the Object Model API is defined primarily through PL/SQL packages.
The object system provides an API for creating new object types @@ -713,8 +719,8 @@ familiar with the data model at a lower level.
The function acs_object.new()
makes a new object for
you. The function acs_object.del()
deletes an object. As
before, this is an abbreviated interface with all the long type
-specs removed. See the data model or developer's guide for the full
-interface.
function new (
+specs removed. See the data model or developer's guide for the
+full interface. function new (
object_id in acs_objects.object_id%TYPE default null,
object_type in acs_objects.object_type%TYPE
default 'acs_object',
@@ -731,10 +737,10 @@
);
Next, we define some generic functions to manipulate attributes.
Again, these interfaces are useful to an extent, but for large
-scale queries, it's likely that developers would have to query the
-data model directly, and then encapsulate their queries in
+scale queries, it's likely that developers would have to query
+the data model directly, and then encapsulate their queries in
procedures.
For names, the default_name
-function is used if you don't want to define your own name
+function is used if you don't want to define your own name
function.
function name (
object_id in acs_objects.object_id%TYPE
) return varchar;
@@ -789,7 +795,7 @@
return v_ticket_id;
end new_ticket;
This function will typically be defined in the context of a
-PL/SQL package, but we've left it stand-alone here for
+PL/SQL package, but we've left it stand-alone here for
simplicity.
To summarize: in order to take advantage of OpenACS 4 services,
a new application need only do three things:
Define a data model to describe application objects. This can
@@ -814,9 +820,9 @@
relation types, and acs_rel
for
relating objects.
These two procedures just insert and remove roles from the
acs_rel_roles
table. This table
-stores the legal relationship "roles" that can be used when
-creating relation types. Examples of roles are, say, "member", or
-"employer".
procedure create_role (
+stores the legal relationship "roles" that can be used
+when creating relation types. Examples of roles are, say,
+"member", or "employer". procedure create_role (
role in acs_rel_roles.role%TYPE
);
@@ -888,9 +894,9 @@
type_extension_table => 'group_types',
name_method => 'acs_group.name'
);
-
In this example, we've made groups a subtype of acs_object
to make the code simpler. The
-actual data model is somewhat different. Also, we've assumed that
-there is a helper table called groups
to store information on groups, and
+
In this example, we've made groups a subtype of acs_object
to make the code simpler. The
+actual data model is somewhat different. Also, we've assumed
+that there is a helper table called groups
to store information on groups, and
that there is a helper table called group_types
that has been defined to store
extra attributes on groups.
Now, assuming we have another object type called person
to represent objects that can be
group members, we define the following relationship type for group
@@ -953,12 +959,12 @@
The Object Model's API and data model provides a small set of
-simple procedures that allow applications to create object types,
-object instances, and object relations. Most of the data model is
-straightforward; the relation type mechanism is a bit more complex,
-but in return it provides functionality on par with the old
-user/groups system in a more general way.
+Summary and Discussion
The Object Model's API and data model provides a small set +of simple procedures that allow applications to create object +types, object instances, and object relations. Most of the data +model is straightforward; the relation type mechanism is a bit more +complex, but in return it provides functionality on par with the +old user/groups system in a more general way.