Design Notes
Component Preparation
When the code was added to the 2.1 Sakai release, it became apparent that it would be difficult to extract a clean component/service/api layered structure. If we use any hibernate based components that are loaded from the component, shared or server class loaders in the request cycle it will become necessary to complete this refactor.
Package Sub Tree: uk.ac.cam.caret.sakai.rwiki.service
All packages in this subtree are pure API, that can be placed in the shared area. They do not depend on components or tool.
Package Sub Tree: uk.ac.cam.caret.sakai.rwiki.component
All packages in this subtree are pure Component. They depend on the service api. They are used by the tool. It is expected that this super component will be deployed under the components class loaded in the future. All Hibernate DAO's and related POJO implementations are at this level. There is nothing specific to the tool at this level.
Package Sub Tree: uk.ac.cam.caret.sakai.rwiki.tool
The tool implementation is located in this package, which will in the future be deployed in the war.
Status
Currently this code is only available under the Sakai 2.1 local branch which mirrors the main Sakai 2.1 Trunk work. We have not separated the deployment descriptors, so the application is still stove pipe in nature.
Model Implementation
Seperated History
We have now separated the Current wiki objects from the historical or versioned wiki objects. They are no longer linked at the hibernate level but are linked at a higher level.
The aim of this approach is to avoid loading all the history content when a page is accessed. Lazy loading would have performed this, however the implementation of Lazy loading in Hibernate is such that we loose some control and often see multiple statements issued for each lazy object. The code now contains RWikiCurrentObject to represent the current objects in the model, and RWikiHistoryObject to represent the versioned objects.
- Status: Implemented and tested
- Loacation: In Trunk
Unlinked content
Even with the above restructure, Hibernate loads the content of each page when any field in the page is accessed. Hence the service method getRWikiCurrentObject will cause the whole of the content of the object to be loaded regardless of if we wanted to use the content or not.
In effect this means as a page is rendered, and the exists() method on the service is invoked, potential all the linked content could be loaded. It would be far better if a lightweight index object was available that contained the smaller fields and a lazy loaded content field was present in the RWikiObject (Current and History) to ensure that the content was only loaded when the content was required.
I don't know if you can have lazy loaded one-to-one relationships in the same table. If you can, then that will address the issue. If not then we will need a separate table with a one-to-one relationship with the 'index' table.
On further investigation one-to-one lazy loading in hibernate is not possible, we could use one-to-many lazy loading, but only ever have one.
One to many lazy loading does not make sense, so we have implemented explicit lazy loading with an object proxy to inject the necessary DAO objects so that loading can take place. This approach works quite well but has the drawback that we have to proxy the object, and that Lists need a proxy implementation, however this is far faster than using an AOP or introspection based proxy, as its direct method to method calls.
- Status: Implemented and tested
- Loacation: In trunk
Data Migration
The above generates a problem. When someone updates the wiki, changes in schema will cause and apparent loss of functionality and content. Hibernate will perform the schema update, but it will not migrate content to the new scheama. So objects derived from the new schema will be missing data.
We need a mechanism, hooked into the last ContextListener that tomcat invokes that will migrate the data. This could be a simple SQL statement that is run against the schema, or it could use the objects present in RWiki to do more complex transformations.
RWiki not has a context listener that loads an injected DataMigrationSpecification from spring. This Specification contains a list of DataMigrationAgents, which it uses, insequence to migrate the data between schema version. To perform the above migrations we have a sql script migration agent, that runs sql script against the database. This is almost the same as the update schema mechinsim in hibernate.
Since we have added SHA1 hashes against the content, we have also added a SHAHashMigration component that extracts all objects of a type from the database using hibernate, and then computes the SHA1 hash of the content before updating them. This is configred in components.xml
We have tested this on our content in sakai-beta, which contains about 200 pages with an average of 20 versions per page. On a G4 1.3 the migration process, on startup takes 27 seconds.
- Status: Implemented and Tested
- Loacation: trunk
Schema Version Control
In order to be able to perform schema data migration, we need to know which version the code is and which version the database is at, independant of Hibernate. A sepeperate object that stored persisntat site wide properties will be created. This object will store, amongst other things, version numbers. Once of these version numbers will be the database scheam version number. This will enable the data migration listener to invoke the correct sequence of sql data migration statements on startup.
If any of the statements fail, this operation will fail and refuse to allow the Tool to start.
See data migration above
- Status: Implemented and tested
- Loacation: Trunk
SHA Hash of Content
Since we now version every change to the page including history, we need a fast way of determining if the page has changed or not. We have added a SHA hash to the content that can be used for this purpose, without going to the expense of loading all the content. We still need to modify the History page to indicate where no content has been changed between pages. At the moment it just displays the hash.
- Status: Implemented and tested
- Loacation: Trunk
Diagrams
These shoul be links
Info Page
- Info Page Wireframe
- Info Page Wireframe v2
Permissions Rules Diagrams
- Can Admin a page
- Can Create a page
- Can Read a page
- Can Update a page
Code Sequence
- Request Processing sequence