Cosmo 0.7 Architecture Notes
Changes from 0.6.1
- ContentItem is now abstract
- Files represented as FileItem instead of ContentItem
- time-range indexes refactored and now stored on EventStamp
- icalendar property indexes removed (this plus time-range index refactoring results in smaller db size)
- new query api that uses new ItemFilter model
The cosmo source code is organized into java packages. The major packages are:
| package name || description |
| org.osaf.cosmo.model || model objects |
| org.osaf.cosmo.service || service api |
| org.osaf.cosmo.service.impl || service api implementation |
| org.osaf.cosmo.dao || dao api |
| org.osaf.cosmo.dao.hibernate || hibernate dao implementation |
| org.osaf.cosmo.hibernate || cosmo hibernate extensions |
| org.osaf.cosmo.acegisecurity.* || cosmo acegi security extensions |
| org.osaf.cosmo.security || security api used in cosmo |
| org.osaf.cosmo.security.impl || security api implementation |
| org.osaf.cosmo.dav.* || WebDAV/CalDAV protocol |
| org.osaf.cosmo.atom.* || Atom protocol |
| org.osaf.cosmo.mc || Morse Code protocol |
| org.osaf.cosmo.cmp || CMP protocol |
| org.osaf.cosmo.ui.* || Application code for Account Browser ui |
The rest of this document is going to focus mainly on the model,service, and dao layers.
represents a Cosmo user. In addition to standard attributes such as username
, and lastName
contains a Set
s which allow arbitrary attributes to be associated to a User
. These preferences are mainly used by the ui. A Set
s provides a way to associate "subscription" collections to a user.
All content in Cosmo inherits from the abstract Item
defines common attributes such as uid
(unique identifier), name
(used in DAV URI), displayName
(item description), and common associations such as owner
(user that created item), parents
(collections that item belong to), attributes
, and stamps
A group of items is called a collection. Collections are modeled as items ( CollectionItem
) with an additional association children
. Cosmo 0.7 supports items belonging to a multiple collections. HomeCollectionItem
is a special CollectionItem
that represents the root collection for a user. When a user is created a HomeCollectionItem
is automatically created. It is possible to have collections within collections.
The abstract class ContentItem
represents a piece of content. There are two implementations. FileItem
represents a file with binary content. All files created through DAV are stored as FilteItem. NoteItem
is the base for all pim (personal information manager) items. A personal information item includes notes, tasks, events, messages and any combination of these.
Items are mapped to the item
table using a table per class hierarchy
This means all Item
subclasses are mapped to the same table, and use a discriminator column ( itemtype
) to denote the item type for the row.
has a Map
objects, indexed by a QName
. An Attribute
can be associated to a single Item
is a composite key that represents a qualified name (namespace and local name). There are several Attribute
implementations (String, Binary, Integer, etc) that are used to store different types of values. The polymorphic mapping allows any number of any mix of Attribute
implementations to be associated to an item.
Attributes are mapped to the attribute
table using a table per class hierarchy
in Hibernate. All Attribute
implementations are stored in the same table, and use a discriminator column ( attributetype
) to denote the attribute type for the row.
There is a bi-directional association between Item
, with Item
containing the inverse mapping. This means that Attribute
is responsible for managing the association.
Save, update and delete operations are cascaded to Attriubte
has a Set
objects. A stamp can be associated to a single Item
A stamp associates a related set of properties/attributes/methods to an Item
This is different from Attribute
, which generally represents a single value.
For example EventStamp
, contains properties and apis relating to an event and MessageStamp
contains properties related to an email message. The stamp model allows items to be stamped and unstamped with these related sets of properties throughout its life-cycle.
is another stamp implementation that is usedto indicate that a CollectionItem
is a calendar collection.
, the polymorphic mapping allows any number and mix of stamps
to be associated to an Item
Stamps are mapped to the stamp
table using a table per class hierarchy
This means all item implementations are stored in the same table, and use a discriminator column ( stamptype
) to denote the stamp type for the row.
are mapped to secondary
tables ( event_stamp
) that is joined to the primary stamp table.
Note that not all Stamp
implementations require a new table. MessageStamp
is implemented by storing its data as item attributes. One benefit (or drawback depending on how you look at it) of this is that
when a stamp is removed, the underlying attributes remain associated to the item, allowing the item to be "stamped" and "unstamped" without losing the stamp data. There is a bi-directional association between
containing the inverse mapping. Like Attribute
, stamp creation, modification and deletion is cascaded by Item
has a set of associated Ticket
objects. A ticket can be associated to a single Item
A ticket is an authorization object that grants some level of access to an item and its descendents based on a unique ticket key. If a client has this key, then the client can gain access to the item and its descendants with the ticket key.
Tickets are mapped to the tickets
table. Ticket privileges are mapped to the ticket_privilege
table. There is a bi-directional association between Item
, with Item
containing the inverse mapping.
contains a set of CollectionSubscription
s. A CollectionSubscription
contains a ticket key and a collection uid. It is a way to associate a collection that is not owned by the user to the user. The ticket key provides the necessary authentication to access the collection.
has a set of associated Tombstone
objects. A tombstone can be associated to a single Item
. A tombstone is a way to represent that something existed and was removed from the item. The implementations are ItemTombstone
, and AttributeTombstone
are added to CollectionItem
and represent that an item was removed from the collection. StampTombstone
are used to represent that a stamp and attribute have been removed from the collection. Tombstones are used for synchronization purposes (think the morse code protocol) where a client only wants the changes from the last time it synchronized.
As stated before, NoteItem
provides the basis for a pim item in Cosmo. Cosmo 0.7 supports the full range of pim items that Chandler supports. All pim items start out as a NoteItem
. The NoteItem
can be stamped as a task, event and message. A calendar event is represented by NoteItem
stamped with EventStamp
contains an ical4j Calendar
which is an iCalendar component as defined in RFC 2445. The iCalendar component must contain a single VEVENT component (master event).
Note that this allows Cosmo to support events submitted using an external protocol such as CalDAV because the internal representation of an event is iCalendar. Cosmo preserves all valid iCalendar data.
Indexing Calendar Items
In order for Cosmo to provide useful queries such as a time-range query, it must be smart about indexing events. Cosmo 0.7 indexes events by calculating and storing extra data on the EventStamp
during creation/modification of the event. This data includes a start and end date for the event and whether that event is recurring or floating (no timezone associated with the event).
Previous versions of Cosmo indexed every occurrence of a recurring event, which didn’t prove well for recurring events with no end (Cosmo only indexed a year or so). Cosmo 0.7 handles these events by expanding them during the query, allowing time-range queries well into the future.
Cosmo 0.7 uses Hibernate annotations (http://www.hibernate.org/397.html
) on all the model classes ( org.osaf.cosmo.model
) to map Cosmo objects to database tables. This frees us from the use of separate XML config files (as used in Cosmo 0.5).
All package level Hibernate configuration annotations are defined in org.osaf.cosmo.model.package-info.java
. This includes named queries and custom types.
Cosmo utlizes Hiberate's second level and query caches. The cache implementation used is Ehcache, which can be configured using echache.xml
Cosmo 0.7 has been developed and tested on Derby 10.2.2.0, MySQL?
), and PostgreSQL?
8.2 databases. Cosmo relies on Hibernate's auto schema generation to create the database schema
at startup (see DbInitializer.java
uses the annotations defined on all the model objects to automatically generate and execute the ddl statements that define the Cosmo schema. Because one of the goals for Cosmo is to support multiple databases, the database schema is not as optimized as is could be. We have to support the least common denominator, which means no custom triggers, stored procedures, or anything that is database specific that isn't handled by
Hibernate's dialect abstraction.
Cosmo makes extensive use of Hibernate's lazy-loading feature. This means associations such as children
, etc aren't loaded until they are needed. This allows additional data to be loaded from the db without requiring additional service calls. Cosmo uses the "Open Session in View" pattern to allow lazing loading outside of the service layer. This means a new Hibernate session is created for each request and kept open for the duration (implemented as a servlet filter).
The DAO layer in Cosmo provides the basic CRUD operations for the model objects. The main DAOs in Cosmo are UserDao
handles all User
CRUD operations and ContentDao
handles the ContentItem
CRUD operations. There is also a CalendarDao
that provides special calendar object query apis. The only consumer of the DAO layer is the Service layer. That is, the only code that uses DAO
objects is the Service layer.
Cosmo provides a Hibernate based implementations for the DAOs. These implementations extend from Spring's HibernateDaoSupport
more notes: CosmoZeroPointSevenDaoLayer
The service layer consists of UserService
and is what is exposed to the application. The transaction boundary in Cosmo is at the service layer, meaning each service call is a transaction. A service can utilize several DAOs. One example is UserService
.createUser(), which needs to create a new User
and create a new HomeCollection
for that User
more notes: CosmoZeroPointSevenServiceLayer
Cosmo uses Spring's declarative transaction management TransactionProxyFactoryBean
for the service layer. There is no transaction code in any of the Cosmo service/dao implementation classes. Instead, transactional behavior is defined in Spring's applicationContext.xml
. For Cosmo, this means each method called on a service requires a transaction to be present, and will create a new transaction if
one doesn't exist. Cosmo relies on transactions for atomicity. If a service method is called that updates multiple database tables and fails halfway through, the transaction is rolled back instead of leaving Cosmo in an invalid state. This requires the use of a database that supports transactions. For MySQL?
this means using InnoDB?
Cosmo utilizes the Spring framework extensively to instantiate and configure the majority of the service/dao layer objects. An example is the Hibernate SessionFactory
(no hibernate.cfg.xml file as everything is configured in Spring's applicationContext.xml).
- 20 Aug 2007