You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2008/04/14 14:35:36 UTC

svn commit: r647767 [1/2] - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core: ./ version/

Author: dpfister
Date: Mon Apr 14 05:35:33 2008
New Revision: 647767

URL: http://svn.apache.org/viewvc?rev=647767&view=rev
Log:
JCR-1104 - JSR 283 support
- shareble nodes (work in progress)
- prepare for returning stable paths on shareable nodes or their descendants

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/AbstractNodeData.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemData.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeData.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeDataRef.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyData.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/VersionImpl.java

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/AbstractNodeData.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/AbstractNodeData.java?rev=647767&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/AbstractNodeData.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/AbstractNodeData.java Mon Apr 14 05:35:33 2008
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import javax.jcr.nodetype.NodeDefinition;
+import org.apache.jackrabbit.core.state.NodeState;
+
+/**
+ * Data object representing a node.
+ */
+public abstract class AbstractNodeData extends ItemData {
+
+    /** Primary parent id of a shareable node. */
+    private NodeId primaryParentId;
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param state node state
+     * @param definition node definition
+     */
+    protected AbstractNodeData(NodeState state, NodeDefinition definition) {
+        super(state, definition);
+
+        if (state.isShareable()) {
+            this.primaryParentId = state.getParentId();
+        }
+    }
+
+    /**
+     * Create a new instance of this class.
+     */
+    protected AbstractNodeData() {
+    }
+
+    /**
+     * Return the associated node state.
+     *
+     * @return node state
+     */
+    public NodeState getNodeState() {
+        return (NodeState) getState();
+    }
+
+    /**
+     * Return the associated node defintion.
+     *
+     * @return node defintion
+     */
+    public NodeDefinition getNodeDefinition() {
+        return (NodeDefinition) getDefinition();
+    }
+
+    /**
+     * Return the parent id of this node. Every shareable node in a shared set
+     * has a different parent.
+     *
+     * @return parent id
+     */
+    public NodeId getParentId() {
+        if (primaryParentId != null) {
+            return primaryParentId;
+        }
+        return getState().getParentId();
+    }
+
+    /**
+     * Return the primary parent id of this node. Every shareable node in a
+     * shared set has a different primary parent. Returns <code>null</code>
+     * for nodes that are not shareable.
+     *
+     * @return primary parent id or <code>null</code>
+     */
+    public NodeId getPrimaryParentId() {
+        return primaryParentId;
+    }
+
+    /**
+     * Set the primary parent id of this node.
+     *
+     * @param primaryParentId primary parent id
+     */
+    protected void setPrimaryParentId(NodeId primaryParentId) {
+        this.primaryParentId = primaryParentId;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isNode() {
+        return true;
+    }
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemData.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemData.java?rev=647767&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemData.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemData.java Mon Apr 14 05:35:33 2008
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import javax.jcr.nodetype.ItemDefinition;
+import org.apache.jackrabbit.core.state.ItemState;
+
+/**
+ * Data object referenced by different <code>ItemImpl</code> instances that
+ * all represent the same item, i.e. items having the same <code>ItemId</code>.
+ */
+public abstract class ItemData {
+
+    /** Associated item state */
+    protected ItemState state;
+
+    /** Associated item definition */
+    protected ItemDefinition definition;
+
+    /** Status */
+    protected int status;
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param state item state
+     * @param definition item definition
+     */
+    protected ItemData(ItemState state, ItemDefinition definition) {
+        this.state = state;
+        this.definition = definition;
+    }
+
+    /**
+     * Create a new instance of this class.
+     */
+    protected ItemData() {
+    }
+
+    /**
+     * Return the associated item state.
+     *
+     * @return item state
+     */
+    public ItemState getState() {
+        return state;
+    }
+
+    /**
+     * Set the associated item state.
+     *
+     * @param state item state
+     */
+    protected void setState(ItemState state) {
+        this.state = state;
+    }
+
+    /**
+     * Return the associated item definition.
+     *
+     * @return item definition
+     */
+    public ItemDefinition getDefinition() {
+        return definition;
+    }
+
+    /**
+     * Set the associated item definition.
+     *
+     * @param definition item definition
+     */
+    protected void setDefinition(ItemDefinition definition) {
+        this.definition = definition;
+    }
+
+    /**
+     * Return the status.
+     *
+     * @return status
+     */
+    public int getStatus() {
+        return status;
+    }
+
+    /**
+     * Set the status.
+     *
+     * @param status
+     */
+    protected void setStatus(int status) {
+        this.status = status;
+    }
+
+    /**
+     * Return a flag indicating whether item is a node.
+     *
+     * @return <code>true</code> if this item is a node;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isNode() {
+        return false;
+    }
+
+    /**
+     * Return the id associated with this item.
+     *
+     * @return item id
+     */
+    public ItemId getId() {
+        return getState().getId();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return getId().toString();
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java?rev=647767&r1=647766&r2=647767&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java Mon Apr 14 05:35:33 2008
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.core;
 
 import org.apache.commons.collections.iterators.IteratorChain;
-import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
 import org.apache.jackrabbit.core.nodetype.NodeDef;
 import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
@@ -65,10 +64,8 @@
 import javax.jcr.version.VersionHistory;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -83,7 +80,7 @@
     protected static final int STATUS_DESTROYED = 2;
     protected static final int STATUS_INVALIDATED = 3;
 
-    protected int status;
+    //protected int status;
 
     protected final ItemId id;
 
@@ -98,9 +95,9 @@
     protected final RepositoryImpl rep;
 
     /**
-     * <code>ItemState</code> associated with this <code>Item</code>
+     * Item data associated with this item.
      */
-    protected ItemState state;
+    protected final ItemData data;
 
     /**
      * <code>ItemManager</code> that created this <code>Item</code>
@@ -113,12 +110,6 @@
     protected final SessionItemStateManager stateMgr;
 
     /**
-     * Listeners (weak references)
-     */
-    protected final Map listeners =
-            Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
-
-    /**
      * Package private constructor.
      *
      * @param itemMgr   the <code>ItemManager</code> that created this <code>Item</code>
@@ -127,21 +118,14 @@
      * @param state     state associated with this <code>Item</code>
      * @param listeners listeners on life cycle changes of this <code>ItemImpl</code>
      */
-    ItemImpl(ItemManager itemMgr, SessionImpl session, ItemId id, ItemState state,
-             ItemLifeCycleListener[] listeners) {
+    ItemImpl(ItemManager itemMgr, SessionImpl session, ItemData data) {
         this.session = session;
         rep = (RepositoryImpl) session.getRepository();
         stateMgr = session.getItemStateManager();
-        this.id = id;
+        this.id = data.getId();
         this.itemMgr = itemMgr;
-        this.state = state;
-        status = STATUS_NORMAL;
-
-        if (listeners != null) {
-            for (int i = 0; i < listeners.length; i++) {
-                addLifeCycleListener(listeners[i]);
-            }
-        }
+        this.data = data;
+        data.setStatus(STATUS_NORMAL);
     }
 
     /**
@@ -154,13 +138,14 @@
         session.sanityCheck();
 
         // check status of this item for read operation
+        final int status = data.getStatus();
         if (status == STATUS_DESTROYED || status == STATUS_INVALIDATED) {
             throw new InvalidItemStateException(id + ": the item does not exist anymore");
         }
     }
 
     protected boolean isTransient() {
-        return state.isTransient();
+        return getItemState().isTransient();
     }
 
     protected abstract ItemState getOrCreateTransientItemState() throws RepositoryException;
@@ -175,6 +160,7 @@
      * @throws RepositoryException if an error occurs
      */
     protected void setRemoved() throws RepositoryException {
+        final int status = data.getStatus();
         if (status == STATUS_INVALIDATED || status == STATUS_DESTROYED) {
             // this instance is already 'invalid', get outta here
             return;
@@ -194,10 +180,10 @@
             stateMgr.moveTransientItemStateToAttic(transientState);
 
             // set state of this instance to 'invalid'
-            status = STATUS_INVALIDATED;
-            // notify the listeners that this instance has been
+            data.setStatus(STATUS_INVALIDATED);
+            // notify the manager that this instance has been
             // temporarily invalidated
-            notifyInvalidated();
+            itemMgr.itemInvalidated(id, data);
         }
     }
 
@@ -207,74 +193,7 @@
      * @return state associated with this <code>Item</code>
      */
     ItemState getItemState() {
-        return state;
-    }
-
-    /**
-     * Notify the listeners that this instance has been created.
-     */
-    protected void notifyCreated() {
-        // copy listeners to array to avoid ConcurrentModificationException
-        ItemLifeCycleListener[] la =
-                (ItemLifeCycleListener[]) listeners.values().toArray(
-                        new ItemLifeCycleListener[listeners.size()]);
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].itemCreated(this);
-            }
-        }
-    }
-
-    /**
-     * Notify the listeners that this instance has been invalidated
-     * (i.e. it has been temporarily rendered 'invalid').
-     */
-    protected void notifyInvalidated() {
-        // copy listeners to array to avoid ConcurrentModificationException
-        ItemLifeCycleListener[] la =
-                (ItemLifeCycleListener[]) listeners.values().toArray(
-                        new ItemLifeCycleListener[listeners.size()]);
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].itemInvalidated(id, this);
-            }
-        }
-    }
-
-    /**
-     * Notify the listeners that this instance has been destroyed
-     * (i.e. it has been permanently rendered 'invalid').
-     */
-    protected void notifyDestroyed() {
-        // copy listeners to array to avoid ConcurrentModificationException
-        ItemLifeCycleListener[] la =
-                (ItemLifeCycleListener[]) listeners.values().toArray(
-                        new ItemLifeCycleListener[listeners.size()]);
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].itemDestroyed(id, this);
-            }
-        }
-    }
-
-    /**
-     * Add an <code>ItemLifeCycleListener</code>
-     *
-     * @param listener the new listener to be informed on life cycle changes
-     */
-    void addLifeCycleListener(ItemLifeCycleListener listener) {
-        if (!listeners.containsKey(listener)) {
-            listeners.put(listener, listener);
-        }
-    }
-
-    /**
-     * Remove an <code>ItemLifeCycleListener</code>
-     *
-     * @param listener an existing listener
-     */
-    void removeLifeCycleListener(ItemLifeCycleListener listener) {
-        listeners.remove(listener);
+        return data.getState();
     }
 
     /**
@@ -353,6 +272,7 @@
         // fail-fast test: check status of this item's state
         if (isTransient()) {
             String msg;
+            final ItemState state = getItemState();
             switch (state.getStatus()) {
                 case ItemState.STATUS_EXISTING_MODIFIED:
                     // add this item's state to the list
@@ -695,15 +615,15 @@
 
         // walk through list of transient items and persist each one
         while (iter.hasNext()) {
-            ItemState itemState = (ItemState) iter.next();
-            ItemImpl item = itemMgr.getItem(itemState);
+            ItemState state = (ItemState) iter.next();
+            ItemImpl item = itemMgr.getItem(state.getId(),
+                    state.getStatus() == ItemState.STATUS_NEW);
             // persist state of transient item
             item.makePersistent();
         }
     }
 
     private void restoreTransientItems(Iterator iter) {
-
         // walk through list of transient states and re-apply transient changes
         while (iter.hasNext()) {
             ItemState itemState = (ItemState) iter.next();
@@ -717,11 +637,7 @@
                     // TransientItemStateManager will bark because of a deleted
                     // state in its attic. We therefore have to forge a new item
                     // instance ourself.
-                    if (itemState.isNode()) {
-                        item = itemMgr.createNodeInstance((NodeState) itemState);
-                    } else {
-                        item = itemMgr.createPropertyInstance((PropertyState) itemState);
-                    }
+                    item = itemMgr.createItemInstance(itemState);
                     itemState.setStatus(ItemState.STATUS_NEW);
                 } else {
                     try {
@@ -730,11 +646,7 @@
                         // itemState probably represents a 'new' item and the
                         // ItemImpl instance wrapping it has already been gc'ed;
                         // we have to re-create the ItemImpl instance
-                        if (itemState.isNode()) {
-                            item = itemMgr.createNodeInstance((NodeState) itemState);
-                        } else {
-                            item = itemMgr.createPropertyInstance((PropertyState) itemState);
-                        }
+                        item = itemMgr.createItemInstance(itemState);
                         itemState.setStatus(ItemState.STATUS_NEW);
                     }
                 }
@@ -943,123 +855,8 @@
      */
     public abstract Name getQName() throws RepositoryException;
 
