jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject svn commit: rev 55234 - in incubator/jackrabbit/trunk: applications/test src/java/org/apache/jackrabbit/core src/java/org/apache/jackrabbit/core/config src/java/org/apache/jackrabbit/core/fs src/java/org/apache/jackrabbit/core/fs/local src/java/org/apache/jackrabbit/core/jndi src/java/org/apache/jackrabbit/core/jndi/provider src/java/org/apache/jackrabbit/core/observation src/java/org/apache/jackrabbit/core/search src/java/org/apache/jackrabbit/core/search/jcrql src/java/org/apache/jackrabbit/core/search/lucene src/java/org/apache/jackrabbit/core/state src/java/org/apache/jackrabbit/core/state/tx src/java/org/apache/jackrabbit/core/state/xml src/java/org/apache/jackrabbit/core/util src/java/org/apache/jackrabbit/core/version src/java/org/apache/jackrabbit/core/xml
Date Thu, 21 Oct 2004 17:17:02 GMT
Author: stefan
Date: Thu Oct 21 10:17:01 2004
New Revision: 55234

Added:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateProvider.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/DefaultTransactionalStore.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/PlaybackListener.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/Transaction.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionException.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionImpl.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionListener.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionLog.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionManager.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalItemStateManager.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalNodeState.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalPropertyState.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalStore.java   (contents, props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/XASessionImpl.java   (contents, props changed)
Removed:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLNodeState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLPropertyState.java
Modified:
   incubator/jackrabbit/trunk/applications/test/   (props changed)
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/AccessManagerImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SearchManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/config.dtd
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/FileSystem.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/RandomAccessOutputStream.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/LocalFileSystem.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/RAFOutputStream.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/BindableRepository.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/RegistryHelper.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/provider/DummyInitialContextFactory.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/NamespaceMappings.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/jcrql/JCRQLParserTokenManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemDirectory.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemInputStream.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemOutputStream.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PathQuery.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistenceManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentNodeState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentPropertyState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ReferenceManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java
Log:
first implementation of jta support

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/AccessManagerImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/AccessManagerImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/AccessManagerImpl.java	Thu Oct 21 10:17:01 2004
@@ -46,6 +46,14 @@
     }
 
     /**
+     * Copy constructor
+     */
+    AccessManagerImpl(AccessManagerImpl other) {
+        this.hierMgr = other.hierMgr;
+        this.nsResolver = other.nsResolver;
+    }
+
+    /**
      * @param id
      * @param permissions
      * @return

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java	Thu Oct 21 10:17:01 2004
@@ -1087,7 +1087,9 @@
         }
 
         // all changes are persisted, now dispatch events
-        events.dispatch();
+        // forward this to the session to let it decide on the right time for those
+        // events to be dispatched in case of transactional support
+        session.dispatch(events);
     }
 
     /**

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java	Thu Oct 21 10:17:01 2004
@@ -50,6 +50,9 @@
  * <li>maintaining a cache of the item instances it created.
  * <li>checking access rights of associated <code>Session</code> in all methods.
  * </ul>
+ * <p/>
+ * If the parent <code>Session</code> is an <code>XASession</code>, there is
+ * one <code>ItemManager</code> instance per started global transaction.
  */
 public class ItemManager implements ItemLifeCycleListener {
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NamespaceRegistryImpl.java	Thu Oct 21 10:17:01 2004
@@ -199,6 +199,7 @@
 
     /**
      * Returns a prefix that is unique among the already registered prefixes.
+     *
      * @param uriHint namespace uri that serves as hint for the prefix generation
      * @return a unique prefix
      */

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java	Thu Oct 21 10:17:01 2004
@@ -23,10 +23,9 @@
 import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.observation.ObservationManagerFactory;
-import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.ItemStateProvider;
-import org.apache.jackrabbit.core.state.PersistentItemStateManager;
-import org.apache.jackrabbit.core.state.ReferenceManager;
+import org.apache.jackrabbit.core.state.*;
+import org.apache.jackrabbit.core.state.tx.TransactionManager;
+import org.apache.jackrabbit.core.state.tx.XASessionImpl;
 import org.apache.jackrabbit.core.util.uuid.UUID;
 import org.apache.jackrabbit.core.version.VersionManager;
 import org.apache.log4j.Logger;
@@ -82,6 +81,7 @@
     private final NamespaceRegistryImpl nsReg;
     private final NodeTypeRegistry ntReg;
     private final VersionManager vMgr;
+    private final TransactionManager txMgr;
 
     // configuration of the repository
     private final RepositoryConfig repConfig;
@@ -202,7 +202,7 @@
                 }
             } else {
                 // create new uuid
-                UUID rootUUID = UUID.randomUUID();	// version 4 uuid
+                UUID rootUUID = UUID.randomUUID();     // version 4 uuid
                 rootNodeUUID = rootUUID.toString();
                 try {
                     // persist uuid of the repository's root node
@@ -242,6 +242,17 @@
             throw new RepositoryException(msg, fse);
         }
 
+        // setup internal transaction manager
+        // @todo rewrite to use file system abstraction (FileSystem interface)
+        try {
+            File txRootDir = new File(repConfig.getHomeDir(), "tx");
+            txMgr = new TransactionManager(new File("tx"));
+        } catch (IOException ioe) {
+            String msg = "failed to initialize internal transaction manager";
+            log.error(msg, ioe);
+            throw new RepositoryException(msg, ioe);
+        }
+
         // workspaces
         Iterator iter = repConfig.getWorkspaceConfigs().iterator();
         while (iter.hasNext()) {
@@ -280,8 +291,10 @@
                         SYSTEM_ROOT_NAME.toJCRName(verSession.getNamespaceResolver()),
                         SYSTEM_ROOT_NAME.toJCRName(verSession.getNamespaceResolver()));
             }
-        } catch (NoPrefixDeclaredException e) {
-            throw new RepositoryException("Error: " + e.toString());
+        } catch (NoPrefixDeclaredException npde) {
+            String msg = "failed to initialize version manager";
+            log.error(msg, npde);
+            throw new RepositoryException(msg, npde);
         }
         vMgr = new VersionManager(verSession);
 
@@ -381,7 +394,7 @@
         return rootNodeUUID;
     }
 
