ACS Documentation : Kernel Overview : ACS Object Model Documentation : Design
Not yet linked.
Before ACS 4, software developers writing ACS applications or modules would develop each data model separately. However, many applications built on ACS share certain characteristics or require certain common services. Examples of such services include:
All of these services involve relating additional service-related information to application data objects. Examples of application objects include:
In the past, developers had to use ad-hoc and inconsistent schemes to interface to various "general" services. ACS 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 ACS 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 particular object.
Every object is created in a particular security context, so the system can provide centralized access control.
Objects are instances of developer-defined object types. Object types allow developers to customize the data that is stored with each object.
Relation types provide a general mechanism for mapping instances of one object type (e.g. users) to instances of another object type (e.g. groups).
The next section will explore these facilities in the context of the the particular programming idioms that we wish to generalize.
Related Links
This design document should be read along with the design documents for the new groups system, subsites and the permissions system
The motivation for most of the facilities in the ACS 4 Object Model can be understood in the context of the 3.x code base and the kinds of programming idioms that evolved there. These are listed and discussed below.
Object identification is a central mechanism in ACS 4. Every
application object in ACS 4 has a unique ID which is mapped to a row
in a central table called acs_objects. Developers that
wish to use ACS 4 services need only take a few simple steps to make
sure that their application objects appear in this table. The fact
that every object has a known unique identifier means that the core
can deal with all objects in a generic way. In other words, we use
object identifiers to enable centralized services in a global and
uniform manner.
Implicit Object Identifiers in ACS 3.x
The motivation for implementing general object identifiers comes
from several observations of data models in ACS 3.x. Many modules use
a (user_id, group_id, scope) column-triple for the purpose of
recording ownership information on objects, for access
control. User/groups also uses (user_id, group_id) pairs
in its user_group_map table as a way to identify data
associated with a single membership relation.
Also, in ACS 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 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 application-independent services unnecessarily difficult.
Object Identifiers in ACS 4
The ACS 4 Object Model defines a single mechanism that applications
use to attach unique identifiers to application data. This identifier
is the primary key of the acs_objects table. This table
forms the core of what we need to provide generic services like access
control, general attribute storage, general presentation and forms
tools, and generalized administrative interfaces. In addition, the
object system provides an API that makes it easy to create new objects
when creating application data. All an application must do to take
advantage of general services in ACS 4 is to use the 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.
Note: Object identifiers are a good example of metadata in
the new system. Each row in
Until the implementation of the general permissions system,
every ACS 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
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 ACS 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 name for the default security domain to which the object
belongs. Each context has a unique identifier, and all the contexts
in a system form a tree. Often this tree will reflect an observed
hierarchy in a site, e.g. a bboard message would probably list
a bboard topic as its context, and a bboard 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
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 ACS 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 ACS access
control system, which is described in in two separate documents: one
for the permissions system
and another for the party groups
system. The context system is also used to implement
subsites.
As mentioned above, many ACS modules provide extensible data
models, and need to use application specific mechanisms to keep track
of user defined attributes and to map application data to these
attributes. In the past, modules either used user/groups or their own
ad hoc data model to provide this functionality.
User/Groups in ACS 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
"
The user/groups data model also provided the
Many applications in ACS 3.x and earlier used the group type mechanism
in ways that were only tangentially related to groups of users, just
to obtain access to this group types mechanism. Thus the motivation
for generalizing the group types mechanism in ACS 4.
Object Types and Subtypes
In ACS 4 object types generalize the ACS 3.x notion of group
types. Each object type can define one or more attributes to be
attached to instances of the type. This allows developers to define
new types without being artificially tied to a particular module
(i.e. user/groups).
In addition, the ACS 4 object model provides mechanism for defining
subtypes of existing types. A subtype of a parent type inherits
all the attributes defined in the parent type, and can define some of
its own. The motivation for subtypes comes from the need for ACS to be
more extensible. In ACS 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 canonical example of this is the
explosion of the
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 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 applications
pollute the core data model with their specific information.
As we described above, the ACS 3.x user/groups system stored object
attributes in two ways. The first was to use columns in the helper
table. The second consisted of two tables, one describing attributes
and one storing values, to provide a flexible means for attaching
attributes to metadata objects. This style of attribute storage is
used in several other parts of ACS 3.x, and we will refer to it as
"skinny tables". For example:
In addition, there are some instances where we are not using this
model but should, e.g. the
The Objet Model generalizes the scheme used in the old ACS 3.x
user/groups system. It defines a table called
Many ACS 3.x modules use mapping tables to model relationships
between application objects. Again, the 3.x user/groups system
provides the canonical example of this design style. In that system,
there was a single table called
In ACS 4, relation types generalize this mechanism. Relation
types allow developers to define general mappings from objects of a
given type T, to other objects of a given type R. Each relation type
is a subtype of
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 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 ACS 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.
The ACS 4 Object Model is designed to generalize and unify the following
mechanisms that are repeatedly implemented in ACS-based systems to
manage generic and application specific 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, and makes it impossible to use these systems for the
development that we do at ArsDigita.
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 something
like this too. The main problem with these systems: each one
implements their own non-portable extensions to SQL to implement
subtyping. Thus, making ACS data models portable would become even
more difficult. In addition, each of these object systems have
strange limitations that make using inheritance difficult in
practice. Finally, object databases are not as widely used as
traditional relational systems. They have not been tested as
extensively and their scalability to very large databases is not
proven (though some will disagree with this statement).
The conclusion: the best design is to add a limited notion of
subtyping to our existing relational data model. By doing this, we
retain all the power of the relational data model while gaining the
object oriented features we need most.
In the context of ACS 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 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 type hierarchy is fairly small. But the
object system data model is not designed to support, for example, a huge type
tree like the Java runtime libraries might define.
This last point cannot be over-stressed: the object model is not
meant to be used for large scale application data storage. It is
meant to represent and store metadata, not application data.
Like most data models, the ACS Core data model has two levels:
(Note that we have subdivided the operational level into the latter
two files.)
The operational level depends on the knowledge level, so we discuss
the knowledge level first. In the text below, we include abbreviated
versions of the SQL definitions of many tables. Generally, these match
the actual definitions in the existing data model but they are meant
to reflect design information, not implementation. Some less relevant
columns may be left out, and things like constraint names are not
included.
The knowledge level data model for ACS objects centers around three
tables that keep track of object types, attributes, and relation
types. The first table is
This table contains one row for every object type in the system. The
key things to note about this table are:
The second table we use to describe types is
The following points are important about this table:
The final part of the knowledge level model keeps track of relationship
types. We said above that object relationships are 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 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 ACS 4, this sort of data can be stored as a relationship type, in
Things to note about this table:
acs_objects stores
information about the application object, but not the
application object itself. This becomes more clear if you skip ahead
and look at the SQL schema code that defines this table.
Object Context and Access Control
address_book table:
... scopeuser_idgroup_id... ... user123 ... ... group 456... ... public ... context_id
column of the acs_objects table.
Object Types
_info" table because the name was generated by appending
_info to the name of the group type.
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 values for both categories of attributes in its
field_value column. These tables allowed developers and
users to define custom sets of attributes to store on groups and group
members without changing the data model at the code level.
users table in ACS 3.x. In addition to
being sloppy technically, these fat tables have a couple of other
problems:
Object Attributes, Skinny Tables
ec_custom_product_fields table defines attributes for
catalog products, and the ec_custom_product_field_values
table stores values for those attributes.
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.
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 ACS 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 ACS version).
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 single
central skinny table. The developer makes this choice on a case by
case basis. For the most part, attribute data is stored in helper
tables so that they can take full advantage of relational data
modeling and because they will generally be more efficient.
Occasionally, a data model will use skinny tables because doing so
allows developers and users to dynamically update the set of
attributes stored on an object without updating the data model at the
code level. The bottom line: Helper tables are more functional and
more efficient, skinny tables are more flexible but limited.
Relation Types
user_group_map that kept
track of which users belonged to what groups. In addition, as we
discussed in the previous section, the system used the
user_group_member_fields and
user_group_member_fields_map tables to allow developers
to attach custom attributes to group members. In fact, these
attributes were not really attached to the users, but to the fact that
a user was a member of a particular group - a subtle but important
distinction.
acs_object, extended 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.
IV. Summary and Design Considerations
Why not Object Databases?
Oracle
V. Data Model
You can browse the data models themselves from here:
Knowledge-Level Model
acs_object_types, shown here in
an abbreviated form:
create table acs_object_types (
object_type varchar(100) not null primary key,
supertype references acs_object_types (object_type),
abstract_p char(1) default 'f' not null
pretty_name varchar(100) not null unique,
pretty_plural varchar(100) not null unique,
table_name varchar(30) not null unique,
id_column varchar(30) not null,
name_method varchar(30),
type_extension_table varchar(30)
);
pretty_name and
pretty_plural).
supertype.
table_name,
id_column).
type_extension_table is for naming a table that
stores extra generic attributes.
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 (
attribute_id integer not null primary key
object_type not null references acs_object_types (object_type),
attribute_name varchar(100) not null,
pretty_name varchar(100) not null,
pretty_plural varchar(100),
sort_order integer not null,
datatype not null,
default_value varchar(4000),
storage varchar(13) default 'type_specific'
check (storage in ('type_specific',
'generic')),
min_n_values integer default 1 not null,
max_n_values integer default 1 not null,
static_p varchar(1)
);
pretty_name, sort_order).
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.
sort_order column stores information about how to
sort the attribute values.
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.
max_n_values and min_n_values
columns encode information about the number of values an attribute may
hold. Attributes can be defined to hold 0 or more total values.
static_p flag indicates whether this attribute
value is shard by all instances of a type, as with static member
fields in C++. Static attribute are like group level attributes in ACS
3.x.
acs_rel_types. The key parts of this table look like
this:
create table acs_rel_types (
rel_type varchar(100) not null
references acs_object_types(object_type),
object_type_one not null
references acs_object_types (object_type),
role_one references acs_rel_roles (role),
object_type_two not null
references acs_object_types (object_type),
role_two references acs_rel_roles (role)
min_n_rels_one integer default 0 not null,
max_n_rels_one integer,
min_n_rels_two integer default 0 not null,
max_n_rels_two integer
);
object_type_one and instances of
object_type_two. Therefore, each instance of this
relation type will be a pair of objects of the appropriate types.
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.
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.
This table is easier to understand if you also know how the
acs_rels table works.
To summarize, the acs_object_types and
acs_attributes tables store metadata that describes every
object type and attribute in the system. These tables generalize the
group types data model in ACS 3.x. The acs_rel_types
table stores information about relation types.
This part of the data model is somewhat analogous to the data dictionary in Oracle. The information stored here is primarily metadata that describes the data stored in the operational level of the data model, which is discussed next.
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 (
object_id integer not null,
object_type not null
references acs_object_types (object_type),
context_id references acs_objects(object_id),
security_inherit_p char(1) default 't' not null,
check (security_inherit_p in ('t', 'f')),
creation_user integer,
creation_date date default sysdate not null,
creation_ip varchar(50),
last_modified date default sysdate not null,
modifying_user integer,
modifying_ip varchar(50)
);
As we said in Section III, security contexts are hierarchical and also
modeled as objects. There is another table called
acs_object_context_index that 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 (
object_id not null
references acs_objects (object_id) on delete cascade,
attribute_id not null
references acs_attributes (attribute_id),
attr_value varchar(4000),
primary key (object_id, attribute_id)
);
Finally, the table acs_rels is used to store object pairs
that are instances of a relation type.
create table acs_rels (
rel_id not null
references acs_objects (object_id)
primary key
rel_type not null
references acs_rel_types (rel_type),
object_id_one not null
references acs_objects (object_id),
object_id_two not null
references acs_objects (object_id),
unique (rel_type, object_id_one, object_id_two)
);
This table is somewhat subtle:
rel_id is the ID of an instance of some
relation type. We do this so we can store all the mapping tables in
this one table.
rel_type is the ID of the relation type
to which this object belongs.
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 user/group tables
user_group_map, user_group_member_fields_map
and user_group_member_fields.
The core tables in the ACS 4 data model store information about
instances of object types and relation types. The
acs_object table provides the central location that
contains a single row for every object in the system. Services can use
this table along with the metadata in stored in the knowledge level
data model to create, manage, query and manipulate objects in a
uniform manner. The 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 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 packages.
The object system provides an API for creating new object types and
then attaching attributes to them. The procedures
create_type and drop_type are used to create
and delete type definitions.
The two calls show up in the package acs_object_type.
procedure create_type (
object_type in acs_object_types.object_type%TYPE,
pretty_name in acs_object_types.pretty_name%TYPE,
pretty_plural in acs_object_types.pretty_plural%TYPE,
supertype in acs_object_types.supertype%TYPE
default 'acs_object',
table_name in acs_object_types.table_name%TYPE default null,
id_column in acs_object_types.id_column%TYPE default 'XXX',
abstract_p in acs_object_types.abstract_p%TYPE default 'f',
type_extension_table in acs_object_types.type_extension_table%TYPE
default null,
name_method in acs_object_types.name_method%TYPE default null
);
-- delete an object type definition
procedure drop_type (
object_type in acs_object_types.object_type%TYPE,
cascade_p in char default 'f'
);
Here the cascade_p argument indicates whether dropping a
type should also remove all its subtypes from the system.
We define a similar interface for defining attributes in the
package acs_attribute:
function create_attribute (
object_type in acs_attributes.object_type%TYPE,
attribute_name in acs_attributes.attribute_name%TYPE,
datatype in acs_attributes.datatype%TYPE,
pretty_name in acs_attributes.pretty_name%TYPE,
pretty_plural in acs_attributes.pretty_plural%TYPE default null,
table_name in acs_attributes.table_name%TYPE default null,
column_name in acs_attributes.column_name%TYPE default null,
default_value in acs_attributes.default_value%TYPE default null,
min_n_values in acs_attributes.min_n_values%TYPE default 1,
max_n_values in acs_attributes.max_n_values%TYPE default 1,
sort_order in acs_attributes.sort_order%TYPE default null,
storage in acs_attributes.storage%TYPE default 'type_specific',
static_p in acs_attributes.static_p%TYPE default 'f'
) return acs_attributes.attribute_id%TYPE;
procedure drop_attribute (
object_type in varchar,
attribute_name in varchar
);
In addition, the following two calls are available for attaching extra annotations onto attributes:
procedure add_description (
object_type in acs_attribute_descriptions.object_type%TYPE,
attribute_name in acs_attribute_descriptions.attribute_name%TYPE,
description_key in acs_attribute_descriptions.description_key%TYPE,
description in acs_attribute_descriptions.description%TYPE
);
procedure drop_description (
object_type in acs_attribute_descriptions.object_type%TYPE,
attribute_name in acs_attribute_descriptions.attribute_name%TYPE,
description_key in acs_attribute_descriptions.description_key%TYPE
);
At this point, what you must do to hook into the object system from your own data model becomes clear:
acs_object_type.create_type() to fill in the
metadata table on this new type. If you want your objects to appear in
the acs_objects table, then your new type must be a
subtype of acs_object.
acs_attribute.create_attribute() to fill in
information on the attributes that this type defines.
So, suppose we are writing a new version of the ticket tracker for
4.0. We probably define a table to store tickets in, and each ticket
might have an ID and a description. If we want each ticket to be an
object, then ticket_id must reference the
object_id column in acs_objects:
create table tickets (
ticket_id references acs_objects (object_id),
description varchar(512),
...
) ;
In addition to defining the table, we need this extra PL/SQL code to hook into the object type tables:
declare
attr_id acs_attributes.attribute_id%TYPE;
begin
acs_object_type.create_type (
supertype => 'acs_object',
object_type => 'ticket',
pretty_name => 'Ticket',
pretty_plural => 'Tickets',
table_name => 'tickets',
id_column => 'ticket_id',
name_method => 'acs_object.default_name'
);
attr_id := acs_attribute.create_attribute (
object_type => 'ticket',
attribute_name => 'description',
datatype => 'string',
pretty_name => 'Description',
pretty_plural => 'Descriptions'
);
... more attributes ...
commit;
end;
Thus, with a small amount of extra code, the new ticket tracker will
now automatically be hooked into every generic object service that
exists. Better still, this code need not be changed as new services
are added. As an aside, the most important service that requires you
to subtype acs_object is permissions.
The next important piece of the API is defined in the
acs_object package, and is concerned with creating and
managing objects. This part of the API is designed to take care of the
mundane bookkeeping needed to create objects and query their
attributes. Realistically however, limitations in PL/SQL and Oracle
will make it hard to build generic procedures for doing large scale
queries in the object system, so developers who need to do this will
probably have to be fairly familiar with the data model at a lower
level.
The function acs_object.new() makes a new object for
you. The function acs_object.delete() 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 (
object_id in acs_objects.object_id%TYPE default null,
object_type in acs_objects.object_type%TYPE
default 'acs_object',
creation_date in acs_objects.creation_date%TYPE
default sysdate,
creation_user in acs_objects.creation_user%TYPE
default null,
creation_ip in acs_objects.creation_ip%TYPE default null,
context_id in acs_objects.context_id%TYPE default null
) return acs_objects.object_id%TYPE;
procedure delete (
object_id in acs_objects.object_id%TYPE
);
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 procedures.
For names, the default_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;
function default_name (
object_id in acs_objects.object_id%TYPE
) return varchar;
The following functions tell you where attributes are stored, and fetch single attributes for you.
procedure get_attribute_storage (
object_id_in in acs_objects.object_id%TYPE,
attribute_name_in in acs_attributes.attribute_name%TYPE,
v_column out varchar2,
v_table_name out varchar2,
v_key_sql out varchar2
);
function get_attribute (
object_id_in in acs_objects.object_id%TYPE,
attribute_name_in in acs_attributes.attribute_name%TYPE
) return varchar2;
procedure set_attribute (
object_id_in in acs_objects.object_id%TYPE,
attribute_name_in in acs_attributes.attribute_name%TYPE,
value_in in varchar2
);
The main use of the acs_object package is to create
application objects and make them available for services via the
acs_objects table. To do this, you just have to make
sure you call acs_object.new() on objects that you wish
to appear in the acs_objects table. In addition, all such
objects must be instances of some subtype of acs_object.
Continuing the ticket example, we might define the following sort of procedure for creating a new ticket:
function new_ticket (
package_id in tickets.ticket_id%TYPE
default null,
description in tickets.description%TYPE default '',
...
) return tickets.ticket_id%TYPE
is
v_ticket_id tickets
begin
v_ticket_id := acs_object.new(
object_id => ticket_id,
object_type => 'ticket',
...
);
insert into tickets
(ticket_id, description)
values
(v_ticket_id, description);
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 simplicity.
To summarize: in order to take advantage of ACS 4 services, a new application need only do three things:
acs_object.new() in addition to whatever SQL code is
needed to insert a new row into the application data model.
One of the design goals of ACS 4 was to provide a straightforward and
consistent mechanism to provide applications with general services.
What we have seen here is that three simple steps and minimal changes
in the application data model are sufficient to make sure that
application objects are represented in the acs_objects
table. Subsequently, all of the general services in ACS 4
(i.e. permissions, general comments, and so on) are written to work
with any object that appears in acs_objects. Therefore,
in general these three steps are sufficient to make ACS 4 services
available to your application.
The relations system defines two packages: acs_rel_type
for creating and managing 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 (
role in acs_rel_roles.role%TYPE
);
procedure drop_role (
role in acs_rel_roles.role%TYPE
);
The main functions in the acs_rel_type package are used to
create and drop relation types.
procedure create_type (
rel_type in acs_rel_types.rel_type%TYPE,
pretty_name in acs_object_types.pretty_name%TYPE,
pretty_plural in acs_object_types.pretty_plural%TYPE,
supertype in acs_object_types.supertype%TYPE
default 'relationship',
table_name in acs_object_types.table_name%TYPE,
id_column in acs_object_types.id_column%TYPE,
abstract_p in acs_object_types.abstract_p%TYPE default 'f',
type_extension_table in acs_object_types.type_extension_table%TYPE
default null,
name_method in acs_object_types.name_method%TYPE default null,
object_type_one in acs_rel_types.object_type_one%TYPE,
role_one in acs_rel_types.role_one%TYPE default null,
min_n_rels_one in acs_rel_types.min_n_rels_one%TYPE,
max_n_rels_one in acs_rel_types.max_n_rels_one%TYPE,
object_type_two in acs_rel_types.object_type_two%TYPE,
role_two in acs_rel_types.role_two%TYPE default null,
min_n_rels_two in acs_rel_types.min_n_rels_two%TYPE,
max_n_rels_two in acs_rel_types.max_n_rels_two%TYPE
);
procedure drop_type (
rel_type in acs_rel_types.rel_type%TYPE,
cascade_p in char default 'f'
);
Finally, the acs_rel package provides an API that you use
to create and destroy instances of a relation type:
function new (
rel_id in acs_rels.rel_id%TYPE default null,
rel_type in acs_rels.rel_type%TYPE default 'relationship',
object_id_one in acs_rels.object_id_one%TYPE,
object_id_two in acs_rels.object_id_two%TYPE,
context_id in acs_objects.context_id%TYPE default null,
creation_user in acs_objects.creation_user%TYPE default null,
creation_ip in acs_objects.creation_ip%TYPE default null
) return acs_rels.rel_id%TYPE;
procedure delete (
rel_id in acs_rels.rel_id%TYPE
);
A good example of how to use relation types appears in the ACS 4 data model for groups. As in 3.x, group membership is modeled using a mapping table, but now we create this mapping using relation types instead of explicitly creating a table. First, we create a helper table to store state on each membership fact:
create table membership_rels (
rel_id constraint membership_rel_rel_id_fk
references acs_rels (rel_id)
constraint membership_rel_rel_id_pk
primary key,
-- null means waiting for admin approval
member_state varchar(20) constraint membership_rel_mem_ck
check (member_state in ('approved', 'banned',
'rejected', 'deleted'))
);
Then, we create a new object type to describe groups.
acs_object_type.create_type (
object_type => 'group',
pretty_name => 'Group',
pretty_plural => 'Groups',
table_name => 'groups',
id_column => 'group_id',
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 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 membership:
acs_rel_type.create_role ('member');
acs_rel_type.create_type (
rel_type => 'membership_rel',
pretty_name => 'Membership Relation',
pretty_plural => 'Membership Relationships',
table_name => 'membership_rels',
id_column => 'rel_id',
object_type_one => 'group',
min_n_rels_one => 0, max_n_rels_one => null,
object_type_two => 'person', role_two => 'member',
min_n_rels_two => 0, max_n_rels_two => null
);
Now we can define the following procedure to add a new member to a
group. All this function does is create a new instance of the
membership relation type and then insert the membership state into the
helper table that we define above. In the actual implementation, this
function is implemented in the membership_rel
package. Here we just define an independent function:
function member_add (
rel_id in membership_rels.rel_id%TYPE default null,
rel_type in acs_rels.rel_type%TYPE default 'membership_rel',
group in acs_rels.object_id_one%TYPE,
member in acs_rels.object_id_two%TYPE,
member_state in membership_rels.member_state%TYPE default null,
creation_user in acs_objects.creation_user%TYPE default null,
creation_ip in acs_objects.creation_ip%TYPE default null
) return membership_rels.rel_id%TYPE
is
v_rel_id integer;
begin
v_rel_id := acs_rel.new (
rel_id => rel_id,
rel_type => rel_type,
object_id_one => group,
object_id_two => person,
context_id => object_id_one,
creation_user => creation_user,
creation_ip => creation_ip
);
insert into membership_rels
(rel_id, member_state)
value
(v_rel_id, new.member_state);
end;
Another simple function can be defined to remove a member from a group:
procedure member_delete (
rel_id in membership_rels.rel_id%TYPE
)
is
begin
delete from membership_rels
where rel_id = membership_rel.delete.rel_id;
acs_rel.delete(rel_id);
end;
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.
Nothing here yet.
Pete Su generated this document from material culled from other documents by Michael Yoon, Richard Li and Rafael Schloming. But, any remaining lies are his and his alone.
| Document Revision # | Action Taken, Notes | When? | By Whom? |
|---|---|---|---|
| 0.1 | Creation | 9/09/2000 | Pete Su |
| 0.2 | Edited for ACS 4 Beta | 9/30/2000 | Kai Wu |
| 0.3 | Edited for ACS 4.0.1, fixed some mistakes, removed use of term "OM" | 11/07/2000 | Pete Su |