-    //----------------------------------------------------< ItemStateListener >
-    /**
-     * {@inheritDoc}
-     */
-    public void stateCreated(ItemState created) {
-        status = STATUS_NORMAL;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void stateDestroyed(ItemState destroyed) {
-        if (state == destroyed) {
-            // set state of this instance to 'destroyed'
-            status = STATUS_DESTROYED;
-            // dispose state
-            if (state == destroyed) {
-                state = null;
-            }
-            /**
-             * notify the listeners that this instance has been
-             * permanently invalidated
-             */
-            notifyDestroyed();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void stateModified(ItemState modified) {
-        if (state == modified) {
-            status = STATUS_MODIFIED;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void stateDiscarded(ItemState discarded) {
-        if (state == discarded) {
-            /**
-             * the state of this item has been discarded, probably as a result
-             * of calling Item.refresh(false) or ItemImpl.setRemoved()
-             */
-            if (isTransient()) {
-                switch (state.getStatus()) {
-                    /**
-                     * persistent item that has been transiently removed
-                     */
-                    case ItemState.STATUS_EXISTING_REMOVED:
-                        /**
-                         * persistent item that has been transiently modified
-                         */
-                    case ItemState.STATUS_EXISTING_MODIFIED:
-                        /**
-                         * persistent item that has been transiently modified or removed
-                         * and the underlying persistent state has been externally
-                         * modified since the transient modification/removal.
-                         */
-                    case ItemState.STATUS_STALE_MODIFIED:
-                        ItemState persistentState = state.getOverlayedState();
-                        /**
-                         * the state is a transient wrapper for the underlying
-                         * persistent state, therefore restore the
-                         * persistent state and resurrect this item instance
-                         * if necessary
-                         */
-                        stateMgr.disconnectTransientItemState(state);
-                        state = persistentState;
-
-                        return;
-
-                        /**
-                         * persistent item that has been transiently modified or removed
-                         * and the underlying persistent state has been externally
-                         * destroyed since the transient modification/removal.
-                         */
-                    case ItemState.STATUS_STALE_DESTROYED:
-                        /**
-                         * first notify the listeners that this instance has been
-                         * permanently invalidated
-                         */
-                        notifyDestroyed();
-                        // now set state of this instance to 'destroyed'
-                        status = STATUS_DESTROYED;
-                        state = null;
-                        return;
-
-                        /**
-                         * new item that has been transiently added
-                         */
-                    case ItemState.STATUS_NEW:
-                        /**
-                         * first notify the listeners that this instance has been
-                         * permanently invalidated
-                         */
-                        notifyDestroyed();
-                        // now set state of this instance to 'destroyed'
-                        status = STATUS_DESTROYED;
-                        // finally dispose state
-                        state = null;
-                        return;
-                }
-            }
-
-            /**
-             * first notify the listeners that this instance has been
-             * invalidated
-             */
-            notifyInvalidated();
-            // now render this instance 'invalid'
-            status = STATUS_INVALIDATED;
-        }
-    }
-
     //-----------------------------------------------------------------< Item >
+
     /**
      * {@inheritDoc}
      */
@@ -1086,6 +883,7 @@
      * {@inheritDoc}
      */
     public boolean isNew() {
+        final ItemState state = getItemState();
         return state.isTransient() && state.getOverlayedState() == null;
     }
 
@@ -1095,6 +893,7 @@
      * be saved but not yet persisted.
      */
     protected boolean isTransactionalNew() {
+        final ItemState state = getItemState();
         return state.getStatus() == ItemState.STATUS_NEW;
     }
 
@@ -1102,6 +901,7 @@
      * {@inheritDoc}
      */
     public boolean isModified() {
+        final ItemState state = getItemState();
         return state.isTransient() && state.getOverlayedState() != null;
     }
 
@@ -1371,7 +1171,7 @@
 
         // check status of this item's state
         if (isTransient()) {
-            transientState = state;
+            transientState = getItemState();
             switch (transientState.getStatus()) {
                 case ItemState.STATUS_STALE_MODIFIED:
                 case ItemState.STATUS_STALE_DESTROYED:
@@ -1484,6 +1284,7 @@
         // check state of this instance
         sanityCheck();
 
+        final ItemState state = getItemState();
         if (state.getParentId() == null) {
             // shortcut
             return 0;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java?rev=647767&r1=647766&r2=647767&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemManager.java Mon Apr 14 05:35:33 2008
@@ -76,7 +76,7 @@
  * 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, Dumpable, ItemStateListener {
+public class ItemManager implements Dumpable, ItemStateListener {
 
     private static Logger log = LoggerFactory.getLogger(ItemManager.class);
 
@@ -197,10 +197,7 @@
         // check privileges
         if (!canRead(id)) {
             // clear cache
-            ItemImpl item = retrieveItem(id);
-            if (item != null) {
-                evictItem(id);
-            }
+            evictItems(id);
             throw new AccessDeniedException("cannot read item " + id);
         }
 
@@ -299,7 +296,7 @@
             // check privileges
             if (!canRead(id)) {
                 // clear cache
-                evictItem(id);
+                evictItems(id);
                 // item exists but the session has not been granted read access
                 return false;
             }
@@ -396,24 +393,24 @@
         // check sanity of session
         session.sanityCheck();
 
-        // check cache
-        ItemImpl item = retrieveItem(id);
-        if (item == null) {
+        ItemData data = retrieveItem(id);
+        if (data == null) {
             // not yet in cache, need to create instance:
             // check privileges
             if (!canRead(id)) {
                 throw new AccessDeniedException("cannot read item " + id);
             }
-            // create instance of item
-            item = createItemInstance(id);
+            // create instance of item data
+            data = createItemData(id);
+            cacheItem(data);
         }
-        return item;
+        return createItemInstance(data);
     }
 
     /**
      * Returns a node with a given id and parent id. If the indicated node is
      * shareable, there might be multiple nodes associated with the same id,
-     * but only one node with the given parent id.
+     * but there'is only one node with the given parent id.
      *
      * @param id node id
      * @param parentId parent node id
@@ -422,54 +419,71 @@
      */
     public synchronized NodeImpl getNode(NodeId id, NodeId parentId)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
-        // check sanity of session
-        session.sanityCheck();
 
-        // check shareable nodes
-        NodeImpl node = shareableNodesCache.retrieve(id, parentId);
-        if (node != null) {
-            return node;
+        if (parentId == null) {
+            return (NodeImpl) getItem(id);
         }
-
-        node = (NodeImpl) getItem(id);
-        if (!node.getParentId().equals(parentId)) {
+        AbstractNodeData data = (AbstractNodeData) retrieveItem(id, parentId);
+        if (data == null) {
+            data = (AbstractNodeData) createItemData(id);
+            cacheItem(data);
+        }
+        if (!data.getParentId().equals(parentId)) {
             // verify that parent actually appears in the shared set
-            if (!node.hasShareParent(parentId)) {
+            if (!data.getNodeState().containsShare(parentId)) {
                 String msg = "Node with id '" + id
                         + "' does not have shared parent with id: " + parentId;
                 throw new ItemNotFoundException(msg);
             }
-
-            node = new NodeImpl(node, parentId, new ItemLifeCycleListener[] { this });
-            node.notifyCreated();
+            data = new NodeDataRef(data, parentId);
+            cacheItem(data);
         }
-        return node;
+        return createNodeInstance(data);
     }
 
     /**
-     * Returns the item instance for the given item state.
+     * Returns the item instance for the given item id.
+     *
      * @param state the item state
+     * @param checkAccess whether to check access
      * @return the item instance for the given item <code>state</code>.
      * @throws RepositoryException
      */
-    public synchronized ItemImpl getItem(ItemState state)
+    synchronized ItemImpl getItem(ItemId id, boolean isNew)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
         // check sanity of session
         session.sanityCheck();
 
-        ItemId id = state.getId();
         // check cache
-        ItemImpl item = retrieveItem(id);
-        if (item == null) {
+        ItemData data = retrieveItem(id);
+        if (data == null) {
             // not yet in cache, need to create instance:
             // only check privileges if state is not new
-            if (state.getStatus() != ItemState.STATUS_NEW && !canRead(id)) {
+            if (!isNew && !canRead(id)) {
                 throw new AccessDeniedException("cannot read item " + id);
             }
             // create instance of item
-            item = createItemInstance(id);
+            data = createItemData(id);
+            cacheItem(data);
         }
-        return item;
+        return createItemInstance(data);
+    }
+
+    /**
+     * Create an item instance from an item state. This method creates a
+     * new <code>ItemData</code> instance without looking at the cache and
+     * returns a new item instance.
+     *
+     * @param state item state
+     * @return item instance
+     * @throws RepositoryException if an error occurs
+     */
+    synchronized ItemImpl createItemInstance(ItemState state)
+            throws RepositoryException {
+
+        ItemData data = createItemData(state);
+        cacheItem(data);
+        return createItemInstance(data);
     }
 
     /**
@@ -603,10 +617,10 @@
     }
 
     //-------------------------------------------------< item factory methods >
-    private ItemImpl createItemInstance(ItemId id)
+
+    private ItemData createItemData(ItemId id)
             throws ItemNotFoundException, RepositoryException {
-        // create instance of item using its state object
-        ItemImpl item;
+
         ItemState state;
         try {
             state = itemStateProvider.getItemState(id);
@@ -617,98 +631,47 @@
             log.error(msg, ise);
             throw new RepositoryException(msg, ise);
         }
+        return createItemData(state);
+    }
 
+    private ItemData createItemData(ItemState state) throws RepositoryException {
+        ItemId id = state.getId();
         if (id.equals(rootNodeId)) {
             // special handling required for root node
-            item = createNodeInstance((NodeState) state, rootNodeDef);
+            return new NodeData((NodeState) state, rootNodeDef);
         } else if (state.isNode()) {
-            item = createNodeInstance((NodeState) state);
+            NodeState nodeState = (NodeState) state;
+            return new NodeData(nodeState, getDefinition(nodeState));
         } else {
-            item = createPropertyInstance((PropertyState) state);
+            PropertyState propertyState = (PropertyState) state;
+            return new PropertyData(propertyState, getDefinition(propertyState));
         }
-        return item;
     }
 
-    NodeImpl createNodeInstance(NodeState state, NodeDefinition def)
-            throws RepositoryException {
-        NodeId id = state.getNodeId();
-        // we want to be informed on life cycle changes of the new node object
-        // in order to maintain item cache consistency
-        ItemLifeCycleListener[] listeners = new ItemLifeCycleListener[]{this};
-        NodeImpl node = null;
+    private ItemImpl createItemInstance(ItemData data) {
+        if (data.isNode()) {
+            return createNodeInstance((AbstractNodeData) data);
+        } else {
+            return createPropertyInstance((PropertyData) data);
+        }
+    }
 
+    private NodeImpl createNodeInstance(AbstractNodeData data) {
         // check special nodes
+        final NodeState state = data.getNodeState();
         if (state.getNodeTypeName().equals(NameConstants.NT_VERSION)) {
-            node = createVersionInstance(id, state, def, listeners);
-
+            return new VersionImpl(this, session, data);
         } else if (state.getNodeTypeName().equals(NameConstants.NT_VERSIONHISTORY)) {
-            node = createVersionHistoryInstance(id, state, def, listeners);
-
+            return new VersionHistoryImpl(this, session, data);
         } else {
             // create node object
-            node = new NodeImpl(this, session, id, state, def, listeners);
+            return new NodeImpl(this, session, data);
         }
-        node.notifyCreated();
-        return node;
-    }
-
-    NodeImpl createNodeInstance(NodeState state) throws RepositoryException {
-        // 1. get definition of the specified node
-        NodeDefinition def = getDefinition(state);
-        // 2. create instance
-        return createNodeInstance(state, def);
     }
 
-    PropertyImpl createPropertyInstance(PropertyState state,
-                                        PropertyDefinition def) {
-        // we want to be informed on life cycle changes of the new property object
-        // in order to maintain item cache consistency
-        ItemLifeCycleListener[] listeners = new ItemLifeCycleListener[]{this};
-        // create property object
-        PropertyImpl property = new PropertyImpl(
-                this, session, state.getPropertyId(), state, def, listeners);
-        property.notifyCreated();
-        return property;
-    }
-
-    PropertyImpl createPropertyInstance(PropertyState state)
-            throws RepositoryException {
-        // 1. get definition for the specified property
-        PropertyDefinition def = getDefinition(state);
-        // 2. create instance
-        return createPropertyInstance(state, def);
-    }
-
-    /**
-     * Create a version instance.
-     * @param id node id
-     * @param state node state
-     * @param def node definition
-     * @param listeners listeners
-     * @return version instance
-     * @throws RepositoryException if an error occurs
-     */
-    protected VersionImpl createVersionInstance(
-            NodeId id, NodeState state, NodeDefinition def,
-            ItemLifeCycleListener[] listeners) throws RepositoryException {
-
-        return new VersionImpl(this, session, id, state, def, listeners);
-    }
-
-    /**
-     * Create a version history instance.
-     * @param id node id
-     * @param state node state
-     * @param def node definition
-     * @param listeners listeners
-     * @return version instance
-     * @throws RepositoryException if an error occurs
-     */
-    protected VersionHistoryImpl createVersionHistoryInstance(
-            NodeId id, NodeState state, NodeDefinition def,
-            ItemLifeCycleListener[] listeners) throws RepositoryException {
-
-        return new VersionHistoryImpl(this, session, id, state, def, listeners);
+    private PropertyImpl createPropertyInstance(PropertyData data) {
+        // check special nodes
+        return new PropertyImpl(this, session, data);
     }
 
     //---------------------------------------------------< item cache methods >
@@ -720,13 +683,31 @@
      * @return the item reference stored in the corresponding cache entry
      *         or <code>null</code> if there's no corresponding cache entry.
      */
-    private ItemImpl retrieveItem(ItemId id) {
+    private ItemData retrieveItem(ItemId id) {
         synchronized (itemCache) {
-            ItemImpl item = (ItemImpl) itemCache.get(id);
-            if (item == null && id.denotesNode()) {
-                item = shareableNodesCache.retrieve((NodeId) id);
+            ItemData data = (ItemData) itemCache.get(id);
+            if (data == null && id.denotesNode()) {
+                data = shareableNodesCache.retrieveFirst((NodeId) id);
             }
-            return item;
+            return data;
+        }
+    }
+
+    /**
+     * Return a node from the cache.
+     *
+     * @param id id of the node that should be retrieved.
+     * @param parentId parent id of the node that should be retrieved
+     * @return reference stored in the corresponding cache entry
+     *         or <code>null</code> if there's no corresponding cache entry.
+     */
+    private AbstractNodeData retrieveItem(NodeId id, NodeId parentId) {
+        synchronized (itemCache) {
+            AbstractNodeData data = shareableNodesCache.retrieve(id, parentId);
+            if (data == null) {
+                data = (AbstractNodeData) itemCache.get(id);
+            }
+            return data;
         }
     }
 
@@ -736,35 +717,67 @@
      *
      * @param item the item to cache
      */
-    private void cacheItem(ItemImpl item) {
+    private void cacheItem(ItemData data) {
         synchronized (itemCache) {
-            ItemId id = item.getId();
+            if (data.isNode()) {
+                AbstractNodeData nd = (AbstractNodeData) data;
+                if (nd.getPrimaryParentId() != null) {
+                    shareableNodesCache.cache(nd);
+                    return;
+                }
+            }
+            ItemId id = data.getId();
             if (itemCache.containsKey(id)) {
                 log.warn("overwriting cached item " + id);
             }
             if (log.isDebugEnabled()) {
                 log.debug("caching item " + id);
             }
-            itemCache.put(id, item);
+            itemCache.put(id, data);
         }
     }
 
     /**
-     * Removes a cache entry for a specific item.
+     * Removes all cache entries with the given item id. If the item is
+     * shareable, there might be more than one cache entry for this item.
      *
-     * @param id id of the item to remove from the cache
+     * @param id id of the items to remove from the cache
      * @return <code>true</code> if the item was contained in this cache,
      *         <code>false</code> otherwise.
      */
-    private boolean evictItem(ItemId id) {
+    private void evictItems(ItemId id) {
         if (log.isDebugEnabled()) {
-            log.debug("removing item " + id + " from cache");
+            log.debug("removing items " + id + " from cache");
         }
         synchronized (itemCache) {
-            return itemCache.remove(id) != null;
+            itemCache.remove(id);
+            if (id.denotesNode()) {
+                shareableNodesCache.evictAll((NodeId) id);
+            }
+        }
+    }
+
+    /**
+     * Removes a cache entry for a specific item.
+     *
+     * @param id id of the item to remove from the cache
+     */
+    private void evictItem(ItemData data) {
+        if (log.isDebugEnabled()) {
+            log.debug("removing item " + data.getId() + " from cache");
+        }
+        synchronized (itemCache) {
+            if (data.isNode()) {
+                shareableNodesCache.evict((AbstractNodeData) data);
+            }
+            ItemData cached = (ItemData) itemCache.get(data.getId());
+            if (cached == data) {
+                itemCache.remove(data.getId());
+            }
         }
     }
 
+
     //-------------------------------------------------< misc. helper methods >
     /**
      * Failsafe conversion of internal <code>Path</code> to JCR path for use in
@@ -801,51 +814,27 @@
     }
 
     //------------------------------------------------< ItemLifeCycleListener >
-    /**
-     * {@inheritDoc}
-     */
-    public void itemCreated(ItemImpl item) {
-        if (log.isDebugEnabled()) {
-            log.debug("created item " + item.getId());
-        }
-        // add instance to cache
-        if (item.isNode()) {
-            NodeImpl node = (NodeImpl) item;
-            if (node.isShareable()) {
-                shareableNodesCache.cache(node);
-                return;
-            }
-        }
-        cacheItem(item);
-    }
 
     /**
      * {@inheritDoc}
      */
-    public void itemInvalidated(ItemId id, ItemImpl item) {
+    public void itemInvalidated(ItemId id, ItemData data) {
         if (log.isDebugEnabled()) {
             log.debug("invalidated item " + id);
         }
-        // remove instance from cache
-        evictItem(id);
-        if (item.isNode()) {
-            shareableNodesCache.evict((NodeImpl) item);
-        }
+        evictItem(data);
     }
 
     /**
      * {@inheritDoc}
      */
-    public void itemDestroyed(ItemId id, ItemImpl item) {
+    public void itemDestroyed(ItemId id, ItemData data) {
         if (log.isDebugEnabled()) {
             log.debug("destroyed item " + id);
         }
-        // we're no longer interested in this item
-        item.removeLifeCycleListener(this);
-        // remove instance from cache
-        evictItem(id);
-        if (item.isNode()) {
-            shareableNodesCache.evict((NodeImpl) item);
+        synchronized (itemCache) {
+            // remove instance from cache
+            evictItems(id);
         }
     }
 
@@ -884,9 +873,9 @@
      * {@inheritDoc}
      */
     public void stateCreated(ItemState created) {
-        ItemImpl item = retrieveItem(created.getId());
-        if (item != null) {
-            item.stateCreated(created);
+        ItemData data = retrieveItem(created.getId());
+        if (data != null) {
+            data.setStatus(ItemImpl.STATUS_NORMAL);
         }
     }
 
@@ -894,9 +883,20 @@
      * {@inheritDoc}
      */
     public void stateModified(ItemState modified) {
-        ItemImpl item = retrieveItem(modified.getId());
-        if (item != null) {
-            item.stateModified(modified);
+        ItemData data = retrieveItem(modified.getId());
+        if (data != null && data.getState() == modified) {
+            data.setStatus(ItemImpl.STATUS_MODIFIED);
+            /*
+            if (modified.isNode()) {
+                NodeState state = (NodeState) modified;
+                if (state.isShareable()) {
+                    //evictItem(modified.getId());
+                    NodeData nodeData = (NodeData) data;
+                    NodeData shareSibling = new NodeData(nodeData, state.getParentId());
+                    shareableNodesCache.cache(shareSibling);
+                }
+            }
+            */
         }
     }
 
@@ -904,9 +904,13 @@
      * {@inheritDoc}
      */
     public void stateDestroyed(ItemState destroyed) {
-        ItemImpl item = retrieveItem(destroyed.getId());
-        if (item != null) {
-            item.stateDestroyed(destroyed);
+        ItemData data = retrieveItem(destroyed.getId());
+        if (data != null) {
+            data.setStatus(ItemImpl.STATUS_DESTROYED);
+            if (data.getState() == destroyed) {
+                data.setState(null);
+            }
+            itemDestroyed(destroyed.getId(), data);
         }
     }
 
@@ -914,54 +918,74 @@
      * {@inheritDoc}
      */
     public void stateDiscarded(ItemState discarded) {
-        ItemImpl item = retrieveItem(discarded.getId());
-        if (item != null) {
-            item.stateDiscarded(discarded);
-        }
-    }
-
-    /**
-     * Invoked by a shareable <code>NodeImpl</code> when it is has become
-     * transient and has therefore replaced its state. Will inform all other
-     * nodes in the shareable set about this change.
-     *
-     * @param node node that has changed its underlying state
-     */
-    public void becameTransient(NodeImpl node) {
-        NodeState state = (NodeState) node.getItemState();
-
-        NodeImpl n = (NodeImpl) retrieveItem(node.getId());
-        if (n != null && n != node) {
-            n.stateReplaced(state);
-        }
-        shareableNodesCache.stateReplaced(node);
-    }
-
-    /**
-     * Invoked by a shareable <code>NodeImpl</code> when it is has become
-     * persistent and has therefore replaced its state. Will inform all other
-     * nodes in the shareable set about this change.
-     *
-     * @param node node that has changed its underlying state
-     */
-    public void persisted(NodeImpl node) {
-        // item has possibly become shareable on this call: move it
-        // from the main cache to the cache of shareable nodes
-        if (evictItem(node.getNodeId())) {
-            shareableNodesCache.cache(node);
-        }
-
-        NodeState state = (NodeState) node.getItemState();
+        ItemData data = retrieveItem(discarded.getId());
+        if (data != null && data.getState() == discarded) {
+            if (discarded.isTransient()) {
+                switch (discarded.getStatus()) {
+                /**
+                 * persistent item that has been transiently removed
+                 */
+                case ItemState.STATUS_EXISTING_REMOVED:
+                case ItemState.STATUS_EXISTING_MODIFIED:
+                case ItemState.STATUS_STALE_MODIFIED:
+                    ItemState persistentState = discarded.getOverlayedState();
+                    /**
+                     * the state is a transient wrapper for the underlying
+                     * persistent state, therefore restore the persistent state
+                     * and resurrect this item instance if necessary
+                     */
+                    SessionItemStateManager stateMgr = session.getItemStateManager();
+                    stateMgr.disconnectTransientItemState(discarded);
+                    data.setState(persistentState);
+                    return;
+
+                    /**
+                     * persistent item that has been transiently modified or
+                     * removed and the underlying persistent state has been
+                     * externally destroyed since the transient
+                     * modification/removal.
+                     */
+                case ItemState.STATUS_STALE_DESTROYED:
+                    /**
+                     * first notify the listeners that this instance has been
+                     * permanently invalidated
+                     */
+                    itemDestroyed(discarded.getId(), data);
+                    // now set state of this instance to 'destroyed'
+                    data.setStatus(ItemImpl.STATUS_DESTROYED);
+                    data.setState(null);
+                    return;
+
+                    /**
+                     * new item that has been transiently added
+                     */
+                case ItemState.STATUS_NEW:
+                    /**
+                     * first notify the listeners that this instance has been
+                     * permanently invalidated
+                     */
+                    itemDestroyed(discarded.getId(), data);
+                    // now set state of this instance to 'destroyed'
+                    // finally dispose state
+                    data.setStatus(ItemImpl.STATUS_DESTROYED);
+                    data.setState(null);
+                    return;
+                }
+            }
 
-        NodeImpl n = (NodeImpl) retrieveItem(node.getId());
-        if (n != null && n != node) {
-            n.stateReplaced(state);
+            /**
+             * first notify the listeners that this instance has been
+             * invalidated
+             */
+            itemInvalidated(discarded.getId(), data);
+            // now render this instance 'invalid'
+            data.setStatus(ItemImpl.STATUS_INVALIDATED);
         }
-        shareableNodesCache.stateReplaced(node);
     }
 
     /**
-     * Cache of shareable nodes.
+     * Cache of shareable nodes. For performance reasons, methods are not
+     * synchronized and thread-safety must be guaranteed by caller.
      */
     class ShareableNodesCache {
 
@@ -993,15 +1017,19 @@
          * @param id node id
          * @return node or <code>null</code>
          */
-        public synchronized NodeImpl retrieve(NodeId id) {
+        public AbstractNodeData retrieveFirst(NodeId id) {
             ReferenceMap map = (ReferenceMap) cache.get(id);
             if (map != null) {
                 Iterator iter = map.values().iterator();
-                while (iter.hasNext()) {
-                    NodeImpl node = (NodeImpl) iter.next();
-                    if (node != null) {
-                        return node;
+                try {
+                    while (iter.hasNext()) {
+                        AbstractNodeData data = (AbstractNodeData) iter.next();
+                        if (data != null) {
+                            return data;
+                        }
                     }
+                } finally {
+                    iter = null;
                 }
             }
             return null;
@@ -1014,10 +1042,10 @@
          * @param parentId parent id
          * @return node or <code>null</code>
          */
-        public synchronized NodeImpl retrieve(NodeId id, NodeId parentId) {
+        public AbstractNodeData retrieve(NodeId id, NodeId parentId) {
             ReferenceMap map = (ReferenceMap) cache.get(id);
             if (map != null) {
-                return (NodeImpl) map.get(parentId);
+                return (AbstractNodeData) map.get(parentId);
             }
             return null;
         }
@@ -1027,13 +1055,14 @@
          *
          * @param node node to cache
          */
-        public synchronized void cache(NodeImpl node) {
-            ReferenceMap map = (ReferenceMap) cache.get(node.getId());
+        public void cache(AbstractNodeData data) {
+            NodeId id = data.getNodeState().getNodeId();
+            ReferenceMap map = (ReferenceMap) cache.get(id);
             if (map == null) {
                 map = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
-                cache.put(node.getId(), map);
+                cache.put(id, map);
             }
-            Object old = map.put(node.getParentId(), node);
+            Object old = map.put(data.getPrimaryParentId(), data);
             if (old != null) {
                 log.warn("overwriting cached item: " + old);
             }
@@ -1044,10 +1073,10 @@
          *
          * @param node node to evict
          */
-        public synchronized void evict(NodeImpl node) {
-            ReferenceMap map = (ReferenceMap) cache.get(node.getId());
+        public void evict(AbstractNodeData data) {
+            ReferenceMap map = (ReferenceMap) cache.get(data.getId());
             if (map != null) {
-                map.remove(node.getParentId());
+                map.remove(data.getPrimaryParentId());
             }
         }
 
@@ -1058,27 +1087,6 @@
          */
         public synchronized void evictAll(NodeId id) {
             cache.remove(id);
-        }
-
-        /**
-         * Replace the state of all nodes that are in the same shared set
-         * as the given node.
-         *
-         * @param node node in shared set.
-         */
-        public synchronized void stateReplaced(NodeImpl node) {
-            NodeState state = (NodeState) node.getItemState();
-
-            ReferenceMap map = (ReferenceMap) cache.get(node.getId());
-            if (map != null) {
-                Iterator iter = map.values().iterator();
-                while (iter.hasNext()) {
-                    NodeImpl n = (NodeImpl) iter.next();
-                    if (n != null && n != node) {
-                        n.stateReplaced(state);
-                    }
-                }
-            }
         }
     }
 }

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeData.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeData.java?rev=647767&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeData.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeData.java Mon Apr 14 05:35:33 2008
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import javax.jcr.nodetype.NodeDefinition;
+import org.apache.jackrabbit.core.state.NodeState;
+
+/**
+ * Data object representing a node. Used for non-shareable nodes or for the
+ * first node in a shared set. For every share-sibling, <code>NodeDataRef</code>
+ * is used instead.
+ */
+class NodeData extends AbstractNodeData {
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param state node state
+     * @param definition node definition
+     */
+    NodeData(NodeState state, NodeDefinition definition) {
+        super(state, definition);
+    }
+}

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeDataRef.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeDataRef.java?rev=647767&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeDataRef.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeDataRef.java Mon Apr 14 05:35:33 2008
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import javax.jcr.nodetype.ItemDefinition;
+import org.apache.jackrabbit.core.state.ItemState;
+
+/**
+ * Data object representing a node. Used for share-siblings of a shareable node
+ * that is already loaded.
+ */
+class NodeDataRef extends AbstractNodeData {
+
+    /** Referenced data object */
+    private final AbstractNodeData data;
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param data data to reference
+     * @param primaryParentId primary parent id
+     */
+    protected NodeDataRef(AbstractNodeData data, NodeId primaryParentId) {
+        this.data = data;
+
+        setPrimaryParentId(primaryParentId);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation returns the state of the referenced data object.
+     */
+    public ItemState getState() {
+        return data.getState();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation sets the state of the referenced data object.
+     */
+    protected void setState(ItemState state) {
+        data.setState(state);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation returns the definition of the referenced data object.
+     */
+    public ItemDefinition getDefinition() {
+        return data.getDefinition();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation sets the definition of the referenced data object.
+     */
+    protected void setDefinition(ItemDefinition definition) {
+        data.setDefinition(definition);
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=647767&r1=647766&r2=647767&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Mon Apr 14 05:35:33 2008
@@ -111,14 +111,12 @@
     /** the definition of this node */
     protected NodeDefinition definition;
 
-    /**
-     * Primary parent id, if this is a shareable node, <code>null</code> otherwise.
-     */
-    private NodeId primaryParentId;
-
     // flag set in status passed to getOrCreateProperty if property was created
     protected static final short CREATED = 0;
 
+    /** node data (avoids casting <code>ItemImpl.data</code>) */
+    private final AbstractNodeData data;
+
     /**
      * Protected constructor.
      *
@@ -129,13 +127,12 @@
      * @param definition definition of <i>this</i> <code>Node</code>
      * @param listeners  listeners on life cylce changes of this <code>NodeImpl</code>
      */
-    protected NodeImpl(ItemManager itemMgr, SessionImpl session, NodeId id,
-                       NodeState state, NodeDefinition definition,
-                       ItemLifeCycleListener[] listeners) {
-        super(itemMgr, session, id, state, listeners);
-        this.definition = definition;
+    protected NodeImpl(ItemManager itemMgr, SessionImpl session, AbstractNodeData data) {
+        super(itemMgr, session, data);
+        this.data = data;
         // paranoid sanity check
         NodeTypeRegistry ntReg = session.getNodeTypeManager().getNodeTypeRegistry();
+        final NodeState state = data.getNodeState();
         if (ntReg.isRegistered(state.getNodeTypeName())) {
             primaryTypeName = state.getNodeTypeName();
         } else {
@@ -147,25 +144,6 @@
                     + state.getNodeTypeName() + "' of node " + safeGetJCRPath());
             primaryTypeName = NameConstants.NT_UNSTRUCTURED;
         }
-        if (isShareable()) {
-            this.primaryParentId = state.getParentId();
-        }
-    }
-
-    /**
-     * Protected constructor. Used when creating a node that is a shared
-     * sibling of another node, and that has the same properties, children nodes,
-     * etc. as the other node.
-     */
-    protected NodeImpl(NodeImpl sharedSibling, NodeId parentId,
-                       ItemLifeCycleListener[] listeners) {
-
-        super(sharedSibling.itemMgr, sharedSibling.session,
-                sharedSibling.id, sharedSibling.state, listeners);
-
-        this.definition = sharedSibling.definition;
-        this.primaryTypeName = sharedSibling.primaryTypeName;
-        this.primaryParentId = parentId;
     }
 
     /**
@@ -190,7 +168,7 @@
             if (relPath.indexOf('/') == -1) {
                 Name propName = session.getQName(relPath);
                 // check if property entry exists
-                NodeState thisState = (NodeState) state;
+                NodeState thisState = data.getNodeState();
                 if (thisState.hasPropertyName(propName)) {
                     return new PropertyId(thisState.getNodeId(), propName);
                 } else {
@@ -235,7 +213,7 @@
                 Path.Element pe = p.getNameElement();
                 if (pe.denotesName()) {
                     // check if node entry exists
-                    NodeState thisState = (NodeState) state;
+                    NodeState thisState = data.getNodeState();
                     int index = pe.getIndex();
                     if (index == 0) {
                         index = 1;
@@ -280,23 +258,24 @@
 
     protected synchronized ItemState getOrCreateTransientItemState()
             throws RepositoryException {
-        if (!isTransient()) {
-            try {
-                // make transient (copy-on-write)
-                NodeState transientState =
-                        stateMgr.createTransientNodeState((NodeState) state, ItemState.STATUS_EXISTING_MODIFIED);
-                // replace persistent with transient state
-                state = transientState;
-                if (isShareable()) {
-                    itemMgr.becameTransient(this);
+
+        synchronized (data) {
+            if (!isTransient()) {
+                try {
+                    // make transient (copy-on-write)
+                    NodeState transientState =
+                            stateMgr.createTransientNodeState(
+                                    data.getNodeState(), ItemState.STATUS_EXISTING_MODIFIED);
+                    // replace persistent with transient state
+                    data.setState(transientState);
+                } catch (ItemStateException ise) {
+                    String msg = "failed to create transient state";
+                    log.debug(msg);
+                    throw new RepositoryException(msg, ise);
                 }
-            } catch (ItemStateException ise) {
-                String msg = "failed to create transient state";
-                log.debug(msg);
-                throw new RepositoryException(msg, ise);
             }
+            return getItemState();
         }
-        return state;
     }
 
     /**
@@ -318,7 +297,7 @@
          * (e.g. using a NodeTypeInstanceHandler interface)
          */
 
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
 
         // compute system generated values
         NodeTypeImpl nt = (NodeTypeImpl) def.getDeclaringNodeType();
@@ -508,7 +487,7 @@
         }
 
         // create Property instance wrapping new property state
-        PropertyImpl prop = itemMgr.createPropertyInstance(propState, def);
+        PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propState.getId());
 
         // modify the state of 'this', i.e. the parent node
         NodeState thisState = (NodeState) getOrCreateTransientItemState();
@@ -543,7 +522,7 @@
         // create Node instance wrapping new node state
         NodeImpl node;
         try {
-            node = itemMgr.createNodeInstance(nodeState, def);
+            node = (NodeImpl) itemMgr.getItem(id);
         } catch (RepositoryException re) {
             // something went wrong
             stateMgr.disposeTransientItemState(nodeState);
@@ -645,7 +624,7 @@
         NodeState thisState = (NodeState) getOrCreateTransientItemState();
         // set id of new definition
         thisState.setDefinitionId(defId);
-        definition = newDef;
+        data.setDefinition(newDef);
     }
 
     protected void onRemove(NodeId parentId) throws RepositoryException {
@@ -659,10 +638,10 @@
                 // leave the child node entries and properties
 
                 // set state of this instance to 'invalid'
-                status = STATUS_INVALIDATED;
-                // notify the listeners that this instance has been
+                data.setStatus(STATUS_INVALIDATED);
+                // notify the item manager that this instance has been
                 // temporarily invalidated
-                notifyInvalidated();
+                itemMgr.itemInvalidated(id, data);
                 return;
             }
         }
@@ -800,7 +779,7 @@
         }
 
         // check for name collisions
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
         NodeState.ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1);
         if (cne != null) {
             // there's already a child node entry with that name;
@@ -816,6 +795,7 @@
         }
 
         // check protected flag of parent (i.e. this) node
+        final NodeDefinition definition = data.getNodeDefinition();
         if (definition.isProtected()) {
             String msg = safeGetJCRPath() + ": cannot add a child to a protected node";
             log.debug(msg);
@@ -827,7 +807,7 @@
     }
 
     private void setMixinTypesProperty(Set mixinNames) throws RepositoryException {
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
         // get or create jcr:mixinTypes property
         PropertyImpl prop;
         if (thisState.hasPropertyName(NameConstants.JCR_MIXINTYPES)) {
@@ -862,7 +842,7 @@
      * @return a set of the <code>Name</code>s of this node's mixin types.
      */
     public Set getMixinTypeNames() {
-        return ((NodeState) state).getMixinTypeNames();
+        return data.getNodeState().getMixinTypeNames();
     }
 
     /**
@@ -873,7 +853,7 @@
      * @throws RepositoryException if an error occurs
      */
     public EffectiveNodeType getEffectiveNodeType() throws RepositoryException {
-        return getEffectiveNodeType(((NodeState) state).getMixinTypeNames());
+        return getEffectiveNodeType(data.getNodeState().getMixinTypeNames());
     }
 
     /**
@@ -965,7 +945,7 @@
             return;
         }
 
-        NodeState transientState = (NodeState) state;
+        NodeState transientState = data.getNodeState();
 
         NodeState persistentState = (NodeState) transientState.getOverlayedState();
         if (persistentState == null) {
@@ -1002,16 +982,12 @@
         // tell state manager to disconnect item state
         stateMgr.disconnectTransientItemState(transientState);
         // swap transient state with persistent state
-        state = persistentState;
+        data.setState(persistentState);
         // reset status
-        status = STATUS_NORMAL;
+        data.setStatus(STATUS_NORMAL);
 
-        if (isShareable()) {
-            // if node has become shareable, set its primary parent id
-            if (primaryParentId == null) {
-                primaryParentId = state.getParentId();
-            }
-            itemMgr.persisted(this);
+        if (isShareable() && data.getPrimaryParentId() == null) {
+            data.setPrimaryParentId(persistentState.getParentId());
         }
     }
 
@@ -1052,6 +1028,7 @@
         }
 
         // check protected flag
+        final NodeDefinition definition = data.getNodeDefinition();
         if (definition.isProtected()) {
             String msg = safeGetJCRPath() + ": cannot add a mixin node type to a protected node";
             log.debug(msg);
@@ -1078,7 +1055,7 @@
         EffectiveNodeType entExisting;
         try {
             // existing mixin's
-            HashSet set = new HashSet(((NodeState) state).getMixinTypeNames());
+            HashSet set = new HashSet(data.getNodeState().getMixinTypeNames());
             // primary type
             set.add(primaryTypeName);
             // build effective node type representing primary type including existing mixin's
@@ -1164,6 +1141,7 @@
         }
 
         // check protected flag
+        NodeDefinition definition = data.getNodeDefinition();
         if (definition.isProtected()) {
             String msg = safeGetJCRPath()
                     + ": cannot remove a mixin node type from a protected node";
@@ -1175,7 +1153,8 @@
         checkLock();
 
         // check if mixin is assigned
-        if (!((NodeState) state).getMixinTypeNames().contains(mixinName)) {
+        final NodeState state = data.getNodeState();
+        if (!state.getMixinTypeNames().contains(mixinName)) {
             throw new NoSuchNodeTypeException();
         }
 
@@ -1183,7 +1162,7 @@
         NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
 
         // build effective node type of remaining mixin's & primary type
-        Set remainingMixins = new HashSet(((NodeState) state).getMixinTypeNames());
+        Set remainingMixins = new HashSet(state.getMixinTypeNames());
         // remove name of target mixin
         remainingMixins.remove(mixinName);
         EffectiveNodeType entRemaining;
@@ -1282,7 +1261,7 @@
         if (ntName.equals(primaryTypeName)) {
             return true;
         }
-        Set mixins = ((NodeState) state).getMixinTypeNames();
+        Set mixins = data.getNodeState().getMixinTypeNames();
         if (mixins.contains(ntName)) {
             return true;
         }
@@ -1456,7 +1435,7 @@
         // check state of this instance
         sanityCheck();
 
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
         if (index == 0) {
             index = 1;
         }
@@ -1498,7 +1477,7 @@
         // check state of this instance
         sanityCheck();
 
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
         if (index == 0) {
             index = 1;
         }
@@ -1545,7 +1524,7 @@
         // check state of this instance
         sanityCheck();
 
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
         if (!thisState.hasPropertyName(name)) {
             return false;
         }
@@ -1830,6 +1809,7 @@
         }
 
         // check protected flag
+        final NodeDefinition definition = data.getNodeDefinition();
         if (definition.isProtected()) {
             String msg = safeGetJCRPath()
                     + ": cannot change child node ordering of a protected node";
@@ -1840,7 +1820,7 @@
         // check lock status
         checkLock();
 
-        ArrayList list = new ArrayList(((NodeState) state).getChildNodeEntries());
+        ArrayList list = new ArrayList(data.getNodeState().getChildNodeEntries());
         int srcInd = -1, destInd = -1;
         for (int i = 0; i < list.size(); i++) {
             NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) list.get(i);
@@ -1930,12 +1910,13 @@
         // child node entries (JCR-1055);
         // => backup list of child node entries beforehand in order
         // to restore it afterwards
-        NodeState.ChildNodeEntry cneExisting = ((NodeState) state).getChildNodeEntry(id);
+        NodeState state = data.getNodeState();
+        NodeState.ChildNodeEntry cneExisting = state.getChildNodeEntry(id);
         if (cneExisting == null) {
             throw new ItemNotFoundException(safeGetJCRPath()
                     + ": no child node entry with id " + id);
         }
-        List cneList = new ArrayList(((NodeState) state).getChildNodeEntries());
+        List cneList = new ArrayList(state.getChildNodeEntries());
 
         // remove existing
         existing.remove();
@@ -1948,21 +1929,24 @@
             }
         }
 
+        // fetch <code>state</code> again, as it changed while removing child
+        state = data.getNodeState();
+
         // restore list of child node entries (JCR-1055)
         if (cneExisting.getName().equals(nodeName)) {
             // restore original child node list
-            ((NodeState) state).setChildNodeEntries(cneList);
+            state.setChildNodeEntries(cneList);
         } else {
             // replace child node entry with different name
             // but preserving original position
-            ((NodeState) state).removeAllChildNodeEntries();
+            state.removeAllChildNodeEntries();
             for (Iterator iter = cneList.iterator(); iter.hasNext();) {
                 NodeState.ChildNodeEntry cne = (NodeState.ChildNodeEntry) iter.next();
                 if (cne.getId().equals(id)) {
                     // replace entry with different name
-                    ((NodeState) state).addChildNodeEntry(nodeName, id);
+                    state.addChildNodeEntry(nodeName, id);
                 } else {
-                    ((NodeState) state).addChildNodeEntry(cne.getName(), cne.getId());
+                    state.addChildNodeEntry(cne.getName(), cne.getId());
                 }
             }
         }
@@ -2019,7 +2003,7 @@
             log.debug(msg);
             throw new ConstraintViolationException(msg, re);
         }
-        NodeState thisState = (NodeState) state;
+        NodeState thisState = data.getNodeState();
         NodeState.ChildNodeEntry cne = thisState.getChildNodeEntry(name, 1);
         if (cne != null) {
             // there's already a child node entry with that name;
@@ -2035,6 +2019,7 @@
         }
 
         // (4) check protected flag of parent (i.e. this) node
+        final NodeDefinition definition = data.getNodeDefinition();
         if (definition.isProtected()) {
             String msg = safeGetJCRPath() + ": cannot add a child to a protected node";
             log.debug(msg);
@@ -2069,6 +2054,7 @@
         // check state of this instance
         sanityCheck();
 
+        final NodeState state = data.getNodeState();
         if (state.getParentId() == null) {
             // this is the root node
             return "";
@@ -2103,16 +2089,12 @@
         // check state of this instance
         sanityCheck();
 
-        // check if shareable node
-        NodeId parentId = this.primaryParentId;
+        // check if root node
+        NodeId parentId = getParentId();
         if (parentId == null) {
-            // check if root node
-            parentId = state.getParentId();
-            if (parentId == null) {
-                String msg = "root node doesn't have a parent";
-                log.debug(msg);
-                throw new ItemNotFoundException(msg);
-            }
+            String msg = "root node doesn't have a parent";
+            log.debug(msg);
+            throw new ItemNotFoundException(msg);
         }
         return (Node) itemMgr.getItem(parentId);
     }
@@ -2627,7 +2609,7 @@
             throw new PathNotFoundException(relPath);
         }
         try {
-            if (((NodeState) state).hasChildNodeEntry(id)) {
+            if (data.getNodeState().hasChildNodeEntry(id)) {
                 return itemMgr.getNode(id, getNodeId());
             }
             return (NodeImpl) itemMgr.getItem(id);
@@ -2789,7 +2771,7 @@
         // check state of this instance
         sanityCheck();
 
-        Set mixinNames = ((NodeState) state).getMixinTypeNames();
+        Set mixinNames = data.getNodeState().getMixinTypeNames();
         if (mixinNames.isEmpty()) {
             return new NodeType[0];
         }
@@ -2844,7 +2826,7 @@
         }
 
         // check protected flag
-        if (definition.isProtected()) {
+        if (data.getNodeDefinition().isProtected()) {
             return false;
         }
 
@@ -2879,7 +2861,7 @@
         EffectiveNodeType entExisting;
         try {
             // existing mixin's
-            HashSet set = new HashSet(((NodeState) state).getMixinTypeNames());
+            HashSet set = new HashSet(data.getNodeState().getMixinTypeNames());
             // primary type
             set.add(primaryTypeName);
             // build effective node type representing primary type including existing mixin's
@@ -2927,7 +2909,7 @@
         // check state of this instance
         sanityCheck();
 
-        return definition;
+        return data.getNodeDefinition();
     }
 
     /**
@@ -3104,7 +3086,7 @@
         if (!isShareable()) {
             list.add(this);
         } else {
-            NodeState state = (NodeState) this.state;
+            NodeState state = data.getNodeState();
             Iterator iter = state.getSharedSet().iterator();
             while (iter.hasNext()) {
                 NodeId parentId = (NodeId) iter.next();
@@ -3182,7 +3164,7 @@
      * @see NodeState#isShareable()
      */
     boolean isShareable() {
-       return ((NodeState) state).isShareable();
+       return data.getNodeState().isShareable();
     }
 
     /**
@@ -3194,10 +3176,7 @@
      * @return parent id
      */
     NodeId getParentId() {
-        if (primaryParentId != null) {
-            return primaryParentId;
-        }
-        return state.getParentId();
+        return data.getParentId();
     }
 
     /**
@@ -3209,7 +3188,7 @@
      *         <code>false</code> otherwise.
      */
     boolean hasShareParent(NodeId parentId) {
-        return ((NodeState) state).containsShare(parentId);
+        return data.getNodeState().containsShare(parentId);
     }
 
     /**
@@ -3241,7 +3220,7 @@
 
         // quickly verify whether the share is already contained before creating
         // a transient state in vain
-        NodeState state = (NodeState) this.state;
+        NodeState state = data.getNodeState();
         if (!state.containsShare(parentId)) {
             state = (NodeState) getOrCreateTransientItemState();
             if (state.addShare(parentId)) {
@@ -3276,7 +3255,7 @@
         NodeState.ChildNodeEntry entry = ((NodeState) parentNode.getItemState()).
                 getChildNodeEntry(getNodeId());
         if (entry == null) {
-            String msg = "failed to build path of " + state.getId() + ": "
+            String msg = "failed to build path of " + id + ": "
                     + parentId + " has no child entry for "
                     + id;
             log.debug(msg);
@@ -3291,16 +3270,6 @@
         return builder.getPath();
     }
 
-    /**
-     * Invoked when another node in the same shared set has replaced the
-     * node state.
-     *
-     * @param state state that is now stored as <code>NodeImpl</code>'s state
-     */
-    void stateReplaced(NodeState state) {
-        this.state = state;
-    }
-
     //------------------------------< versioning support: public Node methods >
     /**
      * {@inheritDoc}
@@ -3765,7 +3734,7 @@
                 Set set = internalGetMergeFailed();
                 set.add(srcNode.getBaseVersion().getUUID());
                 internalSetMergeFailed(set);
-                failedIds.add(state.getId());
+                failedIds.add(id);
                 return null;
             } else {
                 String msg = "Unable to merge nodes. Violating versions. " + safeGetJCRPath();
@@ -4764,12 +4733,13 @@
         }
 
         // check protected flag
-        if (definition.isProtected()) {
+        if (data.getDefinition().isProtected()) {
             String msg = safeGetJCRPath() + ": cannot set primary type of a protected node";
             log.debug(msg);
             throw new ConstraintViolationException(msg);
         }
 
+        final NodeState state = data.getNodeState();
         if (state.getParentId() == null) {
             String msg = "changing the primary type of the root node is not supported";
             log.debug(msg);
@@ -4805,7 +4775,7 @@
             entOld = ntReg.getEffectiveNodeType(primaryTypeName);
 
             // existing mixin's
-            HashSet set = new HashSet(((NodeState) state).getMixinTypeNames());
+            HashSet set = new HashSet(state.getMixinTypeNames());
             // new primary type
             set.add(ntName);
             // try to build new effective node type (will throw in case of conflicts)
@@ -4825,7 +4795,7 @@
             throw new ConstraintViolationException(msg, re);
         }
 
-        if (!defId.equals(((NodeState) state).getDefinitionId())) {
+        if (!defId.equals(state.getDefinitionId())) {
             onRedefine(defId);
         }
 

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyData.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyData.java?rev=647767&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyData.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyData.java Mon Apr 14 05:35:33 2008
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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;
+
+import javax.jcr.nodetype.PropertyDefinition;
+import org.apache.jackrabbit.core.state.PropertyState;
+
+/**
+ * Data object representing a property.
+ */
+public class PropertyData extends ItemData {
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param state associated property state
+     * @param definition associated property definition
+     */
+    PropertyData(PropertyState state, PropertyDefinition definition) {
+        super(state, definition);
+    }
+
+    /**
+     * Return the associated property state.
+     *
+     * @return property state
+     */
+    public PropertyState getPropertyState() {
+        return (PropertyState) getState();
+    }
+
+    /**
+     * Return the associated property definition.
+     *
+     * @return property definition
+     */
+    public PropertyDefinition getPropertyDefinition() {
+        return (PropertyDefinition) getDefinition();
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java?rev=647767&r1=647766&r2=647767&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java Mon Apr 14 05:35:33 2008
@@ -53,7 +53,8 @@
 
     private static Logger log = LoggerFactory.getLogger(PropertyImpl.class);
 
-    private final PropertyDefinition definition;
+    /** property data (avoids casting <code>ItemImpl.data</code>) */
+    private final PropertyData data;
 
     /**
      * Package private constructor.
@@ -65,11 +66,9 @@
      * @param definition definition of <i>this</i> <code>Property</code>
      * @param listeners  listeners on life cylce changes of this <code>PropertyImpl</code>
      */
-    PropertyImpl(ItemManager itemMgr, SessionImpl session, PropertyId id,
-                 PropertyState state, PropertyDefinition definition,
-                 ItemLifeCycleListener[] listeners) {
-        super(itemMgr, session, id, state, listeners);
-        this.definition = definition;
+    PropertyImpl(ItemManager itemMgr, SessionImpl session, PropertyData data) {
+        super(itemMgr, session, data);
+        this.data = data;
         // value will be read on demand
     }
 
@@ -90,20 +89,24 @@
 
     protected synchronized ItemState getOrCreateTransientItemState()
             throws RepositoryException {
-        if (!isTransient()) {
-            // make transient (copy-on-write)
-            try {
-                PropertyState transientState =
-                        stateMgr.createTransientPropertyState((PropertyState) state, ItemState.STATUS_EXISTING_MODIFIED);
-                // swap persistent with transient state
-                state = transientState;
-            } catch (ItemStateException ise) {
-                String msg = "failed to create transient state";
-                log.debug(msg);
-                throw new RepositoryException(msg, ise);
+
+        synchronized (data) {
+            if (!isTransient()) {
+                // make transient (copy-on-write)
+                try {
+                    PropertyState transientState =
+                            stateMgr.createTransientPropertyState(
+                                    data.getPropertyState(), ItemState.STATUS_EXISTING_MODIFIED);
+                    // swap persistent with transient state
+                    data.setState(transientState);
+                } catch (ItemStateException ise) {
+                    String msg = "failed to create transient state";
+                    log.debug(msg);
+                    throw new RepositoryException(msg, ise);
+                }
             }
+            return getItemState();
         }
-        return state;
     }
 
     protected void makePersistent() throws InvalidItemStateException {
@@ -112,7 +115,7 @@
             return;
         }
 
-        PropertyState transientState = (PropertyState) state;
+        PropertyState transientState = data.getPropertyState();
         PropertyState persistentState = (PropertyState) transientState.getOverlayedState();
         if (persistentState == null) {
             // this property is 'new'
@@ -139,9 +142,9 @@
         // tell state manager to disconnect item state
         stateMgr.disconnectTransientItemState(transientState);
         // swap transient state with persistent state
-        state = persistentState;
+        data.setState(persistentState);
         // reset status
-        status = STATUS_NORMAL;
+        data.setStatus(STATUS_NORMAL);
     }
 
     protected void restoreTransient(PropertyState transientState)
@@ -222,6 +225,7 @@
             LockException, ConstraintViolationException,
             RepositoryException {
         NodeImpl parent = (NodeImpl) getParent();
+        PropertyDefinition definition = data.getPropertyDefinition();
 
         // verify that parent node is checked-out
         if (!parent.internalIsCheckedOut()) {
@@ -324,6 +328,7 @@
         checkSetValue(false);
 
         // check type according to definition of this property
+        final PropertyDefinition definition = data.getPropertyDefinition();
         int reqType = definition.getRequiredType();
         if (reqType == PropertyType.UNDEFINED) {
             reqType = PropertyType.NAME;
@@ -373,6 +378,7 @@
         checkSetValue(true);
 
         // check type according to definition of this property
+        final PropertyDefinition definition = data.getPropertyDefinition();
         int reqType = definition.getRequiredType();
         if (reqType == PropertyType.UNDEFINED) {
             reqType = PropertyType.NAME;
@@ -420,6 +426,7 @@
      * @throws RepositoryException
      */
     public InternalValue[] internalGetValues() throws RepositoryException {
+        final PropertyDefinition definition = data.getPropertyDefinition();
         if (definition.isMultiple()) {
             return getPropertyState().getValues();
         } else {
@@ -438,6 +445,7 @@
      * @throws RepositoryException
      */
     public InternalValue internalGetValue() throws RepositoryException {
+        final PropertyDefinition definition = data.getPropertyDefinition();
         if (definition.isMultiple()) {
             throw new ValueFormatException(
                     this + " is a multi-valued property,"
@@ -583,6 +591,7 @@
         checkSetValue(false);
 
         // check type according to definition of this property
+        final PropertyDefinition definition = data.getPropertyDefinition();
         int reqType = definition.getRequiredType();
         if (reqType == PropertyType.UNDEFINED) {
             if (value != null) {
@@ -652,6 +661,7 @@
             }
         }
 
+        final PropertyDefinition definition = data.getPropertyDefinition();
         int reqType = definition.getRequiredType();
         if (reqType == PropertyType.UNDEFINED) {
             reqType = valueType; // use the given type as property type
@@ -705,7 +715,7 @@
         // check state of this instance
         sanityCheck();
 
-        return definition;
+        return data.getPropertyDefinition();
     }
 
     public int getType() throws RepositoryException {