-    synchronized PersistentItemStateManager getWorkspaceStateManager(String workspaceName)
+    synchronized PersistentItemStateProvider getWorkspaceStateManager(String workspaceName)
             throws NoSuchWorkspaceException, RepositoryException {
         // check state
         if (disposed) {
@@ -393,8 +406,8 @@
             throw new NoSuchWorkspaceException(workspaceName);
         }
         // get/create per named workspace (i.e. per physical storage) item state manager
-        PersistentItemStateManager stateMgr =
-                (PersistentItemStateManager) wspStateMgrs.get(workspaceName);
+        PersistentItemStateProvider stateMgr =
+                (PersistentItemStateProvider) wspStateMgrs.get(workspaceName);
         if (stateMgr == null) {
             // create state manager
             try {
@@ -481,7 +494,7 @@
                 }
                 ItemStateProvider stateProvider = getWorkspaceStateManager(workspaceName);
                 SystemSession s = getSystemSession(workspaceName);
-                searchMgr = new SearchManager(stateProvider, s.hierMgr, s,
+                searchMgr = new SearchManager(stateProvider, s.getHierarchyManager(), s,
                         wspConfig.getFileSystem(), wspConfig.getSearchIndexDir());
             } catch (IOException e) {
                 throw new RepositoryException("Exception opening search index.", e);
@@ -674,9 +687,8 @@
             return new SessionImpl(this, ANONYMOUS_CREDENTIALS, wspConfig);
         } else if (credentials instanceof SimpleCredentials) {
             // username/password credentials
-
             // @todo implement authentication/authorization
-            return new SessionImpl(this, credentials, wspConfig);
+            return new XASessionImpl(this, credentials, wspConfig, txMgr);
         } else {
             String msg = "login failed: incompatible credentials";
             log.error(msg);

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SearchManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SearchManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SearchManager.java	Thu Oct 21 10:17:01 2004
@@ -15,6 +15,8 @@
  */
 package org.apache.jackrabbit.core;
 
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.observation.EventImpl;
 import org.apache.jackrabbit.core.observation.SynchronousEventListener;
 import org.apache.jackrabbit.core.search.NamespaceMappings;
@@ -24,8 +26,6 @@
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.ItemStateProvider;
 import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.jackrabbit.core.fs.FileSystem;
-import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.log4j.Logger;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.apache.lucene.document.Document;
@@ -38,12 +38,7 @@
 import javax.jcr.observation.EventIterator;
 import javax.jcr.observation.EventType;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Collections;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.*;
 
 /**
  * Acts as a global entry point to execute queries and index nodes.
@@ -215,7 +210,7 @@
         }
         if (log.isDebugEnabled()) {
             log.debug("onEvent: indexing finished in "
-                    + String.valueOf(System.currentTimeMillis() - time) 
+                    + String.valueOf(System.currentTimeMillis() - time)
                     + " ms.");
         }
     }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java	Thu Oct 21 10:17:01 2004
@@ -15,11 +15,13 @@
  */
 package org.apache.jackrabbit.core;
 
+import org.apache.jackrabbit.core.config.WorkspaceConfig;
 import org.apache.jackrabbit.core.nodetype.*;
+import org.apache.jackrabbit.core.observation.EventStateCollection;
 import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PersistentItemStateProvider;
 import org.apache.jackrabbit.core.state.SessionItemStateManager;
 import org.apache.jackrabbit.core.xml.ImportHandler;
-import org.apache.jackrabbit.core.config.WorkspaceConfig;
 import org.apache.log4j.Logger;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
@@ -95,13 +97,14 @@
     protected final TransientNamespaceMappings nsMappings;
 
     /**
-     * Package private constructor.
+     * Protected constructor.
      *
      * @param rep
      * @param credentials
      * @param wspConfig
      */
-    SessionImpl(RepositoryImpl rep, Credentials credentials, WorkspaceConfig wspConfig)
+    protected SessionImpl(RepositoryImpl rep, Credentials credentials,
+                          WorkspaceConfig wspConfig)
             throws RepositoryException {
         this.rep = rep;
 
@@ -129,10 +132,11 @@
         String wspName = wspConfig.getName();
         wsp = new WorkspaceImpl(wspConfig, rep.getWorkspaceStateManager(wspName),
                 rep.getWorkspaceReferenceManager(wspName), rep, this);
-        itemStateMgr = new SessionItemStateManager(rep.getRootNodeUUID(), wsp.getPersistentStateManager(), getNamespaceResolver());
+
+        itemStateMgr = createSessionItemStateManager(wsp.getPersistentStateManager());
         hierMgr = itemStateMgr.getHierarchyMgr();
-        itemMgr = new ItemManager(itemStateMgr, hierMgr, this, ntMgr.getRootNodeDefinition(), rep.getRootNodeUUID());
-        accessMgr = new AccessManagerImpl(credentials, hierMgr, getNamespaceResolver());
+        itemMgr = createItemManager(itemStateMgr, hierMgr);
+        accessMgr = createAccessManager(credentials, hierMgr);
     }
 
     /**
@@ -156,7 +160,39 @@
                 rep.getWorkspaceReferenceManager(wspName), rep, this);
         itemStateMgr = new SessionItemStateManager(rep.getRootNodeUUID(), wsp.getPersistentStateManager(), getNamespaceResolver());
         hierMgr = itemStateMgr.getHierarchyMgr();
-        itemMgr = new ItemManager(itemStateMgr, hierMgr, this, ntMgr.getRootNodeDefinition(), rep.getRootNodeUUID());
+        itemMgr = createItemManager(itemStateMgr, hierMgr);
+    }
+
+    /**
+     * Create the session item state manager.
+     *
+     * @return session item state manager
+     */
+    protected SessionItemStateManager createSessionItemStateManager(PersistentItemStateProvider provider) {
+
+        return new SessionItemStateManager(rep.getRootNodeUUID(), provider, getNamespaceResolver());
+    }
+
+    /**
+     * Create the item manager.
+     *
+     * @return item manager
+     */
+    protected ItemManager createItemManager(SessionItemStateManager itemStateMgr,
+                                            HierarchyManager hierMgr) {
+
+        return new ItemManager(itemStateMgr, hierMgr, this,
+                ntMgr.getRootNodeDefinition(), rep.getRootNodeUUID());
+    }
+
+    /**
+     * Create the access manager.
+     *
+     * @return access manager
+     */
+    protected AccessManagerImpl createAccessManager(Credentials credentials,
+                                                    HierarchyManager hierMgr) {
+        return new AccessManagerImpl(credentials, hierMgr, getNamespaceResolver());
     }
 
     /**
@@ -164,7 +200,7 @@
      *
      * @return the <code>AccessManager</code> associated with this session
      */
-    AccessManagerImpl getAccessManager() {
+    protected AccessManagerImpl getAccessManager() {
         return accessMgr;
     }
 
@@ -182,7 +218,7 @@
      *
      * @return the <code>ItemManager</code>
      */
-    ItemManager getItemManager() {
+    protected ItemManager getItemManager() {
         return itemMgr;
     }
 
@@ -200,7 +236,7 @@
      *
      * @return the <code>SessionItemStateManager</code> associated with this session
      */
-    SessionItemStateManager getItemStateManager() {
+    protected SessionItemStateManager getItemStateManager() {
         return itemStateMgr;
     }
 
@@ -209,11 +245,21 @@
      *
      * @return the <code>HierarchyManager</code> associated with this session
      */
-    HierarchyManager getHierarchyManager() {
+    protected HierarchyManager getHierarchyManager() {
         return hierMgr;
     }
 
     /**
+     * Dispatch events belonging to a save operation.
+     *
+     * @param events events to dispatch as result of a successful save
+     *               operation
+     */
+    protected void dispatch(EventStateCollection events) {
+        events.dispatch();
+    }
+
+    /**
      * Dumps the state of this <code>Session</code> instance
      * (used for diagnostic purposes).
      *
@@ -223,7 +269,7 @@
     void dump(PrintStream ps) throws RepositoryException {
         ps.println("Session: " + (userId == null ? "unknown" : userId) + " (" + this + ")");
         ps.println();
-        itemMgr.dump(ps);
+        getItemManager().dump(ps);
     }
 
     //--------------------------------------------------------------< Session >
@@ -261,7 +307,7 @@
      * @see Session#getRootNode
      */
     public Node getRootNode() throws RepositoryException {
-        return (Node) itemMgr.getRootNode();
+        return getItemManager().getRootNode();
     }
 
     /**
@@ -269,7 +315,7 @@
      */
     public Node getNodeByUUID(String uuid) throws ItemNotFoundException, RepositoryException {
         try {
-            NodeImpl node = (NodeImpl) itemMgr.getItem(new NodeId(uuid));
+            NodeImpl node = (NodeImpl) getItemManager().getItem(new NodeId(uuid));
             if (node.isNodeType(NodeTypeRegistry.MIX_REFERENCEABLE)) {
                 return node;
             } else {
@@ -286,7 +332,7 @@
      */
     public Item getItem(String absPath) throws PathNotFoundException, RepositoryException {
         try {
-            return itemMgr.getItem(Path.create(absPath, getNamespaceResolver(), true));
+            return getItemManager().getItem(Path.create(absPath, getNamespaceResolver(), true));
         } catch (AccessDeniedException ade) {
             throw new PathNotFoundException(absPath);
         } catch (MalformedPathException mpe) {
@@ -301,7 +347,7 @@
      */
     public boolean itemExists(String absPath) {
         try {
-            itemMgr.getItem(Path.create(absPath, getNamespaceResolver(), true));
+            getItemManager().getItem(Path.create(absPath, getNamespaceResolver(), true));
             return true;
         } catch (RepositoryException re) {
             // fall through...
@@ -315,7 +361,7 @@
      * @see Session#save
      */
     public void save() throws AccessDeniedException, LockException, ConstraintViolationException, InvalidItemStateException, RepositoryException {
-        itemMgr.getRootNode().save();
+        getItemManager().getRootNode().save();
     }
 
     /**
@@ -324,17 +370,17 @@
     public void refresh(boolean keepChanges) throws RepositoryException {
         if (!keepChanges) {
             // optimization
-            itemStateMgr.disposeAllTransientItemStates();
+            getItemStateManager().disposeAllTransientItemStates();
             return;
         }
-        itemMgr.getRootNode().refresh(keepChanges);
+        getItemManager().getRootNode().refresh(keepChanges);
     }
 
     /**
      * @see Session#hasPendingChanges
      */
     public boolean hasPendingChanges() throws RepositoryException {
-        return itemStateMgr.hasAnyTransientItemStates();
+        return getItemStateManager().hasAnyTransientItemStates();
     }
 
     /**
@@ -355,12 +401,12 @@
             srcPath = Path.create(srcAbsPath, getNamespaceResolver(), true);
             srcName = srcPath.getNameElement();
             srcParentPath = srcPath.getAncestor(1);
-            ItemImpl item = itemMgr.getItem(srcPath);
+            ItemImpl item = getItemManager().getItem(srcPath);
             if (!item.isNode()) {
                 throw new PathNotFoundException(srcAbsPath);
             }
             targetNode = (NodeImpl) item;
-            srcParentNode = (NodeImpl) itemMgr.getItem(srcParentPath);
+            srcParentNode = (NodeImpl) getItemManager().getItem(srcParentPath);
         } catch (AccessDeniedException ade) {
             throw new PathNotFoundException(srcAbsPath);
         } catch (MalformedPathException mpe) {
@@ -377,7 +423,7 @@
             destPath = Path.create(destAbsPath, getNamespaceResolver(), true);
             destName = destPath.getNameElement();
             destParentPath = destPath.getAncestor(1);
-            destParentNode = (NodeImpl) itemMgr.getItem(destParentPath);
+            destParentNode = (NodeImpl) getItemManager().getItem(destParentPath);
         } catch (AccessDeniedException ade) {
             throw new PathNotFoundException(destAbsPath);
         } catch (MalformedPathException mpe) {
@@ -396,7 +442,7 @@
         // check for name collisions
 
         try {
-            ItemImpl item = itemMgr.getItem(destPath);
+            ItemImpl item = getItemManager().getItem(destPath);
             if (!item.isNode()) {
                 // there's already a property with that name
                 throw new ItemExistsException(item.safeGetJCRPath());
@@ -461,7 +507,7 @@
     public ContentHandler getImportContentHandler(String parentAbsPath) throws PathNotFoundException, RepositoryException {
         Item item = null;
         try {
-            item = itemMgr.getItem(Path.create(parentAbsPath, getNamespaceResolver(), true));
+            item = getItemManager().getItem(Path.create(parentAbsPath, getNamespaceResolver(), true));
         } catch (MalformedPathException mpe) {
             String msg = parentAbsPath + ": invalid path";
             log.error(msg, mpe);
@@ -521,7 +567,7 @@
      */
     public void logout() {
         // discard all transient changes
-        itemStateMgr.disposeAllTransientItemStates();
+        getItemStateManager().disposeAllTransientItemStates();
 
         // @todo invalidate session, release session-scoped locks, free resources, prepare to get gc'ed etc.
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java	Thu Oct 21 10:17:01 2004
@@ -15,9 +15,9 @@
  */
 package org.apache.jackrabbit.core;
 
-import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
-import org.apache.jackrabbit.core.jndi.RegistryHelper;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.jndi.RegistryHelper;
+import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
 import org.apache.log4j.Logger;
 import org.apache.log4j.PropertyConfigurator;
 
@@ -26,10 +26,13 @@
 import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.util.TraversingItemVisitor;
-import javax.naming.InitialContext;
 import javax.naming.Context;
+import javax.naming.InitialContext;
 import java.io.*;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Hashtable;
+import java.util.Properties;
 
 public class Test {
     private static Logger log = Logger.getLogger(Test.class);
@@ -58,8 +61,34 @@
 
         RegistryHelper.registerRepository(ctx, "repo", configFile, repHomeDir, true);
         Repository r = (Repository) ctx.lookup("repo");
-
         Session session = r.login(new SimpleCredentials("anonymous", "".toCharArray()), null);
+/*
+        XASession session = (XASession) r.login(new SimpleCredentials("anonymous", "".toCharArray()), null);
+        XAResource xares = session.getXAResource();
+
+        Xid xid = new Xid() {
+            public byte[] getBranchQualifier() {
+                return new byte[0];
+            }
+
+            public int getFormatId() {
+                return 0;
+            }
+
+            public byte[] getGlobalTransactionId() {
+                return new byte[0];
+            }
+        };
+
+        xares.start(xid, XAResource.TMNOFLAGS);
+
+        // ....
+
+        xares.end(xid, XAResource.TMSUCCESS);
+
+        xares.prepare(xid);
+        xares.commit(xid, false);
+*/
         Workspace wsp = session.getWorkspace();
 
         NodeTypeManager ntMgr = wsp.getNodeTypeManager();

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java	Thu Oct 21 10:17:01 2004
@@ -67,7 +67,7 @@
      * The persistent state mgr associated with the workspace represented by <i>this</i>
      * <code>Workspace</code> instance.
      */
-    protected final PersistentItemStateManager persistentStateMgr;
+    protected final PersistentItemStateProvider persistentStateMgr;
 
     /**
      * The reference mgr associated with the workspace represented by <i>this</i>
@@ -105,7 +105,7 @@
      * @param rep
      * @param session
      */
-    WorkspaceImpl(WorkspaceConfig wspConfig, PersistentItemStateManager persistentStateMgr,
+    WorkspaceImpl(WorkspaceConfig wspConfig, PersistentItemStateProvider persistentStateMgr,
                   ReferenceManager refMgr, RepositoryImpl rep, SessionImpl session) {
         this.wspConfig = wspConfig;
         this.rep = rep;
@@ -119,7 +119,7 @@
         return rep;
     }
 
-    public PersistentItemStateManager getPersistentStateManager() {
+    public PersistentItemStateProvider getPersistentStateManager() {
         return persistentStateMgr;
     }
 
@@ -137,7 +137,7 @@
     void dump(PrintStream ps) throws RepositoryException {
         ps.println("Workspace: " + wspConfig.getName() + " (" + this + ")");
         ps.println();
-        persistentStateMgr.dump(ps);
+        //persistentStateMgr.dump(ps);
     }
 
     /**
@@ -170,7 +170,7 @@
     protected static PersistentNodeState getNodeState(String nodePath,
                                                       NamespaceResolver nsResolver,
                                                       HierarchyManagerImpl hierMgr,
-                                                      PersistentItemStateManager stateMgr)
+                                                      PersistentItemStateProvider stateMgr)
             throws PathNotFoundException, RepositoryException {
         try {
             return getNodeState(Path.create(nodePath, nsResolver, true), hierMgr, stateMgr);
@@ -193,7 +193,7 @@
     protected static PersistentNodeState getParentNodeState(String path,
                                                             NamespaceResolver nsResolver,
                                                             HierarchyManagerImpl hierMgr,
-                                                            PersistentItemStateManager stateMgr)
+                                                            PersistentItemStateProvider stateMgr)
 
             throws PathNotFoundException, RepositoryException {
         try {
@@ -215,7 +215,7 @@
      */
     protected static PersistentNodeState getNodeState(Path nodePath,
                                                       HierarchyManagerImpl hierMgr,
-                                                      PersistentItemStateManager stateMgr)
+                                                      PersistentItemStateProvider stateMgr)
             throws PathNotFoundException, RepositoryException {
         try {
             ItemId id = hierMgr.resolvePath(nodePath);
@@ -240,7 +240,7 @@
      * @throws ItemStateException
      */
     protected static PersistentNodeState getNodeState(NodeId id,
-                                                      PersistentItemStateManager stateMgr)
+                                                      PersistentItemStateProvider stateMgr)
             throws NoSuchItemStateException, ItemStateException {
         return (PersistentNodeState) stateMgr.getItemState(id);
     }
@@ -262,7 +262,7 @@
                                        NodeTypeRegistry ntReg,
                                        AccessManagerImpl accessMgr,
                                        HierarchyManagerImpl hierMgr,
-                                       PersistentItemStateManager stateMgr)
+                                       PersistentItemStateProvider stateMgr)
             throws ConstraintViolationException, AccessDeniedException,
             PathNotFoundException, ItemExistsException, RepositoryException {
 
@@ -340,7 +340,7 @@
                                           NodeTypeRegistry ntReg,
                                           AccessManagerImpl accessMgr,
                                           HierarchyManagerImpl hierMgr,
-                                          PersistentItemStateManager stateMgr)
+                                          PersistentItemStateProvider stateMgr)
             throws ConstraintViolationException, AccessDeniedException,
             PathNotFoundException, RepositoryException {
 
@@ -433,8 +433,8 @@
                                                      String parentUUID,
                                                      NodeTypeRegistry ntReg,
                                                      HierarchyManagerImpl srcHierMgr,
-                                                     PersistentItemStateManager srcStateMgr,
-                                                     PersistentItemStateManager destStateMgr,
+                                                     PersistentItemStateProvider srcStateMgr,
+                                                     PersistentItemStateProvider destStateMgr,
                                                      boolean clone)
             throws RepositoryException {
         PersistentNodeState newState;
@@ -489,8 +489,8 @@
                                                              QName propName,
                                                              NodeTypeRegistry ntReg,
                                                              HierarchyManagerImpl srcHierMgr,
-                                                             PersistentItemStateManager srcStateMgr,
-                                                             PersistentItemStateManager destStateMgr)
+                                                             PersistentItemStateProvider srcStateMgr,
+                                                             PersistentItemStateProvider destStateMgr)
             throws RepositoryException {
         // @todo special handling required for properties with special semantics (e.g. those defined by mix:versionable, mix:lockable, et.al.)
         PersistentPropertyState newState;
@@ -526,10 +526,10 @@
     }
 
     private static void internalCopy(String srcAbsPath,
-                                     PersistentItemStateManager srcStateMgr,
+                                     PersistentItemStateProvider srcStateMgr,
                                      HierarchyManagerImpl srcHierMgr,
                                      String destAbsPath,
-                                     PersistentItemStateManager destStateMgr,
+                                     PersistentItemStateProvider destStateMgr,
                                      HierarchyManagerImpl destHierMgr,
                                      AccessManagerImpl accessMgr,
                                      NamespaceResolver nsResolver,
@@ -688,7 +688,7 @@
             ItemExistsException, RepositoryException {
         // clone (i.e. pull) subtree at srcAbsPath from srcWorkspace
         // to 'this' workspace at destAbsPath
-        PersistentItemStateManager srcStateMgr = rep.getWorkspaceStateManager(srcWorkspace);
+        PersistentItemStateProvider srcStateMgr = rep.getWorkspaceStateManager(srcWorkspace);
         // FIXME need to setup a hierarchy manager for source workspace
         HierarchyManagerImpl srcHierMgr = new HierarchyManagerImpl(rep.getRootNodeUUID(), srcStateMgr, session.getNamespaceResolver());
         // do cross-workspace copy

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/RepositoryConfig.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/RepositoryConfig.java	Thu Oct 21 10:17:01 2004
@@ -19,17 +19,16 @@
 import org.apache.log4j.Logger;
 import org.jdom.Document;
 import org.jdom.Element;
-import org.jdom.DocType;
-import org.jdom.output.XMLOutputter;
 import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
 import javax.jcr.RepositoryException;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.FileOutputStream;
 import java.util.Collection;
 import java.util.HashMap;
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/config.dtd
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/config.dtd	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/config/config.dtd	Thu Oct 21 10:17:01 2004
@@ -43,15 +43,15 @@
 -->
 <!ELEMENT FileSystem (param*)>
 <!ATTLIST FileSystem
-  class CDATA #REQUIRED>
+    class CDATA #REQUIRED>
 
 <!--
     generic parameter (name/value pair)
 -->
 <!ELEMENT param EMPTY>
 <!ATTLIST param
-  name CDATA #REQUIRED
-  value CDATA #REQUIRED>
+    name CDATA #REQUIRED
+    value CDATA #REQUIRED>
 
 <!--
     the Workspaces element specifies the workspaces root directory
@@ -63,8 +63,8 @@
 -->
 <!ELEMENT Workspaces EMPTY>
 <!ATTLIST Workspaces
-  rootPath CDATA #REQUIRED
-  defaultWorkspace CDATA #REQUIRED>
+    rootPath CDATA #REQUIRED
+    defaultWorkspace CDATA #REQUIRED>
 
 <!--
     the Workspace element serves as a workspace configuration template;
@@ -73,7 +73,7 @@
 -->
 <!ELEMENT Workspace (FileSystem,PersistenceManager,SearchIndex)>
 <!ATTLIST Workspace
-  name CDATA #REQUIRED>
+    name CDATA #REQUIRED>
 
 <!--
     the PersistenceManager element configures the persistence manager
@@ -82,7 +82,7 @@
 -->
 <!ELEMENT PersistenceManager (param*)>
 <!ATTLIST PersistenceManager
-  class CDATA #REQUIRED>
+    class CDATA #REQUIRED>
 
 <!--
     the SearchIndex element specifies the locaction of the search index
@@ -90,5 +90,5 @@
 -->
 <!ELEMENT SearchIndex EMPTY>
 <!ATTLIST SearchIndex
-  path CDATA #REQUIRED>
+    path CDATA #REQUIRED>
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/FileSystem.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/FileSystem.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/FileSystem.java	Thu Oct 21 10:17:01 2004
@@ -82,10 +82,11 @@
      *
      * @param filePath the path of the file.
      * @return an random access output stream for writing bytes to the file.
-     * @throws FileSystemException if the file could not be created or if the
-     *   output stream cannot be obtained.
-     * @exception UnsupportedOperationException if the implementation does
-     *   not support file access through a {@link RandomAccessOutputStream}.
+     * @throws FileSystemException           if the file could not be created or
+     *                                       if the output stream cannot be obtained.
+     * @throws UnsupportedOperationException if the implementation does
+     *                                       not support file access through a
+     *                                      {@link RandomAccessOutputStream}.
      */
     public RandomAccessOutputStream getRandomAccessOutputStream(String filePath)
             throws FileSystemException;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/RandomAccessOutputStream.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/RandomAccessOutputStream.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/RandomAccessOutputStream.java	Thu Oct 21 10:17:01 2004
@@ -15,8 +15,8 @@
  */
 package org.apache.jackrabbit.core.fs;
 
-import java.io.OutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 
 /**
  * Extends the regular <code>java.io.OutputStream</code> with a random
@@ -28,6 +28,7 @@
     /**
      * Sets the current position in the resource where the next write
      * will occur.
+     *
      * @param position the new position in the resource.
      * @throws IOException if an error occurs while seeking to the position.
      */

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/LocalFileSystem.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/LocalFileSystem.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/LocalFileSystem.java	Thu Oct 21 10:17:01 2004
@@ -20,15 +20,7 @@
 import org.apache.jackrabbit.core.fs.RandomAccessOutputStream;
 import org.apache.log4j.Logger;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.OutputStream;
-import java.io.FileOutputStream;
-import java.io.FileFilter;
-import java.io.RandomAccessFile;
+import java.io.*;
 
 /**
  * A <code>LocalFileSystem</code> ...

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/RAFOutputStream.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/RAFOutputStream.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/fs/local/RAFOutputStream.java	Thu Oct 21 10:17:01 2004
@@ -17,8 +17,8 @@
 
 import org.apache.jackrabbit.core.fs.RandomAccessOutputStream;
 
-import java.io.RandomAccessFile;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 
 /**
  * Implements a buffered output stream on a random access file.
@@ -58,7 +58,7 @@
     /**
      * Contructs a new output stream with the given buffer size.
      *
-     * @param raf the underlying <code>RandomAccessFile</code>.
+     * @param raf  the underlying <code>RandomAccessFile</code>.
      * @param size the size of the buffer.
      */
     public RAFOutputStream(RandomAccessFile raf, int size) throws IOException {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/BindableRepository.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/BindableRepository.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/BindableRepository.java	Thu Oct 21 10:17:01 2004
@@ -15,8 +15,8 @@
  */
 package org.apache.jackrabbit.core.jndi;
 
-import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
 
 import javax.jcr.*;
 import javax.naming.NamingException;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/RegistryHelper.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/RegistryHelper.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/RegistryHelper.java	Thu Oct 21 10:17:01 2004
@@ -15,9 +15,9 @@
  */
 package org.apache.jackrabbit.core.jndi;
 
-import javax.naming.NamingException;
-import javax.naming.Context;
 import javax.jcr.RepositoryException;
+import javax.naming.Context;
+import javax.naming.NamingException;
 
 /**
  * <code>RegistryHelper</code> ...
@@ -31,21 +31,20 @@
     }
 
     /**
-     *
-     * @param ctx context where the repository should be registered (i.e. bound)
-     * @param name the name to register the repository with
+     * @param ctx            context where the repository should be registered (i.e. bound)
+     * @param name           the name to register the repository with
      * @param configFilePath path to the configuration file of the repository
-     * @param repHomeDir repository home directory
-     * @param overwrite if <code>true</code>, any existing binding with the given
-     * name will be overwritten; otherwise a <code>NamingException</code> will
-     * be thrown if the name is already bound
+     * @param repHomeDir     repository home directory
+     * @param overwrite      if <code>true</code>, any existing binding with the given
+     *                       name will be overwritten; otherwise a <code>NamingException</code> will
+     *                       be thrown if the name is already bound
      * @throws NamingException
      * @throws RepositoryException
      */
     public static void registerRepository(Context ctx, String name,
-                                         String configFilePath,
-                                         String repHomeDir,
-                                         boolean overwrite)
+                                          String configFilePath,
+                                          String repHomeDir,
+                                          boolean overwrite)
             throws NamingException, RepositoryException {
         Object obj = BindableRepository.create(configFilePath, repHomeDir);
         if (overwrite) {
@@ -56,8 +55,7 @@
     }
 
     /**
-     *
-     * @param ctx context where the repository should be unregistered (i.e. unbound)
+     * @param ctx  context where the repository should be unregistered (i.e. unbound)
      * @param name the name of the repository to unregister
      * @throws NamingException
      */

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/provider/DummyInitialContextFactory.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/provider/DummyInitialContextFactory.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/jndi/provider/DummyInitialContextFactory.java	Thu Oct 21 10:17:01 2004
@@ -15,9 +15,9 @@
  */
 package org.apache.jackrabbit.core.jndi.provider;
 
-import javax.naming.spi.InitialContextFactory;
 import javax.naming.Context;
 import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
 import java.util.Hashtable;
 
 /**

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/observation/EventStateCollection.java	Thu Oct 21 10:17:01 2004
@@ -22,9 +22,9 @@
 
 import javax.jcr.RepositoryException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Collection;
 
 /**
  * The <code>EventStateCollection</code> class implements how {@link EventState}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/NamespaceMappings.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/NamespaceMappings.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/NamespaceMappings.java	Thu Oct 21 10:17:01 2004
@@ -19,19 +19,19 @@
 import org.apache.jackrabbit.core.NamespaceResolver;
 import org.apache.jackrabbit.core.NoPrefixDeclaredException;
 import org.apache.jackrabbit.core.Path;
-import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.log4j.Logger;
 
 import javax.jcr.NamespaceException;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Properties;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.BufferedOutputStream;
 
 /**
  * The class <code>NamespaceMappings</code> implements a {@link

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/jcrql/JCRQLParserTokenManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/jcrql/JCRQLParserTokenManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/jcrql/JCRQLParserTokenManager.java	Thu Oct 21 10:17:01 2004
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.core.search.jcrql;
 
 
-
 public class JCRQLParserTokenManager implements JCRQLParserConstants {
     public java.io.PrintStream debugStream = System.out;
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemDirectory.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemDirectory.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemDirectory.java	Thu Oct 21 10:17:01 2004
@@ -15,15 +15,15 @@
  */
 package org.apache.jackrabbit.core.search.lucene;
 
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.OutputStream;
-import org.apache.lucene.store.InputStream;
-import org.apache.lucene.store.Lock;
+import org.apache.commons.collections.ReferenceMap;
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.fs.FileSystemException;
 import org.apache.jackrabbit.core.fs.FileSystemResource;
-import org.apache.commons.collections.ReferenceMap;
 import org.apache.log4j.Logger;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.InputStream;
+import org.apache.lucene.store.Lock;
+import org.apache.lucene.store.OutputStream;
 
 import java.io.IOException;
 import java.util.Map;
@@ -62,21 +62,21 @@
      * <code>FileSystem</code> will return the previously returned
      * <code>FileSystemDirectory</code> instance.
      *
-     * @param fs the <code>FileSystem</code> where this <code>Directory</code>
-     *   is based on.
+     * @param fs     the <code>FileSystem</code> where this <code>Directory</code>
+     *               is based on.
      * @param create if <code>true</code>, an existing index in this
-     *   <code>Directory</code> is deleted.
+     *               <code>Directory</code> is deleted.
      * @return the <code>FileSystemDirectory</code> instance for the
-     *   <code>FileSystem</code> <code>fs</code>.
+     *         <code>FileSystem</code> <code>fs</code>.
      * @throws IOException if the <code>FileSystemDirectory</code> cannot
-     *   be created.
+     *                     be created.
      */
     static FileSystemDirectory getDirectory(FileSystem fs,
                                             boolean create)
             throws IOException {
 
         synchronized (directories) {
-            FileSystemDirectory dir = (FileSystemDirectory)directories.get(fs);
+            FileSystemDirectory dir = (FileSystemDirectory) directories.get(fs);
             if (dir == null) {
                 dir = new FileSystemDirectory(fs, create);
                 directories.put(fs, dir);
@@ -89,12 +89,12 @@
      * Creates a new <code>FileSystemDirectory</code> based on
      * <code>FileSystem</code> <code>fs</code>.
      *
-     * @param fs the <code>FileSystem</code> where this <code>Directory</code>
-     *   is based on.
+     * @param fs     the <code>FileSystem</code> where this <code>Directory</code>
+     *               is based on.
      * @param create if <code>true</code>, an existing index in this
-     *   <code>Directory</code> is deleted.
+     *               <code>Directory</code> is deleted.
      * @throws IOException if the <code>FileSystemDirectory</code> cannot
-     *   be created.
+     *                     be created.
      */
     private FileSystemDirectory(FileSystem fs,
                                 boolean create) throws IOException {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemInputStream.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemInputStream.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemInputStream.java	Thu Oct 21 10:17:01 2004
@@ -15,9 +15,9 @@
  */
 package org.apache.jackrabbit.core.search.lucene;
 
-import org.apache.lucene.store.InputStream;
-import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+import org.apache.lucene.store.InputStream;
 
 import java.io.IOException;
 
@@ -72,7 +72,7 @@
     }
 
     public Object clone() {
-        FileSystemInputStream clone = (FileSystemInputStream)super.clone();
+        FileSystemInputStream clone = (FileSystemInputStream) super.clone();
         // decouple from this
         clone.in = null;
         clone.position = 0;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemOutputStream.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemOutputStream.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FileSystemOutputStream.java	Thu Oct 21 10:17:01 2004
@@ -15,10 +15,10 @@
  */
 package org.apache.jackrabbit.core.search.lucene;
 
-import org.apache.lucene.store.OutputStream;
-import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.RandomAccessOutputStream;
+import org.apache.lucene.store.OutputStream;
 
 import java.io.IOException;
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java	Thu Oct 21 10:17:01 2004
@@ -16,8 +16,8 @@
 package org.apache.jackrabbit.core.search.lucene;
 
 import org.apache.jackrabbit.core.MalformedPathException;
-import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.Path;
+import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.search.*;
 import org.apache.log4j.Logger;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java	Thu Oct 21 10:17:01 2004
@@ -15,27 +15,19 @@
  */
 package org.apache.jackrabbit.core.search.lucene;
 
+import org.apache.jackrabbit.core.*;
 import org.apache.jackrabbit.core.search.NamespaceMappings;
+import org.apache.jackrabbit.core.state.*;
 import org.apache.jackrabbit.core.util.uuid.UUID;
-import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.jackrabbit.core.state.ItemStateProvider;
-import org.apache.jackrabbit.core.state.PropertyState;
-import org.apache.jackrabbit.core.state.NoSuchItemStateException;
-import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.PropertyId;
-import org.apache.jackrabbit.core.InternalValue;
-import org.apache.jackrabbit.core.QName;
-import org.apache.jackrabbit.core.Path;
-import org.apache.jackrabbit.core.NoPrefixDeclaredException;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 
-import javax.jcr.PropertyType;
 import javax.jcr.NamespaceException;
 import javax.jcr.PathNotFoundException;
-import java.util.List;
-import java.util.Iterator;
+import javax.jcr.PropertyType;
 import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  *

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PathQuery.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PathQuery.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PathQuery.java	Thu Oct 21 10:17:01 2004
@@ -15,13 +15,13 @@
  */
 package org.apache.jackrabbit.core.search.lucene;
 
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.index.Term;
-import org.apache.jackrabbit.core.search.PathQueryNode;
-import org.apache.jackrabbit.core.Path;
 import org.apache.jackrabbit.core.NamespaceResolver;
 import org.apache.jackrabbit.core.NoPrefixDeclaredException;
+import org.apache.jackrabbit.core.Path;
+import org.apache.jackrabbit.core.search.PathQueryNode;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.TermQuery;
 
 /**
  * Implements a query for a path with a match type.

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java	Thu Oct 21 10:17:01 2004
@@ -15,10 +15,10 @@
  */
 package org.apache.jackrabbit.core.search.lucene;
 
+import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.store.Directory;
-import org.apache.jackrabbit.core.fs.FileSystem;
 
 import java.io.IOException;
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java	Thu Oct 21 10:17:01 2004
@@ -16,15 +16,15 @@
 package org.apache.jackrabbit.core.search.lucene;
 
 import EDU.oswego.cs.dl.util.concurrent.FIFOReadWriteLock;
+import org.apache.jackrabbit.core.fs.BasedFileSystem;
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.FileSystemException;
 import org.apache.log4j.Logger;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Hits;
 import org.apache.lucene.search.Query;
-import org.apache.jackrabbit.core.fs.FileSystem;
-import org.apache.jackrabbit.core.fs.FileSystemException;
-import org.apache.jackrabbit.core.fs.BasedFileSystem;
 
 import java.io.IOException;
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java	Thu Oct 21 10:17:01 2004
@@ -77,7 +77,7 @@
 
     protected String baseVersionID;
 
-    protected final ItemId id;
+    protected ItemId id;
 
     /**
      * Listeners (soft references)
@@ -85,7 +85,7 @@
     protected final transient Map listeners = Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT));
 
     // the backing persistent item state (may be null)
-    private transient ItemState overlayedState;
+    protected transient ItemState overlayedState;
 
     /**
      * Protected constructor
@@ -121,6 +121,7 @@
      */
     protected ItemState(ItemState overlayedState, int initialStatus) {
         switch (initialStatus) {
+            case STATUS_EXISTING:
             case STATUS_EXISTING_MODIFIED:
             case STATUS_EXISTING_REMOVED:
                 status = initialStatus;
@@ -130,27 +131,43 @@
                 log.error(msg);
                 throw new IllegalArgumentException(msg);
         }
-        parentUUID = overlayedState.parentUUID;
-        baseVersionID = overlayedState.getBaseVersionID();
-        lastModified = overlayedState.getLastModified();
-        id = overlayedState.getId();
         this.overlayedState = overlayedState;
         // add this transient state as a listener on the overlayed state
         this.overlayedState.addListener(this);
     }
 
     /**
+     * Copy state from another state. Copies over all state variables from
+     * another state.
+     *
+     * @param state state to copy
+     */
+    protected void copy(ItemState state) {
+        parentUUID = state.parentUUID;
+        baseVersionID = state.getBaseVersionID();
+        lastModified = state.getLastModified();
+        id = state.getId();
+    }
+
+    /**
      * Called by <code>TransientItemStateManager</code> when this item state
      * is disposed.
      */
     void onDisposed() {
         // prepare this instance so it can be gc'ed
         listeners.clear();
+        disconnect();
+        status = STATUS_UNDEFINED;
+    }
+
+    /**
+     * Disconnect this state from the underlying overlayed state.
+     */
+    protected void disconnect() {
         if (overlayedState != null) {
             overlayedState.removeListener(this);
             overlayedState = null;
         }
-        status = STATUS_UNDEFINED;
     }
 
     /**
@@ -193,9 +210,9 @@
 
     /**
      * Notify the listeners that the persistent state this object is
-     * representing has been changed.
+     * representing has been updated.
      */
-    protected void notifyStateModified() {
+    protected void notifyStateUpdated() {
         // copy listeners to array to avoid ConcurrentModificationException
         ItemStateListener[] la = new ItemStateListener[listeners.size()];
         Iterator iter = listeners.values().iterator();

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java	Thu Oct 21 10:17:01 2004
@@ -28,7 +28,7 @@
  * An <code>ItemStateCache</code> maintains a cache of <code>ItemState</code>
  * instances.
  */
-abstract class ItemStateCache {
+public abstract class ItemStateCache {
     private static Logger log = Logger.getLogger(ItemStateCache.class);
 
     /**

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/NodeState.java	Thu Oct 21 10:17:01 2004
@@ -61,13 +61,8 @@
      */
     NodeState(NodeState overlayedState, int initialStatus) {
         super(overlayedState, initialStatus);
-        nodeTypeName = overlayedState.getNodeTypeName();
-        mixinTypeNames.addAll(overlayedState.getMixinTypeNames());
-        defId = overlayedState.getDefinitionId();
-        uuid = overlayedState.getUUID();
-        parentUUIDs.addAll(overlayedState.getParentUUIDs());
-        propertyEntries.addAll(overlayedState.getPropertyEntries());
-        childNodeEntries.addAll(overlayedState.getChildNodeEntries());
+
+        copy(overlayedState);
     }
 
     /**
@@ -87,6 +82,26 @@
         this.uuid = uuid;
     }
 
+    /**
+     * @see ItemState#copy
+     */
+    protected void copy(ItemState state) {
+        super.copy(state);
+
+        NodeState nodeState = (NodeState) state;
+        nodeTypeName = nodeState.getNodeTypeName();
+        mixinTypeNames.clear();
+        mixinTypeNames.addAll(nodeState.getMixinTypeNames());
+        defId = nodeState.getDefinitionId();
+        uuid = nodeState.getUUID();
+        parentUUIDs.clear();
+        parentUUIDs.addAll(nodeState.getParentUUIDs());
+        propertyEntries.clear();
+        propertyEntries.addAll(nodeState.getPropertyEntries());
+        childNodeEntries.removeAll();
+        childNodeEntries.addAll(nodeState.getChildNodeEntries());
+    }
+
     //-------------------------------------------------------< public methods >
     /**
      * Determines if this item state represents a node.
@@ -601,15 +616,20 @@
     }
 
     //--------------------------------------------------< ItemState overrides >
+
     /**
-     * @see ItemState#setParentUUID
+     * Sets the UUID of the parent <code>NodeState</code>.
+     *
+     * @param parentUUID the parent <code>NodeState</code>'s UUID or <code>null</code>
+     *                   if either this item state should represent the root node or this item state
+     *                   should be 'free floating', i.e. detached from the repository's hierarchy.
      */
     public synchronized void setParentUUID(String parentUUID) {
         // @todo is this correct?
-        if (!parentUUIDs.contains(parentUUID) && parentUUID != null) {
+        if (parentUUID != null && !parentUUIDs.contains(parentUUID)) {
             parentUUIDs.add(parentUUID);
         }
-        super.setParentUUID(parentUUID);
+        this.parentUUID = parentUUID;
     }
 
     //-------------------------------------------------< Serializable support >

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistenceManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistenceManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistenceManager.java	Thu Oct 21 10:17:01 2004
@@ -15,7 +15,6 @@
  */
 package org.apache.jackrabbit.core.state;
 
-import org.apache.jackrabbit.core.QName;
 import org.apache.jackrabbit.core.config.WorkspaceConfig;
 
 /**
@@ -30,50 +29,28 @@
     public void init(WorkspaceConfig wspConfig) throws Exception;
 
     /**
-     * @param uuid
-     * @return
-     * @throws NoSuchItemStateException
-     * @throws ItemStateException
-     */
-    public PersistentNodeState loadNodeState(String uuid)
-            throws NoSuchItemStateException, ItemStateException;
-
-    /**
      * @param state
-     * @throws ItemStateException
-     */
-    public void reload(PersistentNodeState state) throws ItemStateException;
-
-    /**
-     * @param parentUUID
-     * @param propName
-     * @return
      * @throws NoSuchItemStateException
      * @throws ItemStateException
      */
-    public PersistentPropertyState loadPropertyState(String parentUUID, QName propName)
+    public void load(PersistentNodeState state)
             throws NoSuchItemStateException, ItemStateException;
 
     /**
      * @param state
-     * @throws ItemStateException
-     */
-    public void reload(PersistentPropertyState state) throws ItemStateException;
-
-    /**
-     * @param uuid
-     * @return
      * @throws NoSuchItemStateException
      * @throws ItemStateException
      */
-    public NodeReferences loadNodeReferences(String uuid)
+    public void load(PersistentPropertyState state)
             throws NoSuchItemStateException, ItemStateException;
 
     /**
      * @param refs
+     * @throws NoSuchItemStateException
      * @throws ItemStateException
      */
-    public void reload(NodeReferences refs) throws ItemStateException;
+    public void load(NodeReferences refs)
+            throws NoSuchItemStateException, ItemStateException;
 
     /**
      * @param state
@@ -110,25 +87,4 @@
      * @throws ItemStateException
      */
     public void destroy(NodeReferences refs) throws ItemStateException;
-
-    //------------------------------------------------------< factory methods >
-    /**
-     * @param uuid
-     * @param nodeTypeName
-     * @return
-     */
-    public PersistentNodeState createNodeStateInstance(String uuid, QName nodeTypeName);
-
-    /**
-     * @param name
-     * @param parentUUID
-     * @return
-     */
-    public PersistentPropertyState createPropertyStateInstance(QName name, String parentUUID);
-
-    /**
-     * @param uuid
-     * @return
-     */
-    public NodeReferences createNodeReferencesInstance(String uuid);
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java	Thu Oct 21 10:17:01 2004
@@ -30,11 +30,12 @@
  * <code>PersistentItemStateManager</code> ...
  */
 public class PersistentItemStateManager extends ItemStateCache
-        implements ItemStateProvider, ItemStateListener {
+        implements PersistentItemStateProvider, ItemStateListener {
 
     private static Logger log = Logger.getLogger(PersistentItemStateManager.class);
 
-    private final PersistenceManager persistMgr;
+    protected final PersistenceManager persistMgr;
+
 
     // keep a hard reference to the root node state
     private PersistentNodeState root;
@@ -143,63 +144,46 @@
         ps.println(id + " (" + state + ")");
     }
 
-    //----------------------------------------------------< ItemStateProvider >
     /**
-     * @see ItemStateProvider#getItemState(ItemId)
+     * Create a <code>PersistentNodeState</code> instance. May be overridden by
+     * subclasses.
+     *
+     * @param uuid UUID
+     * @return persistent node state instance
      */
-    public ItemState getItemState(ItemId id)
-            throws NoSuchItemStateException, ItemStateException {
-        if (id.denotesNode()) {
-            return getNodeState((NodeId) id);
-        } else {
-            return getPropertyState((PropertyId) id);
-        }
+    protected PersistentNodeState createNodeStateInstance(String uuid) {
+        return new PersistentNodeState(uuid, persistMgr);
     }
 
     /**
-     * @see ItemStateProvider#hasItemState(ItemId)
-     */
-    public boolean hasItemState(ItemId id) {
-        try {
-            getItemState(id);
-            return true;
-        } catch (ItemStateException ise) {
-            return false;
-        }
-    }
-
-    /**
-     * @see ItemStateProvider#getItemStateInAttic(ItemId)
+     * Create a <code>PersistentPropertyState</code> instance. May be overridden
+     * by subclasses.
+     *
+     * @param name       qualified name
+     * @param parentUUID parent UUID
+     * @return persistent property state instance
      */
-    public ItemState getItemStateInAttic(ItemId id)
-            throws NoSuchItemStateException, ItemStateException {
-        // n/a
-        throw new NoSuchItemStateException(id.toString());
-    }
+    protected PersistentPropertyState createPropertyStateInstance(QName name,
+                                                                  String parentUUID) {
 
-    /**
-     * @see ItemStateProvider#hasItemStateInAttic(ItemId)
-     */
-    public boolean hasItemStateInAttic(ItemId id) {
-        // n/a
-        return false;
+        return new PersistentPropertyState(name, parentUUID, persistMgr);
     }
 
-    //---------------< methods for listing and retrieving ItemState instances >
     /**
      * @param id
      * @return
      * @throws NoSuchItemStateException
      * @throws ItemStateException
      */
-    synchronized PersistentNodeState getNodeState(NodeId id)
+    protected PersistentNodeState getNodeState(NodeId id)
             throws NoSuchItemStateException, ItemStateException {
         // check cache
         if (isCached(id)) {
             return (PersistentNodeState) retrieve(id);
         }
         // load from persisted state
-        PersistentNodeState state = persistMgr.loadNodeState(id.getUUID());
+        PersistentNodeState state = createNodeStateInstance(id.getUUID());
+        persistMgr.load(state);
         state.setStatus(ItemState.STATUS_EXISTING);
         // put it in cache
         cache(state);
@@ -214,14 +198,15 @@
      * @throws NoSuchItemStateException
      * @throws ItemStateException
      */
-    synchronized PersistentPropertyState getPropertyState(PropertyId id)
+    protected PersistentPropertyState getPropertyState(PropertyId id)
             throws NoSuchItemStateException, ItemStateException {
         // check cache
         if (isCached(id)) {
             return (PersistentPropertyState) retrieve(id);
         }
         // load from persisted state
-        PersistentPropertyState state = persistMgr.loadPropertyState(id.getParentUUID(), id.getName());
+        PersistentPropertyState state = createPropertyStateInstance(id.getName(), id.getParentUUID());
+        persistMgr.load(state);
         state.setStatus(ItemState.STATUS_EXISTING);
         // put it in cache
         cache(state);
@@ -230,17 +215,51 @@
         return state;
     }
 
-    //-------------------------< methods for creating new ItemState instances >
+    //----------------------------------------------------< ItemStateProvider >
     /**
-     * Creates a <code>PersistentNodeState</code> instance representing new,
-     * i.e. not yet existing state. Call <code>{@link PersistentNodeState#store()}</code>
-     * on the returned object to make it persistent.
-     *
-     * @param uuid
-     * @param nodeTypeName
-     * @param parentUUID
-     * @return
-     * @throws ItemStateException
+     * @see ItemStateProvider#getItemState(ItemId)
+     */
+    public synchronized ItemState getItemState(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+        if (id.denotesNode()) {
+            return getNodeState((NodeId) id);
+        } else {
+            return getPropertyState((PropertyId) id);
+        }
+    }
+
+    /**
+     * @see ItemStateProvider#hasItemState(ItemId)
+     */
+    public boolean hasItemState(ItemId id) {
+        try {
+            getItemState(id);
+            return true;
+        } catch (ItemStateException ise) {
+            return false;
+        }
+    }
+
+    /**
+     * @see ItemStateProvider#getItemStateInAttic(ItemId)
+     */
+    public ItemState getItemStateInAttic(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+        // n/a
+        throw new NoSuchItemStateException(id.toString());
+    }
+
+    /**
+     * @see ItemStateProvider#hasItemStateInAttic(ItemId)
+     */
+    public boolean hasItemStateInAttic(ItemId id) {
+        // n/a
+        return false;
+    }
+
+    //------------------------------------------< PersistentItemStateProvider >
+    /**
+     * @see PersistentItemStateProvider#createNodeState
      */
     public synchronized PersistentNodeState createNodeState(String uuid, QName nodeTypeName, String parentUUID)
             throws ItemStateException {
@@ -252,7 +271,8 @@
             throw new ItemStateException(msg);
         }
 
-        PersistentNodeState state = persistMgr.createNodeStateInstance(uuid, nodeTypeName);
+        PersistentNodeState state = createNodeStateInstance(uuid);
+        state.setNodeTypeName(nodeTypeName);
         state.setParentUUID(parentUUID);
         // put it in cache
         cache(state);
@@ -262,14 +282,7 @@
     }
 
     /**
-     * Creates a <code>PersistentPropertyState</code> instance representing new,
-     * i.e. not yet existing state. Call <code>{@link PersistentPropertyState#store()}</code>
-     * on the returned object to make it persistent.
-     *
-     * @param parentUUID
-     * @param propName
-     * @return
-     * @throws ItemStateException
+     * @see PersistentItemStateProvider#createPropertyState
      */
     public synchronized PersistentPropertyState createPropertyState(String parentUUID, QName propName)
             throws ItemStateException {
@@ -281,7 +294,7 @@
             throw new ItemStateException(msg);
         }
 
-        PersistentPropertyState state = persistMgr.createPropertyStateInstance(propName, parentUUID);
+        PersistentPropertyState state = createPropertyStateInstance(propName, parentUUID);
         // put it in cache
         cache(state);
         // register as listener

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateProvider.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateProvider.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.QName;
+
+/**
+ * Extends the {@link ItemStateProvider} interface by providing methods that
+ * deal with {@link PersistentNodeState}s and {@link PersistentPropertyState}s.
+ */
+public interface PersistentItemStateProvider extends ItemStateProvider {
+
+    /**
+     * Creates a {@link PersistentNodeState} instance representing new,
+     * i.e. not yet existing state. Call {@link PersistentNodeState#store()}
+     * on the returned object to make it persistent.
+     *
+     * @param uuid         node UUID
+     * @param nodeTypeName qualified node type name
+     * @param parentUUID   parent node's UUID
+     * @return a persistent node state
+     * @throws ItemStateException if an error occurs
+     */
+    public PersistentNodeState createNodeState(String uuid, QName nodeTypeName,
+                                               String parentUUID)
+            throws ItemStateException;
+
+    /**
+     * Creates a {@link PersistentPropertyState} instance representing new,
+     * i.e. not yet existing state. Call {@link PersistentPropertyState#store()}
+     * on the returned object to make it persistent.
+     *
+     * @param parentUUID parent node UUID
+     * @param propName   qualified property name
+     * @return a persistent property state
+     * @throws ItemStateException if an error occurs
+     */
+    public PersistentPropertyState createPropertyState(String parentUUID,
+                                                       QName propName)
+            throws ItemStateException;
+}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentNodeState.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentNodeState.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentNodeState.java	Thu Oct 21 10:17:01 2004
@@ -25,40 +25,96 @@
  * <code>PersistentNodeState</code> represents the persistent state of a
  * <code>Node</code>.
  */
-public abstract class PersistentNodeState extends NodeState implements PersistableItemState {
+public class PersistentNodeState extends NodeState implements PersistableItemState {
 
     static final long serialVersionUID = -371249062564922125L;
 
     protected final transient PersistenceManager persistMgr;
 
     /**
-     * Package private constructor
+     * Public constructor
      *
-     * @param uuid         the UUID of the this node
-     * @param nodeTypeName node type of this node
-     * @param parentUUID   the UUID of the parent node
-     * @param persistMgr   the persistence manager
+     * @param uuid       the UUID of the this node
+     * @param persistMgr the persistence manager
      */
-    protected PersistentNodeState(String uuid, QName nodeTypeName, String parentUUID, PersistenceManager persistMgr) {
-        super(uuid, nodeTypeName, parentUUID, STATUS_NEW);
+    public PersistentNodeState(String uuid, PersistenceManager persistMgr) {
+        super(uuid, null, null, STATUS_NEW);
         this.persistMgr = persistMgr;
     }
 
+    /**
+     * Constructor used for overlay mechanism.
+     *
+     * @param overlayedState other node state to overlay
+     * @param initialStatus  initial status
+     * @param persistMgr     persistence manager
+     */
+    protected PersistentNodeState(PersistentNodeState overlayedState,
+                                  int initialStatus,
+                                  PersistenceManager persistMgr) {
+
+        super(overlayedState, initialStatus);
+
+        this.persistMgr = persistMgr;
+    }
+
+    /**
+     * Set the node type name. Needed for deserialization and should therefore
+     * not change the internal status.
+     *
+     * @param nodeTypeName node type name
+     */
+    public void setNodeTypeName(QName nodeTypeName) {
+        this.nodeTypeName = nodeTypeName;
+    }
+
     //-------------------------------------------------< PersistableItemState >
+
     /**
      * @see PersistableItemState#reload
      */
-    public abstract void reload() throws ItemStateException;
+    public synchronized void reload() throws ItemStateException {
+        status = STATUS_UNDEFINED;
+        getPersistenceManager().load(this);
+        // reset status
+        status = STATUS_EXISTING;
+    }
 
     /**
      * @see PersistableItemState#store
      */
-    public abstract void store() throws ItemStateException;
+    public synchronized void store() throws ItemStateException {
+        getPersistenceManager().store(this);
+        // notify listeners
+        if (status == STATUS_NEW) {
+            notifyStateCreated();
+        } else {
+            notifyStateUpdated();
+        }
+        // reset status
+        status = STATUS_EXISTING;
+    }
 
     /**
      * @see PersistableItemState#destroy
      */
-    public abstract void destroy() throws ItemStateException;
+    public synchronized void destroy() throws ItemStateException {
+        getPersistenceManager().destroy(this);
+        // notify listeners
+        notifyStateDestroyed();
+        // reset status
+        status = STATUS_UNDEFINED;
+    }
+
+    /**
+     * Return the persistence manager to use for loading and storing data. May
+     * be overridden by subclasses.
+     *
+     * @return persistence manager
+     */
+    protected PersistenceManager getPersistenceManager() {
+        return persistMgr;
+    }
 
     //-------------------------------------------------< Serializable support >
     private void writeObject(ObjectOutputStream out) throws IOException {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentPropertyState.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentPropertyState.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentPropertyState.java	Thu Oct 21 10:17:01 2004
@@ -25,39 +25,87 @@
  * <code>PersistentPropertyState</code> represents the persistent state of a
  * <code>Property</code>.
  */
-public abstract class PersistentPropertyState extends PropertyState implements PersistableItemState {
+public class PersistentPropertyState extends PropertyState implements PersistableItemState {
 
     static final long serialVersionUID = -8919019313955817383L;
 
     protected final transient PersistenceManager persistMgr;
 
     /**
-     * Package private constructor
+     * Public constructor
      *
      * @param name       name of the property
      * @param parentUUID the uuid of the parent node
      * @param persistMgr the persistence manager
      */
-    protected PersistentPropertyState(QName name, String parentUUID, PersistenceManager persistMgr) {
+    public PersistentPropertyState(QName name, String parentUUID, PersistenceManager persistMgr) {
         super(name, parentUUID, STATUS_NEW);
         this.persistMgr = persistMgr;
     }
 
+    /**
+     * Constructor used for overlay mechanism.
+     *
+     * @param overlayedState other node state to overlay
+     * @param initialStatus  initial status
+     * @param persistMgr     persistence manager
+     */
+    protected PersistentPropertyState(PersistentPropertyState overlayedState,
+                                      int initialStatus,
+                                      PersistenceManager persistMgr) {
+
+        super(overlayedState, initialStatus);
+
+        this.persistMgr = persistMgr;
+    }
+
     //-------------------------------------------------< PersistableItemState >
+
     /**
      * @see PersistableItemState#reload
      */
-    public abstract void reload() throws ItemStateException;
+    public synchronized void reload() throws ItemStateException {
+        status = STATUS_UNDEFINED;
+        getPersistenceManager().load(this);
+        // reset status
+        status = STATUS_EXISTING;
+    }
 
     /**
      * @see PersistableItemState#store
      */
-    public abstract void store() throws ItemStateException;
+    public synchronized void store() throws ItemStateException {
+        getPersistenceManager().store(this);
+        // notify listeners
+        if (status == STATUS_NEW) {
+            notifyStateCreated();
+        } else {
+            notifyStateUpdated();
+        }
+        // reset status
+        status = STATUS_EXISTING;
+    }
 
     /**
      * @see PersistableItemState#destroy
      */
-    public abstract void destroy() throws ItemStateException;
+    public synchronized void destroy() throws ItemStateException {
+        getPersistenceManager().destroy(this);
+        // notify listeners
+        notifyStateDestroyed();
+        // reset status
+        status = STATUS_UNDEFINED;
+    }
+
+    /**
+     * Return the persistence manager to use for loading and storing data. May
+     * be overridden by subclasses.
+     *
+     * @return persistence manager
+     */
+    protected PersistenceManager getPersistenceManager() {
+        return persistMgr;
+    }
 
     //-------------------------------------------------< Serializable support >
     private void writeObject(ObjectOutputStream out) throws IOException {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PropertyState.java	Thu Oct 21 10:17:01 2004
@@ -47,10 +47,8 @@
      */
     PropertyState(PropertyState overlayedState, int initialStatus) {
         super(overlayedState, initialStatus);
-        name = overlayedState.getName();
-        type = overlayedState.getType();
-        defId = overlayedState.getDefinitionId();
-        values = overlayedState.getValues();
+
+        copy(overlayedState);
     }
 
     /**
@@ -66,6 +64,19 @@
         type = PropertyType.UNDEFINED;
         values = new InternalValue[0];
     }
+
+    /**
+     * @see ItemState#copy
+     */
+    protected void copy(ItemState state) {
+        super.copy(state);
+
+        PropertyState propState = (PropertyState) state;
+        name = propState.getName();
+        type = propState.getType();
+        defId = propState.getDefinitionId();
+        values = propState.getValues();
+    }    
 
     //-------------------------------------------------------< public methods >
     /**

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ReferenceManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ReferenceManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ReferenceManager.java	Thu Oct 21 10:17:01 2004
@@ -58,10 +58,11 @@
         NodeReferences refs;
         try {
             // load persisted references
-            refs = persistMgr.loadNodeReferences(targetId.getUUID());
+            refs = new NodeReferences(new NodeId(targetId.getUUID()));
+            persistMgr.load(refs);
         } catch (NoSuchItemStateException nsise) {
             // does not exist, create new
-            refs = persistMgr.createNodeReferencesInstance(targetId.getUUID());
+            refs = new NodeReferences(new NodeId(targetId.getUUID()));
         } catch (ItemStateException ise) {
             String msg = "error while loading references";
             log.error(msg, ise);

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java	Thu Oct 21 10:17:01 2004
@@ -1,6 +1,6 @@
 /*
  * Copyright 2004 The Apache Software Foundation.
-   *
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -29,9 +29,8 @@
 
     private static Logger log = Logger.getLogger(SessionItemStateManager.class);
 
-    private final PersistentItemStateManager persistentStateMgr;
+    private final PersistentItemStateProvider persistentStateMgr;
     private final TransientItemStateManager transientStateMgr;
-
     private final HierarchyManager hierMgr;
 
     /**
@@ -41,7 +40,7 @@
      * @param persistentStateMgr
      * @param nsResolver
      */
-    public SessionItemStateManager(String rootNodeUUID, PersistentItemStateManager persistentStateMgr, NamespaceResolver nsResolver) {
+    public SessionItemStateManager(String rootNodeUUID, PersistentItemStateProvider persistentStateMgr, NamespaceResolver nsResolver) {
         this.persistentStateMgr = persistentStateMgr;
         // create transient item state manager
         transientStateMgr = new TransientItemStateManager();
@@ -152,7 +151,7 @@
     public void dump(PrintStream ps) {
         ps.println("SessionItemStateManager (" + this + ")");
         ps.println();
-        persistentStateMgr.dump(ps);
+        //persistentStateMgr.dump(ps);
         ps.println();
         transientStateMgr.dump(ps);
         ps.println();
@@ -281,7 +280,7 @@
                  * is a descendant of any of the specified parentId's paths
                  */
                 for (int i = 0; i < paths.length; i++) {
-                    Path p0 = paths[i];	// path to transient state
+                    Path p0 = paths[i]; // path to transient state
                     // walk through array of the specified parentId's paths
                     for (int j = 0; j < parentPaths.length; j++) {
                         Path p1 = parentPaths[j]; // path to specified parentId
@@ -316,7 +315,7 @@
                          */
                         Path[] pa = hierMgr.getAllPaths(new NodeId((String) iterUUIDs.next()));
                         for (int k = 0; k < pa.length; k++) {
-                            Path p0 = pa[k];	// path to removed parent
+                            Path p0 = pa[k];   // path to removed parent
                             // walk through array of the specified parentId's paths
                             for (int j = 0; j < parentPaths.length; j++) {
                                 Path p1 = parentPaths[j]; // path to specified parentId
@@ -351,11 +350,6 @@
         }
 
         return descendants.values().iterator();
-/*
-	ArrayList descendents = new ArrayList();
-	collectDescendantItemStates(parentId, descendents);
-	return Collections.unmodifiableList(descendents).iterator();
-*/
     }
 
     /**
@@ -391,7 +385,7 @@
                  * is a descendant of any of the specified parentId's paths
                  */
                 for (int i = 0; i < paths.length; i++) {
-                    Path p0 = paths[i];	// path to transient state in attic
+                    Path p0 = paths[i]; // path to transient state in attic
                     // walk through array of the specified parentId's paths
                     for (int j = 0; j < parentPaths.length; j++) {
                         Path p1 = parentPaths[j]; // path to specified parentId
@@ -416,11 +410,6 @@
         }
 
         return descendants.values().iterator();
-/*
-	ArrayList descendents = new ArrayList();
-	collectDescendantItemStatesInAttic(parentId, descendents);
-	return Collections.unmodifiableList(descendents).iterator();
-*/
     }
 
     //------< methods for creating & discarding transient ItemState instances >

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/DefaultTransactionalStore.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/DefaultTransactionalStore.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.state.*;
+
+/**
+ * Represents the default transactional store that will sit on top of a
+ * {@link PersistentItemStateProvider}. Updates to this store are not actually
+ * transactional, since failures in later updates will not undo earlier changes.
+ */
+public class DefaultTransactionalStore implements TransactionalStore {
+
+    /**
+     * Provider to use
+     */
+    private PersistentItemStateProvider provider;
+
+    /**
+     * Create a new instance of this class
+     *
+     * @param provider peristent item state provider
+     */
+    public DefaultTransactionalStore(PersistentItemStateProvider provider) {
+        this.provider = provider;
+    }
+
+    //----------------------------------------------------< TransactionalStore >
+
+    /**
+     * @see TransactionalStore#load
+     */
+    public ItemState load(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        return provider.getItemState(id);
+    }
+
+    /**
+     * @see TransactionalStore#beginUpdate
+     */
+    public TransactionalStore.Update beginUpdate() {
+        return new Update();
+    }
+
+    /**
+     * Update implementation
+     */
+    class Update implements TransactionalStore.Update {
+
+        /**
+         * Flag indicating whether this update has already ended
+         */
+        private boolean ended;
+
+        //-----------------------------------------< TransactionalStore.Update >
+
+        /**
+         * @see TransactionalStore.Update#put
+         */
+        public void put(ItemState state) throws ItemStateException {
+            if (ended) {
+                throw new IllegalStateException("Update already ended.");
+            }
+
+            if (state.isNode()) {
+                put((NodeState) state);
+            } else {
+                put((PropertyState) state);
+            }
+        }
+
+        /**
+         * Put a node state
+         *
+         * @param from node state
+         * @throws ItemStateException if an error occurs
+         */
+        private void put(NodeState from) throws ItemStateException {
+            PersistentNodeState to = getOrCreateState(from);
+            to.setParentUUIDs(from.getParentUUIDs());
+            to.setMixinTypeNames(from.getMixinTypeNames());
+            to.setDefinitionId(from.getDefinitionId());
+            to.setChildNodeEntries(from.getChildNodeEntries());
+            to.setPropertyEntries(from.getPropertyEntries());
+            to.store();
+        }
+
+        /**
+         * Get or create node state from underlying provider
+         *
+         * @param state state to retrieve
+         * @return provider's node state
+         */
+        private PersistentNodeState getOrCreateState(NodeState state)
+                throws ItemStateException {
+
+            if (provider.hasItemState(state.getId())) {
+                return (PersistentNodeState) provider.getItemState(state.getId());
+            }
+            return provider.createNodeState(state.getUUID(),
+                    state.getNodeTypeName(), state.getParentUUID());
+        }
+
+        /**
+         * Put a property state
+         *
+         * @param from property state
+         * @throws ItemStateException if an error occurs
+         */
+        private void put(PropertyState from) throws ItemStateException {
+            PersistentPropertyState to = getOrCreateState(from);
+            to.setDefinitionId(from.getDefinitionId());
+            to.setType(from.getType());
+            to.setValues(from.getValues());
+            to.store();
+        }
+
+        /**
+         * Get or create property state from underlying provider
+         *
+         * @param state state to retrieve
+         * @return provider's property state
+         */
+        private PersistentPropertyState getOrCreateState(PropertyState state)
+                throws ItemStateException {
+
+            if (provider.hasItemState(state.getId())) {
+                return (PersistentPropertyState) provider.getItemState(state.getId());
+            }
+            return provider.createPropertyState(state.getParentUUID(), state.getName());
+        }
+
+        /**
+         * @see TransactionalStore.Update#delete
+         */
+        public void delete(ItemState state) throws ItemStateException {
+            if (ended) {
+                throw new IllegalStateException("Update already ended.");
+            }
+
+            PersistableItemState to = (PersistableItemState)
+                    provider.getItemState(state.getId());
+            to.destroy();
+        }
+
+        /**
+         * @see TransactionalStore.Update#end
+         */
+        public void end() throws ItemStateException {
+            ended = true;
+        }
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/PlaybackListener.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/PlaybackListener.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+/**
+ * Listener interface used when playing back the contents of a transaction.
+ */
+interface PlaybackListener {
+
+    /**
+     * Invoked when an element was created.
+     *
+     * @param id element id
+     * @throws TransactionException if an error occurs
+     */
+    public void elementCreated(String id) throws TransactionException;
+
+    /**
+     * Invoked when an item was updated.
+     *
+     * @param id element id
+     * @throws TransactionException if an error occurs
+     */
+    public void elementUpdated(String id) throws TransactionException;
+
+    /**
+     * Invoked when an item was deleted.
+     *
+     * @param id element id
+     * @throws TransactionException if an error occurs
+     */
+    public void elementDeleted(String id) throws TransactionException;
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/Transaction.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/Transaction.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+/**
+ * Represents the transaction on behalf of the component that wants to
+ * explictely demarcate transcation boundaries.
+ */
+public interface Transaction {
+
+    /**
+     * Known attribute name
+     */
+    public static final String ATTRIBUTE_TX_ID = "TX_ID";
+
+    /**
+     * Known attribute name
+     */
+    public static final String ATTRIBUTE_XID = "XID";
+
+    /**
+     * Prepare this transaction. Prepares all changes to items contained in the
+     * transaction. After having prepared a transaction, no more changes to
+     * the underlying items may occur, until either {@link #commit} or
+     * {@link #rollback} has been invoked.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public void prepare() throws TransactionException;
+
+    /**
+     * Commit this transaction. Commits all changes to items contained in the
+     * transaction. After having successfully committed the transaction, it
+     * may no longer be used.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public void commit() throws TransactionException;
+
+    /**
+     * Rollback this transaction. Rollbacks all changes to items contained in
+     * the transaction. After having successfully rolled back the transaction,
+     * it may no longer be used.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public void rollback() throws TransactionException;
+
+    /**
+     * Set the only possible outcome for this transaction to rollback.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public void setRollbackOnly() throws TransactionException;
+
+    /**
+     * Set an attribute on this transaction. If the value specified is
+     * <code>null</code>, it is semantically equivalent to
+     * {@link #removeAttribute}.
+     *
+     * @param name  attribute name
+     * @param value attribute value
+     */
+    public void setAttribute(String name, Object value);
+
+    /**
+     * Return an attribute value on this transaction.
+     *
+     * @param name attribute name
+     * @return attribute value, <code>null</code> if no attribute with that
+     *         name exists
+     */
+    public Object getAttribute(String name);
+
+    /**
+     * Remove an attribute on this transaction.
+     *
+     * @param name attribute name
+     */
+    public void removeAttribute(String name);
+
+    /**
+     * Add a transaction listener. This listener will be invoked when the
+     * transaction is either committed or rolled back.
+     *
+     * @param listener listener to add
+     */
+    public void addListener(TransactionListener listener);
+
+    /**
+     * Remove a transaction listener previously added with {@link #addListener}
+     *
+     * @param listener listener to remove
+     */
+    public void removeListener(TransactionListener listener);
+
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionException.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionException.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.BaseException;
+
+/**
+ * TransactionException is thrown when some operation inside the transaction
+ * fails.
+ */
+public class TransactionException extends BaseException {
+
+    /**
+     * Creates an instance of this class. Takes a detail message as parameter.
+     *
+     * @param message message
+     */
+    public TransactionException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an instance of this class. Takes a root throwable as parameter.
+     *
+     * @param rootCause root throwable
+     */
+    public TransactionException(Throwable rootCause) {
+        super(rootCause);
+    }
+
+    /**
+     * Creates an instance of this class. Takes a message and a root throwable
+     * as parameter.
+     *
+     * @param message   message
+     * @param rootCause root throwable
+     */
+    public TransactionException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionImpl.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionImpl.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,629 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.InternalValue;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.nodetype.NodeDefId;
+import org.apache.jackrabbit.core.nodetype.PropDefId;
+import org.apache.jackrabbit.core.state.*;
+
+import javax.jcr.PropertyType;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Represents the transaction on the behalf of the item state manager. Manages
+ * the transaction log and registration of items that are participating in a
+ * transaction.
+ */
+class TransactionImpl implements Transaction, PlaybackListener {
+
+    /**
+     * Status constant
+     */
+    public static final int STATUS_NONE = 0;
+
+    /**
+     * Status constant
+     */
+    public static final int STATUS_PREPARE = 1;
+
+    /**
+     * Status constant
+     */
+    public static final int STATUS_COMMIT = 2;
+
+    /**
+     * Status constant
+     */
+    public static final int STATUS_ROLLBACK = 3;
+
+    /**
+     * Status constant
+     */
+    public static final int STATUS_ROLLBACK_ONLY = 4;
+
+    /**
+     * Parent directory
+     */
+    private final File directory;
+
+    /**
+     * Transactional store
+     */
+    private final TransactionalStore store;
+
+    /**
+     * Transaction log
+     */
+    private final TransactionLog txLog;
+
+    /**
+     * Transaction listeners
+     */
+    private final List listeners = new ArrayList();
+
+    /**
+     * Transaction attributes
+     */
+    private final Map attributes = new HashMap();
+
+    /**
+     * Update
+     */
+    private TransactionalStore.Update update;
+
+    /**
+     * Transaction state
+     */
+    private int txStatus;
+
+    /**
+     * Number of stored items so far
+     */
+    private int counter;
+
+    /**
+     * Create a new instance of this class. Used when beginnning global
+     * transactions.
+     *
+     * @param directory directory to use for writing the transaction log and
+     *                  objects
+     * @param store     store to use when preparing and committing the transaction
+     */
+    public TransactionImpl(File directory, TransactionalStore store)
+            throws TransactionException {
+
+        this.directory = directory;
+        this.store = store;
+
+        directory.mkdirs();
+        if (!directory.isDirectory()) {
+            throw new TransactionException("Unable to create directory.");
+        }
+        try {
+            txLog = new TransactionLog(new File(directory, "tx.log"));
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create transaction log.", e);
+        }
+    }
+
+    /**
+     * Enlist a item state to this transaction. Saves the state of the item
+     * to a persistent store and adds a record to the transaction log.
+     *
+     * @param state  item state
+     * @param status new status
+     * @throws TransactionException if an error occurs
+     */
+    public void enlist(ItemState state, int status) throws TransactionException {
+        if (txStatus != STATUS_NONE) {
+            throw new TransactionException("Transaction not in clean state.");
+        }
+        switch (status) {
+            case ItemState.STATUS_NEW:
+                try {
+                    txLog.logCreated(store(state));
+                } catch (IOException e) {
+                    throw new TransactionException("Unable to store state.", e);
+                }
+                break;
+            case ItemState.STATUS_EXISTING:
+                try {
+                    txLog.logUpdated(store(state));
+                } catch (IOException e) {
+                    throw new TransactionException("Unable to store state.", e);
+                }
+                break;
+            case ItemState.STATUS_EXISTING_REMOVED:
+                try {
+                    txLog.logDeleted(store(state));
+                } catch (IOException e) {
+                    throw new TransactionException("Unable to store state.", e);
+                }
+                break;
+        }
+    }
+
+    //-----------------------------------------------------------< Transaction >
+
+    /**
+     * @see Transaction#prepare
+     */
+    public void prepare() throws TransactionException {
+        if (txStatus != STATUS_NONE) {
+            throw new TransactionException("Transaction not in clean state.");
+        }
+
+        update = store.beginUpdate();
+
+        txLog.playback(this);
+        txLog.logPrepare();
+
+        txStatus = STATUS_PREPARE;
+    }
+
+    /**
+     * @see Transaction#commit
+     */
+    public void commit() throws TransactionException {
+        if (txStatus != STATUS_PREPARE) {
+            throw new TransactionException("Transaction not prepared.");
+        }
+
+        try {
+            update.end();
+        } catch (ItemStateException e) {
+            throw new TransactionException("Unable to end update.", e);
+        }
+
+        txLog.logCommit();
+        txStatus = STATUS_COMMIT;
+
+        notifyCommitted();
+    }
+
+    /**
+     * @see Transaction#rollback
+     */
+    public void rollback() throws TransactionException {
+        if (txStatus != STATUS_NONE && txStatus != STATUS_PREPARE &&
+                txStatus != STATUS_ROLLBACK_ONLY) {
+            throw new TransactionException("Transaction neither clean nor prepared.");
+        }
+        txLog.logRollback();
+        txStatus = STATUS_ROLLBACK;
+
+        notifyRolledBack();
+    }
+
+    /**
+     * @see Transaction#setRollbackOnly
+     */
+    public void setRollbackOnly() throws TransactionException {
+        if (txStatus != STATUS_NONE || txStatus != STATUS_ROLLBACK_ONLY) {
+            throw new TransactionException("Transaction not clean.");
+        }
+        txStatus = STATUS_ROLLBACK_ONLY;
+    }
+
+    /**
+     * @see Transaction#setAttribute
+     */
+    public void setAttribute(String name, Object value) {
+        if (value == null) {
+            removeAttribute(name);
+        }
+        attributes.put(name, value);
+    }
+
+    /**
+     * @see Transaction#getAttribute
+     */
+    public Object getAttribute(String name) {
+        return attributes.get(name);
+    }
+
+    /**
+     * @see Transaction#removeAttribute
+     */
+    public void removeAttribute(String name) {
+        attributes.remove(name);
+    }
+
+    /**
+     * Add a transaction listener
+     *
+     * @param listener listener to add
+     */
+    public void addListener(TransactionListener listener) {
+        synchronized (listeners) {
+            listeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove a transaction listener
+     *
+     * @param listener listener to remove
+     */
+    public void removeListener(TransactionListener listener) {
+        synchronized (listeners) {
+            listeners.remove(listener);
+        }
+    }
+
+    /**
+     * Notify listeners that transaction was committed. Since there is at most
+     * one commit and one rollback event to be reported, the listeners can
+     * safely be cleared at the same time.
+     */
+    private void notifyCommitted() {
+        TransactionListener[] al;
+
+        synchronized (listeners) {
+            al = new TransactionListener[listeners.size()];
+            listeners.toArray(al);
+            listeners.clear();
+        }
+
+        for (int i = 0; i < al.length; i++) {
+            al[i].transactionCommitted(this);
+        }
+    }
+
+    /**
+     * Notify listeners that transaction was rolled back. Since there is at most
+     * one commit and one rollback event to be reported, the listeners can
+     * safely be cleared at the same time.
+     */
+    private void notifyRolledBack() {
+        TransactionListener[] al;
+
+        synchronized (listeners) {
+            al = new TransactionListener[listeners.size()];
+            listeners.toArray(al);
+            listeners.clear();
+        }
+
+        for (int i = 0; i < al.length; i++) {
+            al[i].transactionRolledBack(this);
+        }
+    }
+
+    //------------------------------------------------------< PlaybackListener >
+
+    /**
+     * @see PlaybackListener#elementCreated
+     */
+    public void elementCreated(String id) throws TransactionException {
+        try {
+            update.put(load(id));
+        } catch (NoSuchItemStateException e) {
+            throw new TransactionException("Unable to load item.", e);
+        } catch (ItemStateException e) {
+            throw new TransactionException("Unable to put item.", e);
+        }
+    }
+
+    /**
+     * @see PlaybackListener#elementUpdated
+     */
+    public void elementUpdated(String id) throws TransactionException {
+        try {
+            update.put(load(id));
+        } catch (NoSuchItemStateException e) {
+            throw new TransactionException("Unable to load item.", e);
+        } catch (ItemStateException e) {
+            throw new TransactionException("Unable to put item.", e);
+        }
+    }
+
+    /**
+     * @see PlaybackListener#elementDeleted
+     */
+    public void elementDeleted(String id) throws TransactionException {
+        try {
+            update.delete(load(id));
+        } catch (NoSuchItemStateException e) {
+            throw new TransactionException("Unable to load item.", e);
+        } catch (ItemStateException e) {
+            throw new TransactionException("Unable to delete item.", e);
+        }
+    }
+
+    //---------------------------------------------------------------< Storage >
+
+    /**
+     * Load a state from the transaction-managed storage
+     *
+     * @param id element id
+     * @throws NoSuchItemStateException if the item state does not exist
+     * @throws ItemStateException       if loading the item state fails
+     */
+    public ItemState load(String id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (id.startsWith("N")) {
+            return loadNodeState(id);
+        } else {
+            return loadPropertyState(id);
+        }
+    }
+
+    /**
+     * Load a node state from the transaction-managed storage
+     */
+    private PersistentNodeState loadNodeState(String id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        Properties p = new Properties();
+        InputStream in = null;
+
+        try {
+            in = new BufferedInputStream(new FileInputStream(new File(directory, id)));
+            p.load(in);
+
+        } catch (IOException e) {
+            throw new ItemStateException("Unable to load state from file.", e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e2) {
+                }
+            }
+        }
+
+        PersistentNodeState state = new TransactionalNodeState(p.getProperty("uuid"), this);
+
+        state.setParentUUID(p.getProperty("parentUUID"));
+        state.setDefinitionId(NodeDefId.valueOf(p.getProperty("definitionId")));
+        state.setNodeTypeName(QName.valueOf(p.getProperty("nodeTypeName")));
+
+        state.setParentUUIDs(getList(p, "parentUUIDs"));
+        state.setMixinTypeNames(getSet(p, "mixinTypeNames"));
+        addPropertyEntries(state, p, "propertyEntries");
+        addChildNodeEntries(state, p, "childNodeEntries");
+
+        return state;
+    }
+
+    /**
+     * Load a property state from the transaction-managed storage
+     */
+    private PersistentPropertyState loadPropertyState(String id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        Properties p = new Properties();
+        InputStream in = null;
+
+        try {
+            in = new BufferedInputStream(new FileInputStream(new File(directory, id)));
+            p.load(in);
+
+        } catch (IOException e) {
+            throw new ItemStateException("Unable to load state from file.", e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e2) {
+                }
+            }
+        }
+
+        PersistentPropertyState state = new TransactionalPropertyState(QName.valueOf(p.getProperty("name")),
+                p.getProperty("parentUUID"), this);
+
+        state.setType(PropertyType.valueFromName(p.getProperty("type")));
+        state.setDefinitionId(PropDefId.valueOf(p.getProperty("definitionId")));
+        state.setValues(getValues(p, "values", state.getType()));
+
+        return state;
+    }
+
+    /**
+     * Store an item state to the transaction-managed storage
+     *
+     * @param state item state
+     * @return independent element id that was assigned
+     * @throws IOException if an error occurs
+     */
+    public String store(ItemState state) throws IOException {
+        if (state.isNode()) {
+            return store((NodeState) state);
+        } else {
+            return store((PropertyState) state);
+        }
+    }
+
+    /**
+     * Store a node state to the transaction-managed storage
+     */
+    private String store(NodeState state) throws IOException {
+        Properties p = new Properties();
+
+        p.setProperty("uuid", state.getUUID());
+        if (state.getParentUUID() != null) {
+            p.setProperty("parentUUID", state.getParentUUID());
+        }
+        p.setProperty("definitionId", state.getDefinitionId().toString());
+        p.setProperty("nodeTypeName", state.getNodeTypeName().toString());
+        setCollection(p, "parentUUIDs", state.getParentUUIDs());
+        setCollection(p, "mixinTypeNames", state.getMixinTypeNames());
+        setCollection(p, "propertyEntries", state.getPropertyEntries());
+        setChildNodeEntries(p, "childNodeEntries", state.getChildNodeEntries());
+
+        return store(p, "N");
+    }
+
+    /**
+     * Store a property state to the transaction-managed storage
+     */
+    private String store(PropertyState state) throws IOException {
+        Properties p = new Properties();
+
+        p.setProperty("name", state.getName().toString());
+        p.setProperty("parentUUID", state.getParentUUID());
+
+        p.setProperty("type", PropertyType.nameFromValue(state.getType()));
+        p.setProperty("definitionId", state.getDefinitionId().toString());
+        setValues(p, "values", state.getValues());
+
+        return store(p, "P");
+    }
+
+    /**
+     * Store some properties for a specific item id
+     */
+    private String store(Properties p, String prefix) throws IOException {
+        OutputStream out = null;
+
+        try {
+            String id = prefix + nextElementId();
+
+            File f = new File(directory, id);
+            f.getParentFile().mkdirs();
+
+            out = new BufferedOutputStream(new FileOutputStream(f));
+            p.store(out, null);
+
+            return id;
+
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e2) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the next element id
+     */
+    private String nextElementId() {
+        return String.valueOf(++counter);
+    }
+
+    /**
+     * Retrieve a list from a properties array.
+     */
+    private List getList(Properties p, String baseName) {
+        List l = new ArrayList();
+
+        int count = Integer.parseInt(p.getProperty(baseName));
+        for (int i = 0; i < count; i++) {
+            l.add(p.getProperty(baseName + "." + i));
+        }
+        return l;
+    }
+
+    /**
+     * Retrieve a set from a properties array.
+     */
+    private Set getSet(Properties p, String baseName) {
+        Set s = new HashSet();
+
+        int count = Integer.parseInt(p.getProperty(baseName));
+        for (int i = 0; i < count; i++) {
+            s.add(p.getProperty(baseName + "." + i));
+        }
+        return s;
+    }
+
+    /**
+     * Retrieve an array of values from a properties array.
+     */
+    private InternalValue[] getValues(Properties p, String baseName, int type) {
+        int count = Integer.parseInt(p.getProperty(baseName));
+
+        InternalValue[] values = new InternalValue[count];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = InternalValue.valueOf(p.getProperty(baseName + "." + i), type);
+        }
+        return values;
+    }
+
+    /**
+     * Add property entries.
+     */
+    private void addPropertyEntries(NodeState s, Properties p, String baseName) {
+        int count = Integer.parseInt(p.getProperty(baseName));
+        for (int i = 0; i < count; i++) {
+            s.addPropertyEntry(QName.valueOf(p.getProperty(baseName + "." + i)));
+        }
+    }
+
+    /**
+     * Add child node entries.
+     */
+    private void addChildNodeEntries(NodeState s, Properties p, String baseName) {
+        int count = Integer.parseInt(p.getProperty(baseName));
+        for (int i = 0; i < count; i++) {
+            QName name = QName.valueOf(p.getProperty(baseName + ".name." + i));
+            String uuid = p.getProperty(baseName + ".uuid." + i);
+            s.addChildNodeEntry(name, uuid);
+        }
+    }
+
+    /**
+     * Set properties from a list. The total number of items will be stored,
+     * as well as the items themselves.
+     */
+    private void setCollection(Properties p, String baseName, Collection c) {
+        p.setProperty(baseName, String.valueOf(c.size()));
+
+        Iterator iter = c.iterator();
+        int i = 0;
+        while (iter.hasNext()) {
+            p.setProperty(baseName + "." + i, iter.next().toString());
+            i++;
+        }
+    }
+
+    /**
+     * Set properties from a list. The total number of items will be stored,
+     * as well as the items themselves.
+     */
+    private void setChildNodeEntries(Properties p, String baseName, Collection c) {
+        p.setProperty(baseName, String.valueOf(c.size()));
+
+        Iterator iter = c.iterator();
+        int i = 0;
+        while (iter.hasNext()) {
+            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
+            p.setProperty(baseName + ".name." + i, entry.getName().toString());
+            p.setProperty(baseName + ".uuid." + i, entry.getUUID());
+            i++;
+        }
+    }
+
+    /**
+     * Set properties from an array of values. The total number of items will be
+     * stored, as well as the items themselves.
+     */
+    private void setValues(Properties p, String baseName, Object[] values) {
+        p.setProperty(baseName, String.valueOf(values.length));
+
+        for (int i = 0; i < values.length; i++) {
+            p.setProperty(baseName + "." + i, values[i].toString());
+        }
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionListener.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionListener.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+/**
+ * Listener on a transaction. Will receive notifications about commit
+ * and rollback actions.
+ *
+ * @see Transaction
+ */
+public interface TransactionListener {
+
+    /**
+     * Transaction was committed
+     *
+     * @param tx transaction that was committed
+     */
+    public void transactionCommitted(Transaction tx);
+
+    /**
+     * Transaction was rolled back
+     *
+     * @param tx transaction that was rolled back
+     */
+    public void transactionRolledBack(Transaction tx);
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionLog.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionLog.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * Logs operations of a transaction.
+ */
+class TransactionLog {
+
+    /**
+     * Writer for transaction log
+     */
+    private RandomAccessFile file;
+
+    /**
+     * Position for last transaction start
+     */
+    private long start;
+
+    /**
+     * Create a new instance of this class
+     *
+     * @param file underlying file for this transaction log
+     */
+    public TransactionLog(File file) throws IOException {
+        this.file = new RandomAccessFile(file, "rw");
+        this.file.setLength(0);
+    }
+
+    /**
+     * Close the transaction log. Closes the underlying file.
+     */
+    public void close() {
+        if (file != null) {
+            try {
+                file.close();
+            } catch (IOException e) {
+            }
+
+            file = null;
+        }
+    }
+
+    /**
+     * Playback transaction log, i.e. scan all entries upto the last
+     * uncommitted transaction and re-create the items
+     */
+    public synchronized void playback(PlaybackListener listener)
+            throws TransactionException {
+
+        checkState();
+
+        try {
+            file.seek(start);
+
+            for (; ;) {
+                String s = file.readLine();
+                if (s == null || s.equals("")) {
+                    break;
+                }
+                switch (s.charAt(0)) {
+                    case 'I':
+                        listener.elementCreated(elementIdFromLogRecord(s));
+                        break;
+                    case 'U':
+                        listener.elementUpdated(elementIdFromLogRecord(s));
+                        break;
+                    case 'D':
+                        listener.elementDeleted(elementIdFromLogRecord(s));
+                        break;
+                    case 'P':
+                    case 'C':
+                    case 'R':
+                        break;
+                    default:
+                        throw new IOException("Bad log record");
+                }
+            }
+        } catch (IOException e) {
+            throw new TransactionException("Unable to playback log.", e);
+        }
+    }
+
+    /**
+     * Add a log record to the transaction log. Invoked when a new item
+     * has been created.
+     *
+     * @param id item id
+     * @throws TransactionException if an error occurs
+     */
+    public synchronized void logCreated(String id) throws TransactionException {
+        checkState();
+
+        try {
+            file.writeBytes("I-");
+            file.writeBytes(id);
+            file.writeByte('\n');
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create log record.", e);
+        }
+    }
+
+    /**
+     * Add a log record to the transaction log. Invoked when an existing item
+     * has been updated.
+     *
+     * @param id item id
+     * @throws TransactionException if an error occurs
+     */
+    public synchronized void logUpdated(String id) throws TransactionException {
+        checkState();
+
+        try {
+            file.writeBytes("U-");
+            file.writeBytes(id);
+            file.writeByte('\n');
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create log record.", e);
+        }
+    }
+
+    /**
+     * Add a log record to the transaction log. Invoked when an existing item
+     * has been deleted.
+     *
+     * @param id item id
+     * @throws TransactionException if an error occurs
+     */
+    public synchronized void logDeleted(String id) throws TransactionException {
+        checkState();
+
+        try {
+            file.writeBytes("D-");
+            file.writeBytes(id);
+            file.writeByte('\n');
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create log record.", e);
+        }
+    }
+
+    /**
+     * Add a log record to the transaction log. Invoked when a transaction has
+     * been prepared.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public synchronized void logPrepare() throws TransactionException {
+        checkState();
+
+        try {
+            file.writeBytes("P");
+            file.writeByte('\n');
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create log record.", e);
+        }
+    }
+
+    /**
+     * Add a log record to the transaction log. Invoked when a transaction has
+     * been committed.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public synchronized void logCommit() throws TransactionException {
+        checkState();
+
+        try {
+            file.writeBytes("C");
+            file.writeByte('\n');
+            start = file.getFilePointer();
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create log record.", e);
+        }
+    }
+
+    /**
+     * Add a log record to the transaction log. Invoked when a transaction has
+     * been rolled back.
+     *
+     * @throws TransactionException if an error occurs
+     */
+    public synchronized void logRollback() throws TransactionException {
+        checkState();
+
+        try {
+            file.writeBytes("R");
+            file.writeByte('\n');
+            start = file.getFilePointer();
+        } catch (IOException e) {
+            throw new TransactionException("Unable to create log record.", e);
+        }
+    }
+
+    /**
+     * Check state of this transaction log.
+     */
+    private void checkState() throws IllegalStateException {
+        if (file == null) {
+            throw new IllegalStateException("Log already closed.");
+        }
+    }
+
+    /**
+     * Recreates an element id from a log record. If a bad format is discovered,
+     * an IOException is generated.
+     *
+     * @param s log record
+     * @return element id
+     * @throws IOException if the log record is invalid
+     */
+    private String elementIdFromLogRecord(String s) throws IOException {
+        if (s.length() > 2) {
+            return s.substring(2);
+        }
+        throw new IOException("Bad log record");
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionManager.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionManager.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import javax.transaction.xa.Xid;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents the transaction manager to be used by a component that will
+ * itself control the boundaries of a transaction.<p>
+ * When starting transactions for operations that directly use
+ * item states and where a <code>PersistentItemStateProvider</code> is
+ * used, this is how to allocate a transaction and associate it with
+ * the provider:
+ * <pre>
+ * // given provider
+ * PersistentItemStateProvider provider;
+ * // given transaction manager
+ * TransactionManager txManager;
+ * <p/>
+ * // create a store to use when committing changes
+ * TransactionalStore store = new DefaultTransactionalStore(provider);
+ * // allocate transaction
+ * Transaction tx = txManager.beginTransaction(store);
+ * // create a new provider to use
+ * PersistentItemStateProvider txProvider;
+ * txProvider = new TransactionalItemStateManager(tx, store);
+ * <p/>
+ * // now operate on the new provider that is transactional
+ * ...
+ * <p/>
+ * // commit transaction, which will automatically save all changes
+ * tx.commit();
+ * </pre>
+ */
+public class TransactionManager implements TransactionListener {
+
+    /**
+     * Directory for transactions
+     */
+    private final File directory;
+
+    /**
+     * Global transactions
+     */
+    private final Map txGlobal = new HashMap();
+
+    /**
+     * Transaction ID counter
+     */
+    private int txIdCounter;
+
+    /**
+     * Create a new instance of this class. Takes the parent directory
+     * of all transactions as parameter.
+     */
+    public TransactionManager(File directory) throws IOException {
+        this.directory = directory;
+
+        directory.mkdirs();
+        if (!directory.isDirectory()) {
+            throw new IOException("Unable to create transaction manager directory");
+        }
+    }
+
+    /**
+     * Begin a new transaction, given a transactional store.
+     *
+     * @param store store to use when committing changes
+     * @return transaction that may be prepared, committed and rolled back
+     * @throws TransactionException if the transaction can not be created
+     */
+    public Transaction beginTransaction(TransactionalStore store)
+            throws TransactionException {
+
+        String txId = nextTxId();
+
+        Transaction tx = new TransactionImpl(txIdToDirectory(txId), store);
+        tx.setAttribute(Transaction.ATTRIBUTE_TX_ID, txId);
+        tx.addListener(this);
+
+        return tx;
+    }
+
+    /**
+     * Begin a new global transaction. The object returned is at the same time
+     * a {@link TransactionalItemStateManager}
+     *
+     * @return transaction
+     */
+    public Transaction beginTransaction(Xid xid, TransactionalStore store)
+            throws TransactionException {
+
+        String txId = nextTxId();
+
+        Transaction tx = new TransactionImpl(txIdToDirectory(txId), store);
+        tx.setAttribute(Transaction.ATTRIBUTE_TX_ID, txId);
+        tx.setAttribute(Transaction.ATTRIBUTE_XID, xid);
+        tx.addListener(this);
+        txGlobal.put(xid, tx);
+
+        return tx;
+    }
+
+    /**
+     * Return a global transaction known to this transaction manager. The object
+     * returned is at the same time a {@link TransactionalItemStateManager}
+     *
+     * @param xid XID
+     * @return transaction, <code>null</code> if the transaction is unknown
+     */
+    public Transaction getTransaction(Xid xid) {
+        return (Transaction) txGlobal.get(xid);
+    }
+
+    //---------------------------------------------------< TransactionListener >
+
+    /**
+     * @see TransactionListener#transactionCommitted
+     *      <p/>
+     *      Remove this transaction if this is a global transaction.
+     */
+    public void transactionCommitted(Transaction tx) {
+        Xid xid = (Xid) tx.getAttribute(Transaction.ATTRIBUTE_XID);
+        if (xid != null) {
+            txGlobal.remove(xid);
+        }
+    }
+
+    /**
+     * @see TransactionListener#transactionCommitted
+     *      <p/>
+     *      Remove this transaction if this is a global transaction.
+     */
+    public void transactionRolledBack(Transaction tx) {
+        Xid xid = (Xid) tx.getAttribute(Transaction.ATTRIBUTE_XID);
+        if (xid != null) {
+            txGlobal.remove(xid);
+        }
+    }
+
+    /**
+     * Return the next available transaction Id
+     */
+    private String nextTxId() {
+        int txId = ++txIdCounter;
+
+        char[] rep = new char[8];
+        for (int i = 0; i < rep.length; i++) {
+            int b = txId & 0x0F;
+            rep[rep.length - i - 1] = Integer.toHexString(b).charAt(0);
+            txId >>>= 4;
+        }
+        return new String(rep);
+    }
+
+    /**
+     * Return the directory used for some transaction.
+     *
+     * @param txId transaction id
+     * @return directory to use
+     */
+    private File txIdToDirectory(String txId) {
+        return new File(directory, txId);
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalItemStateManager.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalItemStateManager.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.state.*;
+import org.apache.log4j.Logger;
+
+/**
+ * Delivers node and item states that will participate in a transaction.
+ */
+public class TransactionalItemStateManager extends ItemStateCache
+        implements PersistentItemStateProvider, ItemStateListener {
+
+    /**
+     * Logger instance
+     */
+    private static Logger log = Logger.getLogger(TransactionalItemStateManager.class);
+
+    /**
+     * Controlling transaction
+     */
+    private final Transaction tx;
+
+    /**
+     * Store to use
+     */
+    private final TransactionalStore store;
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param tx    controlling transaction
+     * @param store store to use when loading elements that have not yet
+     *              been cached
+     */
+    public TransactionalItemStateManager(Transaction tx,
+                                         TransactionalStore store) {
+        this.tx = tx;
+        this.store = store;
+    }
+
+    //-------------------------------------------< PersistentItemStateProvider >
+
+    /**
+     * @see PersistentItemStateProvider#createNodeState
+     */
+    public PersistentNodeState createNodeState(String uuid, QName nodeTypeName,
+                                               String parentUUID)
+            throws ItemStateException {
+
+        NodeId id = new NodeId(uuid);
+        if (isCached(id)) {
+            String msg = "A node state instance with id " + id + " already exists.";
+            log.error(msg);
+            throw new ItemStateException(msg);
+        }
+
+        PersistentNodeState state = createState(id);
+        state.setNodeTypeName(nodeTypeName);
+        state.setParentUUID(parentUUID);
+        cache(state);
+
+        return state;
+    }
+
+    /**
+     * @see PersistentItemStateProvider#createPropertyState
+     */
+    public PersistentPropertyState createPropertyState(String parentUUID,
+                                                       QName propName)
+            throws ItemStateException {
+
+        PropertyId id = new PropertyId(parentUUID, propName);
+        if (isCached(id)) {
+            String msg = "A property state instance with id " + id + " already exists.";
+            log.error(msg);
+            throw new ItemStateException(msg);
+        }
+
+        PersistentPropertyState state = createState(id);
+        cache(state);
+
+        return state;
+    }
+
+    /**
+     * @see PersistentItemStateProvider#getItemState
+     *      <p/>
+     *      Calls the transactional store to retrieve the shared, read-only item.
+     *      Wraps this item with a transactional extension that will intercept
+     *      calls to {@link PersistableItemState#store} and
+     *      {@link PersistableItemState#destroy}.
+     */
+    public synchronized ItemState getItemState(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (isCached(id)) {
+            return retrieve(id);
+        }
+
+        ItemState state = store.load(id);
+        if (state.isNode()) {
+            state = createState((PersistentNodeState) state);
+        } else {
+            state = createState((PersistentPropertyState) state);
+        }
+        cache(state);
+        state.addListener(this);
+
+        return state;
+    }
+
+    /**
+     * @see PersistentItemStateProvider#hasItemState
+     */
+    public boolean hasItemState(ItemId id) {
+        try {
+            getItemState(id);
+            return true;
+        } catch (ItemStateException e) {
+            return false;
+        }
+    }
+
+    /**
+     * @see PersistentItemStateProvider#getItemStateInAttic
+     */
+    public ItemState getItemStateInAttic(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        throw new NoSuchItemStateException(id.toString());
+    }
+
+    /**
+     * @see PersistentItemStateProvider#hasItemStateInAttic
+     */
+    public boolean hasItemStateInAttic(ItemId id) {
+        return false;
+    }
+
+    /**
+     * Create a persistent node state instance given its id.
+     *
+     * @param id node id
+     */
+    private PersistentNodeState createState(NodeId id) {
+        return new TransactionalNodeState(id.getUUID(), tx);
+    }
+
+    /**
+     * Create a persistent node state instance given its overlaid state.
+     *
+     * @param state overlaid state
+     */
+    private PersistentNodeState createState(PersistentNodeState state) {
+        return new TransactionalNodeState(state, tx);
+    }
+
+    /**
+     * Create a persistent property state instance given its id.
+     *
+     * @param id property id
+     */
+    private PersistentPropertyState createState(PropertyId id) {
+        return new TransactionalPropertyState(id.getName(),
+                id.getParentUUID(), tx);
+    }
+
+    /**
+     * Create a persistent property state instance given its overlaid state.
+     *
+     * @param state overlaid state
+     */
+    private PersistentPropertyState createState(PersistentPropertyState state) {
+        return new TransactionalPropertyState(state, tx);
+    }
+
+    //-----------------------------------------------------< ItemStateListener >
+
+    /**
+     * @see ItemStateListener#stateCreated
+     *      <p/>
+     *      Invoked when the state of some freshly created item has been stored
+     *      for the first time.
+     */
+    public void stateCreated(ItemState created) {
+    }
+
+    /**
+     * @see ItemStateListener#stateModified
+     *      <p/>
+     *      Invoked when the state of some item has been stored. The state is no
+     *      longer connected to the actual persistent state, but will continue
+     *      its life in the transaction.
+     */
+    public void stateModified(ItemState modified) {
+    }
+
+    /**
+     * @see ItemStateListener#stateDestroyed
+     *      <p/>
+     *      Invoked when the state of some item has been destroyed. The state is no
+     *      longer connected to the actual persistent state, but will continue
+     *      its life in the transaction.
+     */
+    public void stateDestroyed(ItemState destroyed) {
+        destroyed.removeListener(this);
+    }
+
+    /**
+     * @see ItemStateListener#stateDiscarded
+     */
+    public void stateDiscarded(ItemState discarded) {
+        discarded.removeListener(this);
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalNodeState.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalNodeState.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.PersistentNodeState;
+
+/**
+ * Represents a node state delivered by the transactional subsystem. This
+ * state will be backed up by a persistent node state and return live values
+ * as long as no modifier method is invoked.
+ */
+class TransactionalNodeState extends PersistentNodeState {
+
+    /**
+     * Transaction we are participating in
+     */
+    private TransactionImpl tx;
+
+    /**
+     * Create a new instance of this class. Used when this state is backed
+     * up by an existing persistent node state.
+     *
+     * @param state existing node state
+     * @param tx    transaction
+     */
+    public TransactionalNodeState(PersistentNodeState state,
+                                  Transaction tx) {
+
+        super(state, STATUS_EXISTING, null);
+
+        this.tx = (TransactionImpl) tx;
+    }
+
+    /**
+     * Create a new instance of this class. Used when this state is not backed
+     * up by an existing persistent node state.
+     *
+     * @param uuid UUID of this node
+     * @param tx   transaction
+     */
+    public TransactionalNodeState(String uuid, Transaction tx) {
+        super(uuid, null);
+
+        this.tx = (TransactionImpl) tx;
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.ItemStateListener#stateDestroyed
+     *      <p/>
+     *      Invoked when the underlying persistent node state has been destroyed.
+     *      When such a thing happens, we change our status to
+     *      {@link #STATUS_STALE_DESTROYED}.
+     */
+    public synchronized void stateDestroyed(ItemState destroyed) {
+        switch (status) {
+            case STATUS_NEW:
+            case STATUS_EXISTING_REMOVED:
+            case STATUS_STALE_DESTROYED:
+            case STATUS_STALE_MODIFIED:
+            case STATUS_UNDEFINED:
+                break;
+            case STATUS_EXISTING:
+            case STATUS_EXISTING_MODIFIED:
+                status = STATUS_STALE_DESTROYED;
+                break;
+        }
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.ItemStateListener#stateModified
+     *      <p/>
+     *      Invoked when the underlying persistent node state has been modified
+     *      (i.e. made persistent by some other session). If we still have a
+     *      clean copy of the persistent node state, we update our internal copy
+     *      with the actual values.
+     */
+    public synchronized void stateModified(ItemState updated) {
+        switch (status) {
+            case STATUS_NEW:
+            case STATUS_EXISTING_MODIFIED:
+            case STATUS_EXISTING_REMOVED:
+            case STATUS_STALE_DESTROYED:
+            case STATUS_STALE_MODIFIED:
+            case STATUS_UNDEFINED:
+                break;
+            case STATUS_EXISTING:
+                copy(updated);
+                break;
+        }
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.PersistableItemState#store
+     *      <p/>
+     *      A store on an item disconnects the transactional state from the
+     *      actual persistent state, i.e. changes to the underlying persistent
+     *      state are no longer reflected in this state.
+     */
+    public synchronized void store() throws ItemStateException {
+        try {
+            tx.enlist(this, status);
+        } catch (TransactionException e) {
+            throw new ItemStateException("Unable to store item.", e);
+        }
+
+        if (status == STATUS_NEW) {
+            notifyStateCreated();
+        } else {
+            notifyStateUpdated();
+        }
+        status = STATUS_EXISTING;
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.PersistableItemState#destroy
+     *      <p/>
+     *      A store on an item disconnects the transactional state from the
+     *      actual persistent state, i.e. changes to the underlying persistent
+     *      state are no longer reflected in this state.
+     */
+    public synchronized void destroy() throws ItemStateException {
+        try {
+            tx.enlist(this, STATUS_EXISTING_REMOVED);
+        } catch (TransactionException e) {
+            throw new ItemStateException("Unable to destroy item.", e);
+        }
+        notifyStateDestroyed();
+
+        status = STATUS_UNDEFINED;
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalPropertyState.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalPropertyState.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.PersistentPropertyState;
+
+/**
+ * Represents a property state delivered by the transactional subsystem. This
+ * state will be backed up by a persistent property state and return live values
+ * as long as no modifier method is invoked.
+ */
+class TransactionalPropertyState extends PersistentPropertyState {
+
+    /**
+     * Transaction we are participating in
+     */
+    private TransactionImpl tx;
+
+    /**
+     * Create a new instance of this class. Used when this state is backed
+     * up by an existing persistent property state.
+     *
+     * @param state existing property state
+     * @param tx    transaction
+     */
+    public TransactionalPropertyState(PersistentPropertyState state,
+                                      Transaction tx) {
+
+        super(state, STATUS_EXISTING, null);
+
+        this.tx = (TransactionImpl) tx;
+    }
+
+    /**
+     * Create a new instance of this class. Used when this state is not backed
+     * up by an existing persistent property state.
+     *
+     * @param name       qualified name
+     * @param parentUUID parent UUID
+     * @param tx         transaction
+     */
+    public TransactionalPropertyState(QName name, String parentUUID,
+                                      Transaction tx) {
+
+        super(name, parentUUID, null);
+
+        this.tx = (TransactionImpl) tx;
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.ItemStateListener#stateDestroyed
+     */
+    public synchronized void stateDestroyed(ItemState destroyed) {
+        switch (status) {
+            case STATUS_NEW:
+            case STATUS_EXISTING_REMOVED:
+            case STATUS_STALE_DESTROYED:
+            case STATUS_STALE_MODIFIED:
+            case STATUS_UNDEFINED:
+                break;
+            case STATUS_EXISTING:
+            case STATUS_EXISTING_MODIFIED:
+                status = STATUS_STALE_DESTROYED;
+                break;
+        }
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.ItemStateListener#stateModified
+     */
+    public synchronized void stateModified(ItemState modified) {
+        switch (status) {
+            case STATUS_NEW:
+            case STATUS_EXISTING_MODIFIED:
+            case STATUS_EXISTING_REMOVED:
+            case STATUS_STALE_DESTROYED:
+            case STATUS_STALE_MODIFIED:
+            case STATUS_UNDEFINED:
+                break;
+            case STATUS_EXISTING:
+                copy(modified);
+                break;
+        }
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.PersistableItemState#store
+     *      <p/>
+     *      A store on an item disconnects the transactional state from the
+     *      actual persistent state, i.e. changes to the underlying persistent
+     *      state are no longer reflected in this state.
+     */
+    public synchronized void store() throws ItemStateException {
+        try {
+            tx.enlist(this, status);
+        } catch (TransactionException e) {
+            throw new ItemStateException("Unable to store item.", e);
+        }
+
+        if (status == STATUS_NEW) {
+            notifyStateCreated();
+        } else {
+            notifyStateUpdated();
+        }
+        status = STATUS_EXISTING;
+    }
+
+    /**
+     * @see org.apache.jackrabbit.core.state.PersistableItemState#destroy
+     *      <p/>
+     *      A store on an item disconnects the transactional state from the
+     *      actual persistent state, i.e. changes to the underlying persistent
+     *      state are no longer reflected in this state.
+     */
+    public synchronized void destroy() throws ItemStateException {
+        try {
+            tx.enlist(this, STATUS_EXISTING_REMOVED);
+        } catch (TransactionException e) {
+            throw new ItemStateException("Unable to destroy item.", e);
+        }
+        notifyStateDestroyed();
+
+        status = STATUS_UNDEFINED;
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalStore.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/TransactionalStore.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+
+/**
+ * Represents a store that atomically saves its changes, i.e. either everything
+ * is saved or nothing.
+ */
+public interface TransactionalStore {
+
+    /**
+     * Load an item from the store.
+     */
+    public ItemState load(ItemId id)
+            throws NoSuchItemStateException, ItemStateException;
+
+    /**
+     * Begin an update operation. Subsequent save operations are passed to
+     * the update object returned.
+     *
+     * @return update object
+     */
+    public Update beginUpdate();
+
+    /**
+     * Update operation. Created by a {@link TransactionalStore#beginUpdate}
+     * operation.
+     */
+    public static interface Update {
+
+        /**
+         * Put a state as part of an update operation
+         *
+         * @param state state to store
+         * @throws ItemStateException if an error occurs
+         */
+        public void put(ItemState state) throws ItemStateException;
+
+        /**
+         * Delete a state as part of an update operation
+         *
+         * @param state state to delete
+         * @throws ItemStateException if an error occurs
+         */
+        public void delete(ItemState state) throws ItemStateException;
+
+        /**
+         * End this update operation. After having invoked this operation,
+         * the update object should be considered invalid.
+         *
+         * @throws ItemStateException if an error occurs
+         */
+        public void end() throws ItemStateException;
+    }
+}

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/XASessionImpl.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/tx/XASessionImpl.java	Thu Oct 21 10:17:01 2004
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.tx;
+
+import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.config.WorkspaceConfig;
+import org.apache.jackrabbit.core.observation.EventStateCollection;
+import org.apache.jackrabbit.core.state.SessionItemStateManager;
+import org.apache.log4j.Logger;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.xa.XASession;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+/**
+ * Session extension that provides XA support.
+ */
+public class XASessionImpl extends SessionImpl
+        implements XASession, XAResource {
+
+    /**
+     * Logger instance
+     */
+    private static final Logger log = Logger.getLogger(XASessionImpl.class);
+
+    /**
+     * Known attribute name
+     */
+    private static final String ATTRIBUTE_ITEM_STATE_MANAGER = "ItemStateManager";
+
+    /**
+     * Known attribute name
+     */
+    private static final String ATTRIBUTE_HIERARCHY_MANAGER = "HierarchyManager";
+
+    /**
+     * Known attribute name
+     */
+    private static final String ATTRIBUTE_ITEM_MANAGER = "ItemManager";
+
+    /**
+     * Known attribute name
+     */
+    private static final String ATTRIBUTE_ACCESS_MANAGER = "AccessManager";
+
+    /**
+     * Transaction manager
+     */
+    private final TransactionManager txManager;
+
+    /**
+     * Transaction timeout, in seconds
+     */
+    private int txTimeout;
+
+    /**
+     * Currently associated transaction
+     */
+    private Transaction tx;
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param rep         repository
+     * @param credentials credentials
+     * @param wspConfig   workspace configuration
+     * @param txManager   transaction manager
+     * @throws RepositoryException if an error occurs
+     */
+    public XASessionImpl(RepositoryImpl rep, Credentials credentials,
+                         WorkspaceConfig wspConfig,
+                         TransactionManager txManager)
+            throws RepositoryException {
+
+        super(rep, credentials, wspConfig);
+
+        this.txManager = txManager;
+    }
+
+    //-------------------------------------------------------------< XASession >
+
+    /**
+     * @see XASession#getXAResource
+     */
+    public XAResource getXAResource() {
+        return this;
+    }
+
+    //------------------------------------------------------------< XAResource >
+
+    /**
+     * @see XAResource#getTransactionTimeout
+     */
+    public int getTransactionTimeout() throws XAException {
+        return txTimeout;
+    }
+
+    /**
+     * @see XAResource#setTransactionTimeout
+     */
+    public boolean setTransactionTimeout(int seconds) throws XAException {
+        txTimeout = seconds;
+        return true;
+    }
+
+    /**
+     * @see XAResource#isSameRM
+     *      <p/>
+     *      Two resources belong to the same resource manager if both connections
+     *      (i.e. sessions) have the same credentials.
+     */
+    public boolean isSameRM(XAResource xares) throws XAException {
+        if (xares instanceof XASessionImpl) {
+            XASessionImpl xases = (XASessionImpl) xares;
+            return stringsEqual(userId, xases.userId);
+        }
+        return false;
+    }
+
+    /**
+     * @see XAResource#start
+     *      <p/>
+     *      If <code>TMNOFLAGS</code> is specified, we create a new transaction
+     *      context and associate it with this resource.
+     *      If <code>TMJOIN</code> is specified, this resource should use the
+     *      same transaction context as another, already known transaction.
+     *      If <code>TMRESUME</code> is specified, we should resume work on
+     *      a transaction context that was suspended earlier.
+     *      All other flags generate an <code>XAException</code> of type
+     *      <code>XAER_INVAL</code>
+     */
+    public void start(Xid xid, int flags) throws XAException {
+        if (isAssociated()) {
+            log.error("Resource already associated with a transaction.");
+            throw new XAException(XAException.XAER_PROTO);
+        }
+
+        Transaction tx = null;
+        if (flags == TMNOFLAGS) {
+            tx = txManager.getTransaction(xid);
+            if (tx != null) {
+                throw new XAException(XAException.XAER_DUPID);
+            }
+            try {
+                tx = beginTransaction(xid);
+            } catch (TransactionException e) {
+                log.error("Unable to begin transaction.", e);
+                throw new XAException(XAException.XAER_RMERR);
+            }
+        } else if (flags == TMJOIN) {
+            tx = txManager.getTransaction(xid);
+            if (tx == null) {
+                throw new XAException(XAException.XAER_NOTA);
+            }
+        } else if (flags == TMRESUME) {
+            tx = txManager.getTransaction(xid);
+            if (tx == null) {
+                throw new XAException(XAException.XAER_NOTA);
+            }
+        } else {
+            throw new XAException(XAException.XAER_INVAL);
+        }
+
+        associate(tx);
+    }
+
+    /**
+     * Begin a global transaction. Rebuilds all transaction-local
+     * objects and stores them as attributes of the newly created transaction.
+     *
+     * @param xid xid of global transaction.
+     * @return transaction
+     */
+    private Transaction beginTransaction(Xid xid) throws TransactionException {
+        TransactionalStore store = new DefaultTransactionalStore(wsp.getPersistentStateManager());
+
+        Transaction tx = txManager.beginTransaction(xid, store);
+
+        SessionItemStateManager itemStateMgr =
+                createSessionItemStateManager(new TransactionalItemStateManager(tx, store));
+        tx.setAttribute(ATTRIBUTE_ITEM_STATE_MANAGER, itemStateMgr);
+
+        HierarchyManager hierMgr = itemStateMgr.getHierarchyMgr();
+        tx.setAttribute(ATTRIBUTE_HIERARCHY_MANAGER, hierMgr);
+
+        ItemManager itemMgr = createItemManager(itemStateMgr, hierMgr);
+        tx.setAttribute(ATTRIBUTE_ITEM_MANAGER, itemMgr);
+
+        AccessManagerImpl accessMgr = createAccessManager(null, hierMgr);
+        tx.setAttribute(ATTRIBUTE_ACCESS_MANAGER, accessMgr);
+
+        return tx;
+    }
+
+    /**
+     * @see XAResource#end
+     *      <p/>
+     *      If <code>TMSUCCESS</code> is specified, we disassociate this session
+     *      from the transaction specified.
+     *      If <code>TMFAIL</code> is specified, we disassociate this session from
+     *      the transaction specified and mark the transaction rollback only.
+     *      If <code>TMSUSPEND</code> is specified, we disassociate this session
+     *      from the transaction specified.
+     *      All other flags generate an <code>XAException</code> of type
+     *      <code>XAER_INVAL</code>
+     */
+    public void end(Xid xid, int flags) throws XAException {
+        if (!isAssociated()) {
+            log.error("Resource not associated with a transaction.");
+            throw new XAException(XAException.XAER_PROTO);
+        }
+
+        Transaction tx = txManager.getTransaction(xid);
+        if (tx == null) {
+            throw new XAException(XAException.XAER_NOTA);
+        }
+        if (flags == TMSUCCESS) {
+            disassociate();
+        } else if (flags == TMFAIL) {
+            disassociate();
+            try {
+                tx.setRollbackOnly();
+            } catch (TransactionException e) {
+                log.error("Unable to set rollback only.", e);
+                throw new XAException(XAException.XAER_RMERR);
+            }
+        } else if (flags == TMSUSPEND) {
+            disassociate();
+        } else {
+            throw new XAException(XAException.XAER_INVAL);
+        }
+    }
+
+    /**
+     * @see XAResource#prepare
+     */
+    public synchronized int prepare(Xid xid) throws XAException {
+        Transaction tx = txManager.getTransaction(xid);
+        if (tx == null) {
+            throw new XAException(XAException.XAER_NOTA);
+        }
+        try {
+            tx.prepare();
+        } catch (TransactionException e) {
+            log.error("Unable to prepare transaction.", e);
+            throw new XAException(XAException.XAER_RMERR);
+        }
+        return XA_OK;
+    }
+
+    /**
+     * @see XAResource#rollback
+     */
+    public void rollback(Xid xid) throws XAException {
+        Transaction tx = txManager.getTransaction(xid);
+        if (tx == null) {
+            throw new XAException(XAException.XAER_NOTA);
+        }
+        try {
+            tx.rollback();
+        } catch (TransactionException e) {
+            log.error("Unable to rollback transaction.", e);
+            throw new XAException(XAException.XAER_RMERR);
+        }
+    }
+
+    /**
+     * @see XAResource#commit
+     */
+    public void commit(Xid xid, boolean onePhase) throws XAException {
+        Transaction tx = txManager.getTransaction(xid);
+        if (tx == null) {
+            throw new XAException(XAException.XAER_NOTA);
+        }
+
+        if (onePhase) {
+            try {
+                tx.prepare();
+            } catch (TransactionException e) {
+                log.error("Unable to prepare one phase transaction.", e);
+                throw new XAException(XAException.XAER_RMERR);
+            }
+        }
+
+        try {
+            tx.commit();
+        } catch (TransactionException e) {
+            log.error("Unable to commit transaction.", e);
+            throw new XAException(XAException.XAER_RMERR);
+        }
+    }
+
+    /**
+     * @see XAResource#recover
+     *      <p/>
+     *      No recovery support yet.
+     */
+    public Xid[] recover(int flags) throws XAException {
+        return new Xid[0];
+    }
+
+    /**
+     * @see XAResource#recover
+     *      <p/>
+     *      No recovery support yet.
+     */
+    public void forget(Xid xid) throws XAException {
+    }
+
+    /**
+     * Associate this session with a global transaction. Internally, set
+     * the transaction containing all transaction-local objects to be
+     * used when performing item retrieval and store.
+     */
+    void associate(Transaction tx) {
+        this.tx = tx;
+    }
+
+    /**
+     * Return a flag indicating whether this resource is associated
+     * with a transaction.
+     *
+     * @return <code>true</code> if this resource is associated
+     *         with a transaction; otherwise <code>false</code>
+     */
+    boolean isAssociated() {
+        return tx != null;
+    }
+
+    /**
+     * Disassociate this session from a global transaction. Internally,
+     * clear the transaction object.
+     */
+    void disassociate() {
+        tx = null;
+    }
+
+    /**
+     * @see SessionImpl#getItemManager
+     *      <p/>
+     *      Has to be overridden to provide the session item state manager
+     *      based on items contained in the current transaction
+     */
+    protected SessionItemStateManager getItemStateManager() {
+        if (tx != null) {
+            return (SessionItemStateManager) tx.getAttribute(ATTRIBUTE_ITEM_STATE_MANAGER);
+        }
+        return super.getItemStateManager();
+    }
+
+    /**
+     * @see SessionImpl#getItemManager
+     *      <p/>
+     *      Has to be overridden to provide the hierarchy manager
+     *      based on items contained in the current transaction
+     */
+    protected HierarchyManager getHierarchyManager() {
+        if (tx != null) {
+            return (HierarchyManager) tx.getAttribute(ATTRIBUTE_HIERARCHY_MANAGER);
+        }
+        return super.getHierarchyManager();
+    }
+
+    /**
+     * @see SessionImpl#getItemManager
+     *      <p/>
+     *      Has to be overridden to provide the item manager based on items
+     *      contained in the current transaction
+     */
+    protected ItemManager getItemManager() {
+        if (tx != null) {
+            return (ItemManager) tx.getAttribute(ATTRIBUTE_ITEM_MANAGER);
+        }
+        return super.getItemManager();
+    }
+
+    /**
+     * @see SessionImpl#getAccessManager
+     *      <p/>
+     *      Has to be overridden to provide the access manager based on items
+     *      contained in the current transaction
+     */
+    protected AccessManagerImpl getAccessManager() {
+        if (tx != null) {
+            return (AccessManagerImpl) tx.getAttribute(ATTRIBUTE_ACCESS_MANAGER);
+        }
+        return super.getAccessManager();
+    }
+
+
+    /**
+     * @see SessionImpl#dispatch
+     *      <p/>
+     *      If we are currently associated with a transaction, the dispatch operation
+     *      will be postponed until commit.
+     */
+    protected void dispatch(EventStateCollection events) {
+        if (tx != null) {
+            tx.addListener(new EventDispatcher(events));
+            return;
+        }
+        super.dispatch(events);
+    }
+
+    /**
+     * Internal {@link TransactionListener} implementation that will dispatch
+     * events only when a transaction has actually been committed.
+     */
+    static class EventDispatcher implements TransactionListener {
+
+        /**
+         * Events to dispatch if transaction is committed
+         */
+        private final EventStateCollection events;
+
+        /**
+         * Create a new instance of this class.
+         *
+         * @param events events to dispatch on commit
+         */
+        public EventDispatcher(EventStateCollection events) {
+            this.events = events;
+        }
+
+        /**
+         * @see TransactionListener#transactionCommitted
+         *      <p/>
+         *      Dispatch events.
+         */
+        public void transactionCommitted(Transaction tx) {
+            events.dispatch();
+        }
+
+        /**
+         * @see TransactionListener#transactionCommitted
+         *      <p/>
+         *      Nothing to do.
+         */
+        public void transactionRolledBack(Transaction tx) {
+        }
+    }
+
+    /**
+     * Compare two strings for equality. If both are <code>null</code>, this
+     * is also considered to be equal.
+     */
+    private static boolean stringsEqual(String s1, String s2) {
+        return s1 == null ? s2 == null : s1.equals(s2);
+    }
+}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java	Thu Oct 21 10:17:01 2004
@@ -15,7 +15,10 @@
  */
 package org.apache.jackrabbit.core.state.xml;
 
-import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.BLOBFileValue;
+import org.apache.jackrabbit.core.InternalValue;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.QName;
 import org.apache.jackrabbit.core.config.WorkspaceConfig;
 import org.apache.jackrabbit.core.fs.*;
 import org.apache.jackrabbit.core.fs.FileSystem;
@@ -26,8 +29,6 @@
 import org.apache.log4j.Logger;
 import org.jdom.Element;
 import org.jdom.JDOMException;
-import org.jdom.filter.ContentFilter;
-import org.jdom.filter.Filter;
 import org.jdom.input.SAXBuilder;
 
 import javax.jcr.PropertyType;
@@ -73,8 +74,6 @@
     private static final String NAME_ATTRIBUTE = "name";
     private static final String TYPE_ATTRIBUTE = "type";
     private static final String COUNT_ATTRIBUTE = "count";
-    private static final String VALUES_ELEMENT = "values";
-    private static final String VALUE_ELEMENT = "value";
 
     private static final String NODES_ELEMENT = "nodes";
 
@@ -228,79 +227,6 @@
         }
     }
 
-    private void readState(Element propElement, PersistentPropertyState state)
-            throws ItemStateException {
-        // first do some paranoid sanity checks
-        if (!propElement.getName().equals(PROPERTY_ELEMENT)) {
-            String msg = "invalid serialization format (unexpected element: " + propElement.getName() + ")";
-            log.error(msg);
-            throw new ItemStateException(msg);
-        }
-        // check name
-        if (!state.getName().equals(QName.valueOf(propElement.getAttributeValue(NAME_ATTRIBUTE)))) {
-            String msg = "invalid serialized state: name mismatch";
-            log.error(msg);
-            throw new ItemStateException(msg);
-        }
-        // check parentUUID
-        String parentUUID = propElement.getAttributeValue(PARENTUUID_ATTRIBUTE);
-        if (!parentUUID.equals(state.getParentUUID())) {
-            String msg = "invalid serialized state: parentUUID mismatch";
-            log.error(msg);
-            throw new ItemStateException(msg);
-        }
-
-        // now we're ready to read state
-
-        // type
-        String typeName = propElement.getAttributeValue(TYPE_ATTRIBUTE);
-        int type;
-        try {
-            type = PropertyType.valueFromName(typeName);
-        } catch (IllegalArgumentException iae) {
-            // should never be getting here
-            throw new ItemStateException("unexpected property-type: " + typeName, iae);
-        }
-        state.setType(type);
-
-        // definition id
-        String definitionId = propElement.getAttributeValue(DEFINITIONID_ATTRIBUTE);
-        state.setDefinitionId(PropDefId.valueOf(definitionId));
-
-        // values
-        Iterator iter = propElement.getChild(VALUES_ELEMENT).getChildren(VALUE_ELEMENT).iterator();
-        ArrayList values = new ArrayList();
-        while (iter.hasNext()) {
-            Element valueElement = (Element) iter.next();
-            Filter filter = new ContentFilter(ContentFilter.TEXT | ContentFilter.CDATA);
-            List content = valueElement.getContent(filter);
-
-            InternalValue val;
-            if (!content.isEmpty()) {
-                // read serialized value
-                String text = valueElement.getTextTrim();
-                if (type == PropertyType.BINARY) {
-                    // special handling required for binary value:
-                    // the value stores the path to the actual binary file in the blob store
-                    try {
-                        val = InternalValue.create(new FileSystemResource(blobStore, text));
-                    } catch (IOException ioe) {
-                        String msg = "error while reading serialized binary valuey";
-                        log.error(msg, ioe);
-                        throw new ItemStateException(msg, ioe);
-                    }
-                } else {
-                    val = InternalValue.valueOf(text, type);
-                }
-            } else {
-                // null value
-                val = null;
-            }
-            values.add(val);
-        }
-        state.setValues((InternalValue[]) values.toArray(new InternalValue[values.size()]));
-    }
-
     private void readState(Properties props, PersistentPropertyState state)
             throws ItemStateException {
         // type
@@ -380,16 +306,19 @@
     }
 
     /**
-     * @see PersistenceManager#loadNodeState
+     * @see PersistenceManager#load(PersistentNodeState)
      */
-    public synchronized PersistentNodeState loadNodeState(String uuid)
+    public synchronized void load(PersistentNodeState state)
             throws NoSuchItemStateException, ItemStateException {
+
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
 
         Exception e = null;
+        String uuid = state.getUUID();
         String nodeFilePath = buildNodeFilePath(uuid);
+
         try {
             if (!itemStateStore.isFile(nodeFilePath)) {
                 throw new NoSuchItemStateException(uuid);
@@ -400,53 +329,11 @@
                 SAXBuilder builder = new SAXBuilder();
                 Element rootElement = builder.build(in).getRootElement();
                 String ntName = rootElement.getAttributeValue(NODETYPE_ATTRIBUTE);
-
-                PersistentNodeState state = createNodeStateInstance(uuid, QName.valueOf(ntName));
-                readState(rootElement, state);
-
-                return state;
-            } finally {
-                in.close();
-            }
-        } catch (JDOMException jde) {
-            e = jde;
-            // fall through
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-        String msg = "failed to read node state: " + uuid;
-        log.error(msg, e);
-        throw new ItemStateException(msg, e);
-    }
-
-    /**
-     * @see PersistenceManager#reload
-     */
-    public synchronized void reload(PersistentNodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        Exception e = null;
-        String uuid = state.getUUID();
-        String nodeFilePath = buildNodeFilePath(uuid);
-        try {
-            InputStream in = itemStateStore.getInputStream(nodeFilePath);
-            try {
-                SAXBuilder builder = new SAXBuilder();
-                Element rootElement = builder.build(in).getRootElement();
-
-                // reset state
-                state.removeAllParentUUIDs();
-                state.removeAllPropertyEntries();
-                state.removeAllChildNodeEntries();
+                state.setNodeTypeName(QName.valueOf(ntName));
 
                 readState(rootElement, state);
                 return;
+
             } finally {
                 in.close();
             }
@@ -466,78 +353,11 @@
     }
 
     /**
-     * @see PersistenceManager#loadPropertyState
+     * @see PersistenceManager#load(PersistentPropertyState)
      */
-    public synchronized PersistentPropertyState loadPropertyState(String parentUUID, QName propName)
+    public synchronized void load(PersistentPropertyState state)
             throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
 
-        Exception e = null;
-        String propFilePath = buildPropFilePath(parentUUID, propName);
-/*
-	// read property state from xml file
-        try {
-	    if (!fs.isFile(propFilePath)) {
-		throw new NoSuchItemStateException(parentUUID + "/" + propName);
-	    }
-	    InputStream in = fs.getInputStream(propFilePath);
-	    try {
-		SAXBuilder builder = new SAXBuilder();
-		Element rootElement = builder.build(in).getRootElement();
-
-		PersistentPropertyState state = createPropertyStateInstance(propName, parentUUID);
-		readState(rootElement, state);
-
-		return state;
-	    } finally {
-		in.close();
-	    }
-	} catch (JDOMException jde) {
-	    e = jde;
-	    // fall through
-	} catch (IOException ioe) {
-	    e = ioe;
-	    // fall through
-	} catch (FileSystemException fse) {
-	    e = fse;
-	    // fall through
-	}
-*/
-        // read property state from java.util.Properties file
-        try {
-            if (!itemStateStore.isFile(propFilePath)) {
-                throw new NoSuchItemStateException(parentUUID + "/" + propName);
-            }
-            InputStream in = itemStateStore.getInputStream(propFilePath);
-            try {
-                Properties props = new Properties();
-                props.load(in);
-                PersistentPropertyState state = createPropertyStateInstance(propName, parentUUID);
-                readState(props, state);
-
-                return state;
-            } finally {
-                in.close();
-            }
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-
-        String msg = "failed to read property state: " + parentUUID + "/" + propName;
-        log.error(msg, e);
-        throw new ItemStateException(msg, e);
-    }
-
-    /**
-     * @see PersistenceManager#reload
-     */
-    public synchronized void reload(PersistentPropertyState state) throws ItemStateException {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
@@ -546,30 +366,7 @@
         String parentUUID = state.getParentUUID();
         QName propName = state.getName();
         String propFilePath = buildPropFilePath(parentUUID, propName);
-/*
-	// read property state from xml file
-        try {
-            InputStream in = itemStateStore.getInputStream(propFilePath);
-            try {
-                SAXBuilder builder = new SAXBuilder();
-                Element rootElement = builder.build(in).getRootElement();
-                readState(rootElement, state);
-                return;
-            } finally {
-                in.close();
-            }
-        } catch (JDOMException jde) {
-            e = jde;
-            // fall through
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-*/
-        // read property state from java.util.Properties file
+
         try {
             if (!itemStateStore.isFile(propFilePath)) {
                 throw new NoSuchItemStateException(parentUUID + "/" + propName);
@@ -580,6 +377,7 @@
                 props.load(in);
                 readState(props, state);
                 return;
+
             } finally {
                 in.close();
             }
@@ -937,95 +735,23 @@
     }
 
     /**
-     * @see PersistenceManager#createNodeStateInstance
-     */
-    public PersistentNodeState createNodeStateInstance(String uuid, QName nodeTypeName) {
-        return new XMLNodeState(uuid, nodeTypeName, null, this);
-    }
-
-    /**
-     * @see PersistenceManager#createPropertyStateInstance
-     */
-    public PersistentPropertyState createPropertyStateInstance(QName name, String parentUUID) {
-        return new XMLPropertyState(name, parentUUID, this);
-    }
-
-    /**
-     * @see PersistenceManager#createNodeReferencesInstance(String)
-     */
-    public NodeReferences createNodeReferencesInstance(String uuid) {
-        return new NodeReferences(new NodeId(uuid));
-    }
-
-    /**
-     * @see PersistenceManager#loadNodeReferences(String uuid)
+     * @see PersistenceManager#load(NodeReferences)
      */
-    public NodeReferences loadNodeReferences(String uuid) throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        Exception e = null;
-        String refsFilePath = buildNodeReferencesFilePath(uuid);
-        try {
-            if (!itemStateStore.isFile(refsFilePath)) {
-                throw new NoSuchItemStateException(uuid);
-            }
-            NodeReferences refs = createNodeReferencesInstance(uuid);
-
-            InputStream in = itemStateStore.getInputStream(refsFilePath);
-            BufferedReader reader = null;
-            try {
-                String encoding = DEFAULT_ENCODING;
-                try {
-                    reader = new BufferedReader(new InputStreamReader(in, encoding));
-                } catch (UnsupportedEncodingException uee) {
-                    // should never get here!
-                    InputStreamReader isw = new InputStreamReader(in);
-                    encoding = isw.getEncoding();
-                    reader = new BufferedReader(isw);
-                }
-                // read references (i.e. the id's of the REFERENCE properties)
-                String s;
-                while ((s = reader.readLine()) != null) {
-                    if (s.length() > 0) {
-                        PropertyId propId = PropertyId.valueOf(s);
-                        refs.addReference(propId);
-                    }
-                }
-            } finally {
-                reader.close();
-            }
-
-            return refs;
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-        String msg = "failed to load references: " + uuid;
-        log.error(msg, e);
-        throw new ItemStateException(msg, e);
-    }
+    public void load(NodeReferences refs)
+            throws NoSuchItemStateException, ItemStateException {
 
-    /**
-     * @see PersistenceManager#reload(NodeReferences)
-     */
-    public void reload(NodeReferences refs) throws ItemStateException {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
 
         Exception e = null;
         String uuid = refs.getTargetId().getUUID();
+
         String refsFilePath = buildNodeReferencesFilePath(uuid);
         try {
             if (!itemStateStore.isFile(refsFilePath)) {
                 throw new NoSuchItemStateException(uuid);
             }
-
             refs.clearAllReferences();
 
             InputStream in = itemStateStore.getInputStream(refsFilePath);
@@ -1048,11 +774,11 @@
                         refs.addReference(propId);
                     }
                 }
+                return;
+
             } finally {
                 reader.close();
             }
-
-            return;
         } catch (IOException ioe) {
             e = ioe;
             // fall through

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/util/Text.java	Thu Oct 21 10:17:01 2004
@@ -16,8 +16,8 @@
 package org.apache.jackrabbit.core.util;
 
 import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
 import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 
 /**
  * This Class provides some text related utilities

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalFrozenNode.java	Thu Oct 21 10:17:01 2004
@@ -55,17 +55,17 @@
     /**
      * the frozen uuid of the original node
      */
-    private String frozenUUID=null;
+    private String frozenUUID = null;
 
     /**
      * the frozen primary type of the orginal node
      */
-    private QName frozenPrimaryType=null;
+    private QName frozenPrimaryType = null;
 
     /**
      * the frozen list of mixin types of the original node
      */
-    private QName[] frozenMixinTypes=null;
+    private QName[] frozenMixinTypes = null;
 
     /**
      * Creates a new frozen node based on the given persistance node.
@@ -109,10 +109,10 @@
         frozenProperties = (PersistentProperty[]) propList.toArray(new PersistentProperty[propList.size()]);
 
         // do some checks
-        if (frozenMixinTypes==null) {
-            frozenMixinTypes=new QName[0];
+        if (frozenMixinTypes == null) {
+            frozenMixinTypes = new QName[0];
         }
-        if (frozenPrimaryType==null) {
+        if (frozenPrimaryType == null) {
             throw new RepositoryException("Illegal frozen node. Must have 'frozenPrimaryType'");
         }
         // init the frozen child nodes

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/PersistentNode.java	Thu Oct 21 10:17:01 2004
@@ -43,7 +43,7 @@
     /**
      * the state manager
      */
-    private final PersistentItemStateManager stateMgr;
+    private final PersistentItemStateProvider stateMgr;
 
     /**
      * the node type manager
@@ -62,7 +62,7 @@
      * @param ntMgr
      * @param nodeState
      */
-    protected PersistentNode(PersistentItemStateManager statemgr,
+    protected PersistentNode(PersistentItemStateProvider statemgr,
                              NodeTypeManagerImpl ntMgr,
                              PersistentNodeState nodeState) {
         this.nodeState = nodeState;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java	Thu Oct 21 10:17:01 2004
@@ -17,10 +17,10 @@
 
 import org.apache.jackrabbit.core.*;
 
+import javax.jcr.Item;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Item;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionHistory;
 import javax.jcr.version.VersionIterator;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionImpl.java	Thu Oct 21 10:17:01 2004
@@ -17,10 +17,10 @@
 
 import org.apache.jackrabbit.core.NodeImpl;
 
+import javax.jcr.Item;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Item;
 import javax.jcr.version.Version;
 import java.util.Calendar;
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java	Thu Oct 21 10:17:01 2004
@@ -19,7 +19,7 @@
 import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.PersistentItemStateManager;
+import org.apache.jackrabbit.core.state.PersistentItemStateProvider;
 import org.apache.jackrabbit.core.state.PersistentNodeState;
 import org.apache.log4j.Logger;
 
@@ -119,7 +119,7 @@
     /**
      * the state manager for the version storage
      */
-    private PersistentItemStateManager stateMgr;
+    private PersistentItemStateProvider stateMgr;
 
     /**
      * the nodetype manager for the version storage

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java	Thu Oct 21 10:17:01 2004
@@ -52,6 +52,7 @@
      * Stores character data encountered in <code>{@link #characters(char[], int, int)}</code>
      * as <code>jcr:xmlcharacters</code> property of <code>jcr:xmltext</code>
      * child node.
+     *
      * @param parent
      * @param text
      * @throws SAXException

Mime
View raw message