Ok, it seems unlikely that we are going to find a "free lunch" here. Agreed, it probably would be expensive to pre-fetch *all* metadata. But what if ContentManager kept ThreadLocal refererences to Sessions just for "read" purposes? Then, you could pre-fetch the most common metadata items, like... - page title/name - ACL - version - creation/modification times - author
Page contents would not be pre-fetched. Calling getContent() would grab an existing Session and use it to get the content. For the modify/write case, any of the set() methods store local, uncommitted changes in the WikiPage object. The save() method starts a new Session and commits. Or maybe any of set() methods start a new transaction which is held open until the save(). Basically, the idea is to make read-only access very cheap, but writes more expensive... and to hide all of the state management details from callers. My thinking on this has been influenced by Alfresco. Here are some interesting notes about what they are doing: http://wiki.alfresco.com/wiki/Introducing_the_Alfresco_Java_Content_Repository_API http://wiki.alfresco.com/wiki/Java_Foundation_API Andrew On Mon, Feb 9, 2009 at 10:36 AM, Janne Jalkanen <[email protected]> wrote: > public class WikiPage > { > private Node m_node; > > public String getContentAsString() > { > return m_node.getProperty("wiki:content").getString(); > } > } > > Getting a new Node reference for each access is simply too expensive, > and so would pre-emptively fetching all metadata into memory also be. > > We can't subclass nor implement, due to the way JCR API is > designed. Node is an interface, which you receive from the JCR > Session with Session.getItem(); > > /Janne > > On Mon, Feb 09, 2009 at 08:18:28AM -0500, Andrew Jaquith wrote: >> Janne, is WikiPage == Node? (subclass or implementation). Seems like >> there's an implied tight coupling between the two... >> >> On Feb 9, 2009, at 4:12, Janne Jalkanen <[email protected]> wrote: >> >>>> Re-reading your e-mail, I *think* I understand the comment you made >>>> about not wanting to keep a ThreadLocal field in ContentManager that >>>> keeps a reference to the current Session. But why keep the Session? >>>> Couldn't a getPage() operation open a new Session, get the page, then >>>> close the Session and return the page? Then there'd be no reason to >>>> stash the Session anywhere. >>> >>> The problem is that the Node keeps a reference to a Session, and the >>> WikiPage holds a reference to the Node (if it does not, it is >>> impossible to manipulate the content of the repository through >>> WikiPage methods, such as get/setAttribute()). >>> >>> So, if getPage() closes the session, all attempts to use the WikiPage >>> for anything will fail spectacularly. >>> >>>> Or alternatively, maybe you (we) guarantee that any time we get a >>>> Session we have to complete work on the session, then call refresh(), >>>> before returning. That would mean you could keep ThreadLocals in >>>> ContentManager. But maybe I'm missing your point about the exception >>>> cases. Could you explain further? >>> >>> Guaranteeing calling Session.refresh() means the following pattern: >>> >>> m_engine = ... // Get engine somehow >>> try >>> { >>> WikiPage p = m_engine.getPage(...) >>> // Do something >>> } >>> finally >>> { >>> // This must be done here, or exceptions will go upwards and >>> // one might end up with unpurged data in the Session. >>> m_engine.getContentManager().refresh(); // Calls Session.refresh() >>> } >>> >>> So in the end this pattern is *exactly* the same as the pattern above, >>> with the exception that this does not explicitly state the start of >>> the Session, and leaves the lifecycle muddy. We can't call >>> refresh in our WikiFilter because that does not work for embedders. >>> >>> Note that for the most part, the pattern I suggested will only affect >>> WikiFilter and WikiContextFactory, since those are the ones who get >>> WikiEngine. Most of our APIs get a WikiContext, and for plugin writers >>> *nothing* would change (because they did not acquire a WikiEngine, >>> they just used WikiContext.getEngine()). >>> >>> /Janne >
