Author: tripod
Date: Fri Mar 11 06:26:27 2005
New Revision: 157083
URL: http://svn.apache.org/viewcvs?view=rev&rev=157083
Log:
- fixing latest locking issues
- fixing version items refresh
Added:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
(with props)
Modified:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java Fri Mar 11
06:26:27 2005
@@ -523,7 +523,7 @@
removeChildProperty(qName);
}
- protected void removeChildProperty(QName propName) throws RepositoryException {
+ public void removeChildProperty(QName propName) throws RepositoryException {
// modify the state of 'this', i.e. the parent node
NodeState thisState = (NodeState) getOrCreateTransientItemState();
@@ -3645,22 +3645,7 @@
checkLockable();
LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- Lock lock = lockMgr.lock(this, isDeep, isSessionScoped);
-
- try {
- internalSetProperty(JCR_LOCKOWNER,
- InternalValue.create(session.getUserId()));
- internalSetProperty(JCR_LOCKISDEEP,
- InternalValue.create(isDeep));
- save();
-
- } catch (RepositoryException e) {
-
- // An error occurred, so remove lock
- lockMgr.unlock(this);
- throw e;
- }
- return lock;
+ return lockMgr.lock(this, isDeep, isSessionScoped);
}
/**
@@ -3699,10 +3684,6 @@
LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
lockMgr.unlock(this);
-
- removeChildProperty(JCR_LOCKOWNER);
- removeChildProperty(JCR_LOCKISDEEP);
- save();
}
/**
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java Fri
Mar 11 06:26:27 2005
@@ -779,6 +779,11 @@
/**
* {@inheritDoc}
*/
+ public void loggingOut(SessionImpl session) {}
+
+ /**
+ * {@inheritDoc}
+ */
public void loggedOut(SessionImpl session) {
// remove session from active sessions
activeSessions.remove(session);
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java Fri Mar
11 06:26:27 2005
@@ -409,6 +409,24 @@
}
/**
+ * Notify the listeners that this session is about to be closed.
+ */
+ protected void notifyLoggingOut() {
+ // copy listeners to array to avoid ConcurrentModificationException
+ SessionListener[] la = new SessionListener[listeners.size()];
+ Iterator iter = listeners.values().iterator();
+ int cnt = 0;
+ while (iter.hasNext()) {
+ la[cnt++] = (SessionListener) iter.next();
+ }
+ for (int i = 0; i < la.length; i++) {
+ if (la[i] != null) {
+ la[i].loggingOut(this);
+ }
+ }
+ }
+
+ /**
* Notify the listeners that this session has been closed.
*/
protected void notifyLoggedOut() {
@@ -1023,6 +1041,9 @@
// ignore
return;
}
+
+ // notify listeners that session is about to be closed
+ notifyLoggingOut();
// discard all transient changes
itemStateMgr.disposeAllTransientItemStates();
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java Fri
Mar 11 06:26:27 2005
@@ -25,6 +25,15 @@
public interface SessionListener {
/**
+ * Called when a <code>Session</code> is about to be 'closed' by
+ * calling <code>{@link javax.jcr.Session#logout()}</code. At this
+ * moment the session is still valid.
+ *
+ * @param session the <code>Session</code> that is about to be 'closed'
+ */
+ public void loggingOut(SessionImpl session);
+
+ /**
* Called when a <code>Session</code> has been 'closed' by
* calling <code>{@link javax.jcr.Session#logout()}</code.
*
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockImpl.java Fri
Mar 11 06:26:27 2005
@@ -16,6 +16,9 @@
*/
package org.apache.jackrabbit.core.lock;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.NodeId;
+
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.Node;
@@ -35,7 +38,7 @@
/**
* Node holding lock
*/
- private Node node;
+ private final Node node;
/**
* Create a new instance of this class.
@@ -92,6 +95,12 @@
* {@inheritDoc}
*/
public void refresh() throws LockException, RepositoryException {
- //@todo implement refresh
+ if (isLive()) {
+ throw new LockException("Lock still alive.");
+ }
+ if (getLockToken() == null) {
+ throw new LockException("Session does not hold lock.");
+ }
+ info.refresh((NodeImpl) node);
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockInfo.java Fri
Mar 11 06:26:27 2005
@@ -18,8 +18,13 @@
import org.apache.jackrabbit.core.SessionListener;
import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.log4j.Logger;
import javax.jcr.Session;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
/**
* Contains information about a lock and gets placed inside the child
@@ -28,6 +33,16 @@
class LockInfo implements SessionListener {
/**
+ * Logger
+ */
+ private static final Logger log = Logger.getLogger(LockInfo.class);
+
+ /**
+ * Lock manager
+ */
+ private final LockManagerImpl lockMgr;
+
+ /**
* Lock token
*/
final LockToken lockToken;
@@ -59,18 +74,19 @@
/**
* Create a new instance of this class.
+ * @param lockMgr lock manager
* @param lockToken lock token
* @param sessionScoped whether lock token is session scoped
* @param deep whether lock is deep
* @param lockOwner owner of lock
*/
- public LockInfo(LockToken lockToken, boolean sessionScoped,
- boolean deep, String lockOwner) {
+ public LockInfo(LockManagerImpl lockMgr, LockToken lockToken,
+ boolean sessionScoped, boolean deep, String lockOwner) {
+ this.lockMgr = lockMgr;
this.lockToken = lockToken;
this.sessionScoped = sessionScoped;
this.deep = deep;
this.lockOwner = lockOwner;
- this.live = true;
}
/**
@@ -111,7 +127,7 @@
* itself, otherwise a <code>null</code> string
*/
public String getLockToken(Session session) {
- if (live && session.equals(lockHolder)) {
+ if (session.equals(lockHolder)) {
return lockToken.toString();
}
return null;
@@ -125,12 +141,34 @@
return live;
}
+ /**
+ * Refresh a lock. Will try to re-insert this lock info into the lock
+ * manager's path map, provided the node is not already locked.
+ * @param node node to lock again
+ * @throws LockException if the node is already locked
+ * @throws RepositoryException if some other error occurs
+ * @see javax.jcr.Node#refresh
+ */
+ public void refresh(NodeImpl node) throws LockException, RepositoryException {
+ lockMgr.lock(node, this);
+ }
+
//-------------------------------------------------------< SessionListener >
/**
* {@inheritDoc}
- */
- public void loggedOut(SessionImpl session) {
- live = false;
+ *
+ * When the owning session is logging out, we should unlock the node on
+ * behalf of the user currently holding it.
+ */
+ public void loggingOut(SessionImpl session) {
+ if (live) {
+ lockMgr.unlock(this);
+ }
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public void loggedOut(SessionImpl session) {}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
Fri Mar 11 06:26:27 2005
@@ -145,9 +145,10 @@
NodeImpl node = (NodeImpl) session.getNodeByUUID(lockToken.uuid);
Path path = node.getPrimaryPath();
- LockInfo info = new LockInfo(lockToken, false,
+ LockInfo info = new LockInfo(this, lockToken, false,
node.getProperty(Constants.JCR_LOCKISDEEP).getBoolean(),
node.getProperty(Constants.JCR_LOCKOWNER).getString());
+ info.setLive(true);
lockMap.put(path, info);
} catch (RepositoryException e) {
log.warn("Unable to recreate lock '" + lockToken +
@@ -192,35 +193,46 @@
}
}
- //-----------------------------------------------------------< LockManager >
-
/**
- * {@inheritDoc}
+ * Internal <code>lock</code> implementation that takes as parameter
+ * a lock info that will be used inside the path map.
+ * @param node node to lock
+ * @param info lock info
+ * @throws LockException if the node is already locked
+ * @throws RepositoryException if another error occurs
+ * @return lock
*/
- public synchronized Lock lock(NodeImpl node, boolean isDeep,
- boolean isSessionScoped)
- throws LockException, RepositoryException {
+ Lock lock(NodeImpl node, LockInfo info)
+ throws LockException, RepositoryException {
+ // check whether node is already locked
Path path = node.getPrimaryPath();
PathMap.Child child = lockMap.map(path, false);
- LockInfo info = (LockInfo) child.get();
- if (info != null) {
+ LockInfo other = (LockInfo) child.get();
+ if (other != null) {
if (child.hasPath(path)) {
throw new LockException("Node already locked: " + node.safeGetJCRPath());
- } else if (info.deep) {
+ } else if (other.deep) {
throw new LockException("Parent node has deep lock.");
}
}
- if (isDeep && child.hasPath(path)) {
+ if (info.deep && child.hasPath(path)) {
throw new LockException("Some child node is locked.");
}
+ // add properties to content
+ node.internalSetProperty(Constants.JCR_LOCKOWNER,
+ InternalValue.create(node.getSession().getUserId()));
+ node.internalSetProperty(Constants.JCR_LOCKISDEEP,
+ InternalValue.create(info.deep));
+ node.save();
+
+ // create lock token
SessionImpl session = (SessionImpl) node.getSession();
- info = new LockInfo(new LockToken(node.internalGetUUID()),
- isSessionScoped, isDeep, session.getUserId());
info.setLockHolder(session);
- if (isSessionScoped) {
+ info.setLive(true);
+ if (info.sessionScoped) {
session.addListener(info);
}
session.addLockToken(info.lockToken.toString(), false);
@@ -229,6 +241,60 @@
}
/**
+ * Unlock a node given by its info. Invoked when a session logs out and
+ * all session scoped locks of that session must be unlocked.
+ * @param info lock info
+ */
+ void unlock(LockInfo info) {
+ // if no session currently holds lock, take system session
+ SessionImpl session = info.getLockHolder();
+ if (session == null) {
+ session = this.session;
+ }
+
+ try {
+ // get node's path and remove child in path map
+ NodeImpl node = (NodeImpl) session.getItemManager().getItem(
+ new NodeId(info.getUUID()));
+ Path path = node.getPrimaryPath();
+
+ PathMap.Child child = lockMap.map(path, true);
+ if (child != null) {
+ child.set(null);
+ }
+
+ // set live flag to false
+ info.setLive(false);
+
+ // remove properties in content
+ node.removeChildProperty(Constants.JCR_LOCKOWNER);
+ node.removeChildProperty(Constants.JCR_LOCKISDEEP);
+ node.save();
+
+ } catch (RepositoryException e) {
+ log.warn("Unable to unlock session-scoped lock on node '" +
+ info.lockToken + "': " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+
+ }
+
+ //-----------------------------------------------------------< LockManager >
+
+ /**
+ * {@inheritDoc}
+ */
+ public synchronized Lock lock(NodeImpl node, boolean isDeep,
+ boolean isSessionScoped)
+ throws LockException, RepositoryException {
+
+ // create lock info to use and pass to internal implementation
+ LockInfo info = new LockInfo(this, new LockToken(node.internalGetUUID()),
+ isSessionScoped, isDeep, node.getSession().getUserId());
+ return lock(node, info);
+ }
+
+ /**
* {@inheritDoc}
*/
public synchronized Lock getLock(NodeImpl node)
@@ -254,6 +320,7 @@
public synchronized void unlock(NodeImpl node)
throws LockException, RepositoryException {
+ // check whether node is locked by this session
Path path = node.getPrimaryPath();
PathMap.Child child = lockMap.map(path, true);
@@ -268,11 +335,15 @@
if (!node.getSession().equals(info.getLockHolder())) {
throw new LockException("Node not locked by session: " + node.safeGetJCRPath());
}
- child.set(null);
+ // remove lock in path map
+ child.set(null);
info.setLive(false);
- info.setLockHolder(null);
- session.removeLockToken(info.lockToken.toString(), false);
+
+ // remove properties in content
+ node.removeChildProperty(Constants.JCR_LOCKOWNER);
+ node.removeChildProperty(Constants.JCR_LOCKISDEEP);
+ node.save();
}
/**
@@ -399,7 +470,7 @@
switch (event.getType()) {
case Event.NODE_ADDED:
try {
- childAdded(event.getChildUUID(),
+ nodeAdded(event.getChildUUID(),
Path.create(event.getPath(), nsResolver, true));
} catch (MalformedPathException e) {
log.info("Unable to get event's path: " + e.getMessage());
@@ -409,7 +480,7 @@
break;
case Event.NODE_REMOVED:
try {
- childRemoved(event.getChildUUID(),
+ nodeRemoved(event.getChildUUID(),
Path.create(event.getPath(), nsResolver, true));
} catch (MalformedPathException e) {
log.info("Unable to get event's path: " + e.getMessage());
@@ -422,9 +493,11 @@
}
/**
- * Invoked when some child has been added.
+ * Invoked when some node has been added. Relink the child inside our
+ * zombie map to the new parent. Revitalize all locks inside the
+ * zombie child hierarchy.
*/
- private synchronized void childAdded(String uuid, Path path) {
+ private synchronized void nodeAdded(String uuid, Path path) {
try {
PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
if (parent != null) {
@@ -432,27 +505,41 @@
}
PathMap.Child zombie = (PathMap.Child) zombieNodes.remove(uuid);
if (zombie != null) {
+ zombie.traverse(new PathMap.ChildVisitor() {
+ public void childVisited(PathMap.Child child) {
+ LockInfo info = (LockInfo) child.get();
+ info.setLive(true);
+ }
+ }, false);
lockMap.resurrect(path, zombie);
}
} catch (PathNotFoundException e) {
- log.warn("Added child does not have parent, ignoring event.");
+ log.warn("Added node does not have parent, ignoring event.");
}
}
/**
- * Invoked when some child has been removed.
+ * Invoked when some node has been removed. Unlink the child inside
+ * our path map corresponding to that node. Disable all locks contained
+ * in that subtree.
*/
- private synchronized void childRemoved(String uuid, Path path) {
+ private synchronized void nodeRemoved(String uuid, Path path) {
try {
PathMap.Child parent = lockMap.map(path.getAncestor(1), true);
if (parent != null) {
PathMap.Child child = parent.removeChild(path.getNameElement());
if (child != null) {
+ child.traverse(new PathMap.ChildVisitor() {
+ public void childVisited(PathMap.Child child) {
+ LockInfo info = (LockInfo) child.get();
+ info.setLive(false);
+ }
+ }, false);
zombieNodes.put(uuid, child);
}
}
} catch (PathNotFoundException e) {
- log.warn("Added child does not have parent, ignoring event.");
+ log.warn("Removed node does not have parent, ignoring event.");
}
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/lock/PathMap.java Fri Mar
11 06:26:27 2005
@@ -203,7 +203,7 @@
if (child != null) {
childrenCount--;
}
- if (childrenCount == 0) {
+ if (obj == null && childrenCount == 0) {
remove();
}
return child;
@@ -323,7 +323,7 @@
/**
* Recursively invoked traversal method.
*/
- private void traverse(ChildVisitor visitor, boolean includeEmpty) {
+ public void traverse(ChildVisitor visitor, boolean includeEmpty) {
if (children != null) {
Iterator iter = children.values().iterator();
while (iter.hasNext()) {
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItem.java
Fri Mar 11 06:26:27 2005
@@ -37,4 +37,11 @@
* @return
*/
public InternalVersionItem getParent();
+
+ /**
+ * Adds a vesion item state listener to this item
+ *
+ * @param listener
+ */
+ public void addListener(InternalVersionItemListener listener);
}
Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java?view=auto&rev=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
(added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
Fri Mar 11 06:26:27 2005
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * 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.version;
+
+/**
+ * This Interface defines a listener on versionitem events
+ *
+ * @author tripod
+ * @version $Revision:$, $Date:$
+ */
+public interface InternalVersionItemListener {
+
+ /**
+ * is called, when the version item was modified
+ *
+ * @param item
+ */
+ public void itemModifed(InternalVersionItem item);
+
+}
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
------------------------------------------------------------------------------
svn =
Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/InternalVersionItemListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- 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
Fri Mar 11 06:26:27 2005
@@ -26,6 +26,7 @@
import org.apache.jackrabbit.core.QName;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.UnknownPrefixException;
+import org.apache.jackrabbit.core.RepositoryImpl;
import org.apache.jackrabbit.core.state.NodeState;
import javax.jcr.Item;
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManager.java?view=diff&r1=157082&r2=157083
==============================================================================
--- 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
Fri Mar 11 06:26:27 2005
@@ -159,4 +159,6 @@
* @throws Exception if an error occurs
*/
public void close() throws Exception;
+
+
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
Fri Mar 11 06:26:27 2005
@@ -18,6 +18,7 @@
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.Constants;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.state.ItemStateException;
import org.apache.jackrabbit.core.state.ItemStateManager;
@@ -36,7 +37,7 @@
* version storage ({@link VersionItemStateProvider}) and the persistent
* version manager.
*/
-public class VersionManagerImpl implements VersionManager {
+public class VersionManagerImpl implements VersionManager, Constants, InternalVersionItemListener
{
/**
* the default logger
@@ -122,11 +123,8 @@
*/
public VersionHistory createVersionHistory(NodeImpl node) throws RepositoryException
{
InternalVersionHistory history = vMgr.createVersionHistory(node);
- try {
- virtProvider.getItemState(new NodeId(VERSION_STORAGE_NODE_UUID)).discard();
- } catch (ItemStateException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings
| File Templates.
- }
+ history.addListener(this);
+ onVersionStorageChanged();
return (VersionHistory) node.getSession().getNodeByUUID(history.getId());
}
@@ -148,7 +146,11 @@
* @throws RepositoryException
*/
public InternalVersionHistory getVersionHistory(String id) throws RepositoryException
{
- return vMgr.getVersionHistory(id);
+ InternalVersionHistory hist = vMgr.getVersionHistory(id);
+ if (hist != null) {
+ hist.addListener(this);
+ }
+ return hist;
}
/**
@@ -189,7 +191,11 @@
* @throws RepositoryException
*/
public InternalVersion getVersion(String id) throws RepositoryException {
- return vMgr.getVersion(id);
+ InternalVersion vers = vMgr.getVersion(id);
+ if (vers != null) {
+ vers.addListener(this);
+ }
+ return vers;
}
/**
@@ -210,7 +216,11 @@
* @throws RepositoryException
*/
public InternalVersionItem getItem(String id) throws RepositoryException {
- return vMgr.getItemByExternal(id);
+ InternalVersionItem item = vMgr.getItemByExternal(id);
+ if (item != null) {
+ item.addListener(this);
+ }
+ return item;
}
/**
@@ -223,11 +233,7 @@
*/
public Version checkin(NodeImpl node) throws RepositoryException {
InternalVersion version = vMgr.checkin(node);
- try {
- virtProvider.getItemState(new NodeId(version.getVersionHistory().getId())).discard();
- } catch (ItemStateException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings
| File Templates.
- }
+ version.addListener(this);
return (Version) node.getSession().getNodeByUUID(version.getId());
}
@@ -243,5 +249,27 @@
*/
public void setItemReferences(InternalVersionItem item, List references) {
vMgr.setItemReferences(item, references);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void itemModifed(InternalVersionItem item) {
+ try {
+ virtProvider.getItemState(new NodeId(item.getId())).discard();
+ } catch (ItemStateException e) {
+ log.error("Error while refreshing virtual item.", e);
+ }
+ }
+
+ /**
+ * Flushes the virtual node state information of the version storage
+ */
+ public void onVersionStorageChanged() {
+ try {
+ virtProvider.getItemState(new NodeId(VERSION_STORAGE_NODE_UUID)).discard();
+ } catch (ItemStateException e) {
+ log.error("Error while refreshing virtual version storage.", e);
+ }
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionHistoryImpl.java
Fri Mar 11 06:26:27 2005
@@ -273,6 +273,7 @@
stateMgr.update();
succeeded = true;
+ notifyModifed();
} finally {
if (!succeeded) {
// update operation failed, cancel all modifications
@@ -331,6 +332,7 @@
stateMgr.cancel();
}
}
+ notifyModifed();
return prev;
}
@@ -363,7 +365,7 @@
stateMgr.cancel();
}
}
-
+ notifyModifed();
return v;
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/InternalVersionItemImpl.java
Fri Mar 11 06:26:27 2005
@@ -18,11 +18,16 @@
import org.apache.jackrabbit.core.version.InternalVersionItem;
import org.apache.jackrabbit.core.version.PersistentVersionManager;
+import org.apache.jackrabbit.core.version.InternalVersionItemListener;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Iterator;
/**
*
*/
-abstract class InternalVersionItemImpl {
+abstract class InternalVersionItemImpl implements InternalVersionItem {
/**
* the version manager
@@ -30,6 +35,11 @@
private final PersistentVersionManager vMgr;
/**
+ * the item listeners
+ */
+ private final Set listeners = new HashSet();
+
+ /**
* Creates a new Internal version item impl
*
* @param vMgr
@@ -45,6 +55,23 @@
*/
protected PersistentVersionManager getVersionManager() {
return vMgr;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addListener(InternalVersionItemListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * notifys the listeners that this item was modified
+ */
+ protected void notifyModifed() {
+ Iterator iter = listeners.iterator();
+ while (iter.hasNext()) {
+ ((InternalVersionItemListener) iter.next()).itemModifed(this);
+ }
}
/**
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/version/persistence/NativePVM.java
Fri Mar 11 06:26:27 2005
@@ -563,6 +563,9 @@
initVirtualIds(v.getId(), v.getNode().getState());
+ // notify listeners
+ history.notifyModifed();
+
return v;
} catch (ItemStateException e) {
throw new RepositoryException(e);
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java?view=diff&r1=157082&r2=157083
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
(original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/lock/LockTest.java
Fri Mar 11 06:26:27 2005
@@ -42,59 +42,98 @@
public class LockTest extends AbstractJCRTest {
/**
+ * Test lock token functionality
+ */
+ public void testAddRemoveLockToken() throws Exception {
+ // create new node
+ Node n = testRootNode.addNode(nodeName1, testNodeType);
+ n.addMixin(mixReferenceable);
+ n.addMixin(mixLockable);
+ testRootNode.save();
+
+ // lock node and get lock token
+ Lock lock = n.lock(false, true);
+
+ // assert: session must get a non-null lock token
+ assertNotNull("session must get a non-null lock token",
+ lock.getLockToken());
+
+ // assert: session must hold lock token
+ assertTrue("session must hold lock token",
+ containsLockToken(superuser, lock.getLockToken()));
+
+ // remove lock token
+ String lockToken = lock.getLockToken();
+ superuser.removeLockToken(lockToken);
+
+ // assert: session must get a null lock token
+ assertNull("session must get a null lock token",
+ lock.getLockToken());
+
+ // assert: session must still hold lock token
+ assertFalse("session must not hold lock token",
+ containsLockToken(superuser, lockToken));
+
+ // assert: session unable to modify node
+ try {
+ n.addNode(nodeName2, testNodeType);
+ fail("session unable to modify node");
+ } catch (LockException e) {
+ // expected
+ }
+
+ // add lock token
+ superuser.addLockToken(lockToken);
+
+ // assert: session must get a non-null lock token
+ assertNotNull("session must get a non-null lock token",
+ lock.getLockToken());
+
+ // assert: session must hold lock token
+ assertTrue("session must hold lock token",
+ containsLockToken(superuser, lock.getLockToken()));
+
+ // assert: session able to modify node
+ n.addNode(nodeName2, testNodeType);
+ }
+
+ /**
* Test session scope: other session may not access nodes that are
* locked.
*/
- public void testSessionScope() throws Exception {
+ public void testNodeLocked() throws Exception {
// create new node and lock it
- Node n = testRootNode.addNode(nodeName1, testNodeType);
- if (!n.isNodeType(mixReferenceable)) {
- n.addMixin(mixReferenceable);
- }
- if (!n.isNodeType(mixLockable)) {
- n.addMixin(mixLockable);
- }
+ Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+ n1.addMixin(mixReferenceable);
+ n1.addMixin(mixLockable);
testRootNode.save();
- // remember uuid
- String uuid = n.getUUID();
-
// lock node
- Lock lock = n.lock(false, true);
+ Lock lock = n1.lock(false, true);
- // assertion: isLive must return true
+ // assert: isLive must return true
assertTrue("Lock must be live", lock.isLive());
- // assertion: lock token must not be null for holding session
- String lockToken = n.getLock().getLockToken();
- assertNotNull("Lock token must not be null for holding session", lockToken);
-
// create new session
Session otherSuperuser = helper.getSuperuserSession();
// get same node
- n = otherSuperuser.getNodeByUUID(uuid);
+ Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
- // assertion: lock token must be null for other session
+ // assert: lock token must be null for other session
assertNull("Lock token must be null for other session",
- n.getLock().getLockToken());
+ n2.getLock().getLockToken());
- // assertion: modifying same node in other session must fail
+ // assert: modifying same node in other session must fail
try {
- n.addNode(nodeName2);
- fail("Modified node locked by other session.");
+ n2.addNode(nodeName2, testNodeType);
+ fail("modifying same node in other session must fail");
} catch (LockException e) {
// expected
}
// logout
otherSuperuser.logout();
-
- // unlock node
- superuser.getNodeByUUID(uuid).unlock();
-
- // assertion: isLive must return false
- assertFalse("Lock must be dead", lock.isLive());
}
/**
@@ -139,14 +178,168 @@
// lock child node
n2.lock(false, true);
- // lock parent node
- n1.lock(true, false);
+ // assert: unable to deep lock parent node
+ try {
+ n1.lock(true, true);
+ fail("unable to deep lock parent node");
+ } catch (LockException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Test locks are released when session logs out
+ */
+ public void testLogout() throws Exception {
+ // add node
+ Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+ n1.addMixin(mixReferenceable);
+ n1.addMixin(mixLockable);
+ testRootNode.save();
+
+ // create new session
+ Session otherSuperuser = helper.getSuperuserSession();
+
+ // get node created above
+ Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
+
+ // lock node
+ Lock lock = n2.lock(false, true);
+
+ // assert: lock must be alive
+ assertTrue("lock must be alive", lock.isLive());
+
+ // assert: node must be locked
+ assertTrue("node must be locked", n1.isLocked());
+
+ // log out
+ otherSuperuser.logout();
+
+ // assert: lock must not be alive
+ assertFalse("lock must not be alive", lock.isLive());
+
+ // assert: node must not be locked
+ assertFalse("node must not be locked", n1.isLocked());
+ }
+
+ /**
+ * Test locks may be transferred to other session
+ */
+ public void testLockTransfer() throws Exception {
+ // add node
+ Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+ n1.addMixin(mixReferenceable);
+ n1.addMixin(mixLockable);
+ testRootNode.save();
+
+ // create new session
+ Session otherSuperuser = helper.getSuperuserSession();
- // unlock child node
- n2.unlock();
+ // get node created above
+ Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
- // parent node must still hold lock
- assertTrue("parent node must still hold lock", n1.holdsLock());
+ // lock node
+ Lock lock = n2.lock(false, true);
+
+ // assert: user must get non-null token
+ assertNotNull("user must get non-null token", lock.getLockToken());
+
+ // transfer to standard session
+ String lockToken = lock.getLockToken();
+ otherSuperuser.removeLockToken(lockToken);
+ superuser.addLockToken(lockToken);
+
+ // assert: user must get null token
+ assertNull("user must get null token", lock.getLockToken());
+
+ // assert: user must get non-null token
+ assertNotNull("user must get non-null token",
+ n1.getLock().getLockToken());
+
+ // log out
+ otherSuperuser.logout();
+ }
+
+ /**
+ * Test open-scoped locks
+ */
+ public void testOpenScopedLocks() throws Exception {
+ // add node
+ Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+ n1.addMixin(mixReferenceable);
+ n1.addMixin(mixLockable);
+ testRootNode.save();
+
+ // create new session
+ Session otherSuperuser = helper.getSuperuserSession();
+
+ // get node created above
+ Node n2 = otherSuperuser.getNodeByUUID(n1.getUUID());
+
+ // lock node
+ Lock lock = n2.lock(false, false);
+
+ // transfer to standard session
+ String lockToken = lock.getLockToken();
+ otherSuperuser.removeLockToken(lockToken);
+ superuser.addLockToken(lockToken);
+
+ // log out
+ otherSuperuser.logout();
+
+ // assert: node still locked
+ assertTrue(n1.isLocked());
+ }
+
+ /**
+ * Test refresh
+ */
+ public void testRefresh() throws Exception {
+ // create new node
+ Node n = testRootNode.addNode(nodeName1, testNodeType);
+ n.addMixin(mixReferenceable);
+ n.addMixin(mixLockable);
+ testRootNode.save();
+
+ // lock node and get lock token
+ Lock lock = n.lock(false, true);
+
+ // assert: lock must be alive
+ assertTrue("lock must be alive", lock.isLive());
+
+ // assert: refresh must fail, since lock is still alive
+ try {
+ lock.refresh();
+ fail("refresh must fail, since lock is still alive");
+ } catch (LockException e) {
+ // expected
+ }
+
+ // unlock node
+ n.unlock();
+
+ // assert: lock must not be alive
+ assertFalse("lock must not be alive", lock.isLive());
+
+ // refresh
+ lock.refresh();
+
+ // assert: lock must again be alive
+ assertTrue("lock must again be alive", lock.isLive());
+ }
+
+ /**
+ * Return a flag indicating whether the indicated session contains
+ * a specific lock token
+ */
+ private boolean containsLockToken(Session session, String lockToken) {
+ String[] lt = session.getLockTokens();
+ for (int i = 0; i < lt.length; i++) {
+ if (lt[i].equals(lockToken)) {
+ return true;
+ }
+ }
+ return false;
}
}
|