You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oz...@apache.org on 2007/08/17 01:13:13 UTC
svn commit: r566884 - in /commons/proper/transaction/branches/TRANSACTION_2:
src/java/org/apache/commons/transaction/
src/java/org/apache/commons/transaction/file/
src/java/org/apache/commons/transaction/locking/
src/java/org/apache/commons/transaction...
Author: ozeigermann
Date: Thu Aug 16 16:13:11 2007
New Revision: 566884
URL: http://svn.apache.org/viewvc?view=rev&rev=566884
Log:
Readded RWLockManager based on custom ReadWriteLock implementation.
All tests pass.
Added:
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/AbstractLockManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/RWLockManager.java
Modified:
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceUndoManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileSequence.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/MemoryUndoManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/locks/ResourceRWLock.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/package.html
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceException.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceManager.java
commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/StreamableResource.java
commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java
commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java
commons/proper/transaction/branches/TRANSACTION_2/xdocs/index.xml
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java Thu Aug 16 16:13:11 2007
@@ -31,7 +31,8 @@
* implement {@link #createContext()} to create an object of that type, and
* {@link #commitCanFail()}.
*
- * @param <T>
+ * <p>
+ * This implementation is <em>thread-safe</em>.
*/
public abstract class AbstractTransactionalResourceManager<T extends AbstractTransactionalResourceManager.AbstractTxContext>
implements ManageableResourceManager {
@@ -42,9 +43,6 @@
private String name;
protected abstract T createContext();
-
- public AbstractTransactionalResourceManager() {
- }
public AbstractTransactionalResourceManager(String name) {
this.name = name;
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceManager.java Thu Aug 16 16:13:11 2007
@@ -33,6 +33,14 @@
import org.apache.commons.transaction.resource.StreamableResource;
import org.apache.commons.transaction.util.FileHelper;
+/**
+ * Default file system implementation of a {@link ResourceManager resource manager}.
+ *
+ * <p>
+ * This implementation is <b>NOT</b> <em>thread-safe</em>. Use
+ * {@link TxFileResourceManager} if you require a <em>thread-safe</em>
+ * implementation.
+ */
public class FileResourceManager implements ResourceManager<FileResourceManager.FileResource> {
private Log logger = LogFactory.getLog(getClass());
@@ -62,7 +70,7 @@
private final File file;
private final String canonicalPath;
-
+
private final String name;
protected static File getFileForResource(StreamableResource resource)
@@ -124,7 +132,7 @@
return file.exists();
}
- public List<? extends FileResource> getChildren() throws ResourceException {
+ public List<? extends FileResource> getChildren() throws ResourceException {
List<FileResource> result = new ArrayList<FileResource>();
File[] files = file.listFiles();
for (File file : files) {
@@ -140,7 +148,8 @@
* if (getPath().equals(getRootPath())) return null;
*/
File parent = file.getParentFile();
- if (parent == null) return null;
+ if (parent == null)
+ return null;
return create(parent);
}
@@ -268,7 +277,7 @@
protected FileResource create(File file) throws ResourceException {
return new FileResource(file);
}
-
+
public FileResource getChild(String name) throws ResourceException {
File child = new File(file, name);
return create(child);
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceUndoManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceUndoManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceUndoManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileResourceUndoManager.java Thu Aug 16 16:13:11 2007
@@ -4,20 +4,20 @@
public interface FileResourceUndoManager {
- public enum Code {
+ enum Code {
DELETED_DIRECTORY, UPDATED_CONTENT, CREATED
}
- public void startRecord();
+ void startRecord();
- public void undoRecord();
+ void undoRecord();
- public void forgetRecord();
+ void forgetRecord();
- public void recordCreate(File file);
+ void recordCreate(File file);
- public void recordDelete(File file);
+ void recordDelete(File file);
- public void recordUpdate(File file);
+ void recordUpdate(File file);
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileSequence.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileSequence.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileSequence.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/FileSequence.java Thu Aug 16 16:13:11 2007
@@ -38,7 +38,6 @@
* versioning values of sequences and throwing away all versions, but the
* current and the previous one.
*
- * @version $Id: FileSequence.java 493628 2007-01-07 01:42:48Z joerg $
*/
public class FileSequence {
@@ -47,7 +46,7 @@
protected final String storeDir;
/**
- * Creates a new resouce manager operation on the specified directories.
+ * Creates a new resource manager operation on the specified directories.
*
* @param storeDir
* directory where sequence information is stored
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/MemoryUndoManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/MemoryUndoManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/MemoryUndoManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/MemoryUndoManager.java Thu Aug 16 16:13:11 2007
@@ -10,7 +10,16 @@
import org.apache.commons.logging.LogFactory;
import org.apache.commons.transaction.util.FileHelper;
-// TODO: memory version to be serialized to XML using JAXB
+/**
+ * Default implementation of {@link FileResourceUndoManager}.
+ *
+ * <p>Undo information is held in memory only. Changed content streams are stored as temporary files.
+ *
+ * <p><em>Caution:</em>This implementation does not guarantee ACID properties after a JVM crash.
+ *
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ */
public class MemoryUndoManager implements FileResourceUndoManager {
private Log logger = LogFactory.getLog(getClass());
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java Thu Aug 16 16:13:11 2007
@@ -36,11 +36,38 @@
import org.apache.commons.transaction.locking.HierarchicalLockManager;
import org.apache.commons.transaction.locking.DefaultHierarchicalLockManager;
import org.apache.commons.transaction.locking.LockManager;
+import org.apache.commons.transaction.locking.RWLockManager;
import org.apache.commons.transaction.resource.ResourceException;
import org.apache.commons.transaction.resource.ResourceManager;
import org.apache.commons.transaction.resource.StreamableResource;
import org.apache.commons.transaction.util.FileHelper;
+/**
+ * Transactional file system implementation of a
+ * {@link ResourceManager resource manager}.
+ *
+ * <p>
+ * All meaningful actions that ensure ACID transactions are delegated to special
+ * components. This allows for customization of all kinds:
+ * <ul>
+ * <li>{@link #setLm(LockManager)}: Sets the {@link LockManager lock manager}
+ * that decides how to protect resources from concurrent modifications. Defaults
+ * to {@link DefaultLockManager}.
+ * <li>{@link #setUndoManager(FileResourceUndoManager)}: Sets the
+ * {@link FileResourceUndoManager undo manager} that keeps track of all changes
+ * and allows to undo them upon request.Defaults
+ * to {@link MemoryUndoManager}.
+ * </ul>
+ *
+ *
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ *
+ * @see FileResourceUndoManager
+ * @see ResourceManager
+ * @see HierarchicalLockManager
+ * @see LockManager
+ */
public class TxFileResourceManager extends
AbstractTransactionalResourceManager<TxFileResourceManager.FileTxContext> implements
ManageableResourceManager, ResourceManager<StreamableResource> {
@@ -56,7 +83,37 @@
public TxFileResourceManager(String name, String rootPath) {
super(name);
wrapped = new FileResourceManager(rootPath);
- setLm(new DefaultLockManager<Object, Object>());
+ setLm(new RWLockManager<Object, Object>());
+ }
+
+ @Override
+ public void setLm(LockManager<Object, Object> lm) {
+ super.setLm(lm);
+ hlm = new DefaultHierarchicalLockManager(getRootPath(), lm);
+ }
+
+ @Override
+ public boolean commitCanFail() {
+ return false;
+ }
+
+ @Override
+ public FileResource getResource(String path) throws ResourceException {
+ FileTxContext context = getActiveTx();
+ if (context != null) {
+ return context.getResource(path);
+ } else {
+ return wrapped.getResource(path);
+ }
+
+ }
+
+ public String getRootPath() {
+ return wrapped.getRootPath();
+ }
+
+ public void setUndoManager(FileResourceUndoManager undoManager) {
+ this.undoManager = undoManager;
}
@Override
@@ -68,10 +125,8 @@
return hlm;
}
- @Override
- public void setLm(LockManager<Object, Object> lm) {
- super.setLm(lm);
- hlm = new DefaultHierarchicalLockManager(getRootPath(), lm);
+ protected FileResourceUndoManager getUndoManager() {
+ return undoManager;
}
public class FileTxContext extends AbstractTxContext implements
@@ -104,7 +159,7 @@
getUndoManager().startRecord();
super.start(timeout, unit);
}
-
+
@Override
public String getRootPath() {
return TxFileResourceManager.this.getRootPath();
@@ -344,33 +399,6 @@
super.writeLock();
}
}
- }
-
- @Override
- public boolean commitCanFail() {
- return false;
- }
-
- public FileResource getResource(String path) throws ResourceException {
- FileTxContext context = getActiveTx();
- if (context != null) {
- return context.getResource(path);
- } else {
- return wrapped.getResource(path);
- }
-
- }
-
- public String getRootPath() {
- return wrapped.getRootPath();
- }
-
- protected FileResourceUndoManager getUndoManager() {
- return undoManager;
- }
-
- public void setUndoManager(FileResourceUndoManager undoManager) {
- this.undoManager = undoManager;
}
}
Added: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/AbstractLockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/AbstractLockManager.java?view=auto&rev=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/AbstractLockManager.java (added)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/AbstractLockManager.java Thu Aug 16 16:13:11 2007
@@ -0,0 +1,147 @@
+/*
+ * 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.commons.transaction.locking;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.locking.LockException.Code;
+
+/**
+ * Abstract implementation of {@link LockManager}.
+ *
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ */
+public abstract class AbstractLockManager<K, M> implements LockManager<K, M> {
+ private Log logger = LogFactory.getLog(getClass());
+
+ protected Map<Thread, Set<Lock>> locksForThreads = new ConcurrentHashMap<Thread, Set<Lock>>();
+
+ protected Map<Thread, Long> effectiveGlobalTimeouts = new ConcurrentHashMap<Thread, Long>();
+
+ @Override
+ public void endWork() {
+ release();
+ }
+
+ @Override
+ public void startWork(long timeout, TimeUnit unit) {
+ if (isWorking()) {
+ throw new IllegalStateException("work has already been started");
+ }
+ locksForThreads.put(Thread.currentThread(), new HashSet<Lock>());
+
+ long timeoutMSecs = unit.toMillis(timeout);
+ long now = System.currentTimeMillis();
+ long effectiveTimeout = now + timeoutMSecs;
+ effectiveGlobalTimeouts.put(Thread.currentThread(), effectiveTimeout);
+ }
+
+ protected long computeRemainingTime(Thread thread) {
+ long timeout = effectiveGlobalTimeouts.get(thread);
+ long now = System.currentTimeMillis();
+ long remaining = timeout - now;
+ return remaining;
+ }
+
+ public boolean isWorking() {
+ return locksForThreads.get(Thread.currentThread()) != null;
+ }
+
+ @Override
+ public void lock(M managedResource, K key, boolean exclusive) throws LockException {
+ long remainingTime = computeRemainingTime(Thread.currentThread());
+
+ boolean locked = tryLockInternal(managedResource, key, exclusive, remainingTime,
+ TimeUnit.MILLISECONDS);
+ if (!locked) {
+ throw new LockException(Code.TIMED_OUT, key);
+ }
+ }
+
+ @Override
+ public boolean tryLock(M managedResource, K key, boolean exclusive) {
+ return tryLockInternal(managedResource, key, exclusive, 0, TimeUnit.MILLISECONDS);
+ }
+
+ abstract protected boolean tryLockInternal(M managedResource, K key, boolean exclusive,
+ long time, TimeUnit unit) throws LockException;
+
+ protected void reportTimeout(Thread thread) throws LockException {
+ if (hasTimedOut(thread)) {
+ throw new LockException(LockException.Code.TIMED_OUT);
+ }
+ }
+
+ protected boolean hasTimedOut(Thread thread) {
+ long remainingTime = computeRemainingTime(thread);
+ return (remainingTime < 0);
+
+ }
+
+ protected void release() {
+ Set<Lock> locks = locksForThreads.get(Thread.currentThread());
+ // graceful reaction...
+ if (locks == null) {
+ return;
+ }
+ for (Lock lock : locks) {
+ lock.unlock();
+ }
+
+ locksForThreads.remove(Thread.currentThread());
+ }
+
+ protected static class KeyEntry<K, M> {
+
+ private K k;
+
+ private M m;
+
+ public KeyEntry(K k, M m) {
+ this.k = k;
+ this.m = m;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj instanceof KeyEntry) {
+ KeyEntry otherEntry = (KeyEntry) obj;
+ return (otherEntry.k.equals(k) && otherEntry.m.equals(m));
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return k.hashCode() + m.hashCode();
+ }
+
+ public String toString() {
+ return m.toString() + ":" + k.toString();
+ }
+ }
+
+}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java Thu Aug 16 16:13:11 2007
@@ -18,6 +18,23 @@
import java.util.concurrent.TimeUnit;
+/**
+ * Default implementation of the {@link HierarchicalLockManager}.
+ *
+ * <p>
+ * It splits the path into segments and non-exclusively locks each segment
+ * beginning from the root. The final segment - which is supposed to be the
+ * resource to be locked itself - can either be locked exclusively or
+ * non-exclusively. Too choose between the two, provide the flag in
+ * {@link #lockInHierarchy(Object, String, boolean)}.
+ *
+ * <p>
+ * This implementation needs an ordinary {@link LockManager lock manager} that
+ * it delegates all locking calls to.
+ *
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ */
public class DefaultHierarchicalLockManager<M> implements HierarchicalLockManager<Object, M> {
private final String rootPath;
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java Thu Aug 16 16:13:11 2007
@@ -16,11 +16,11 @@
*/
package org.apache.commons.transaction.locking;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.logging.Log;
@@ -28,103 +28,27 @@
import org.apache.commons.transaction.locking.LockException.Code;
/**
+ * Default implementation of {@link LockManager}.
*
- * @author olli
+ * <p>
+ * This is a minimal implementation that only knows a single type of lock.
+ * Read-/Write-locks are not supported. Deadlock detection is not performed.
+ * Transferring locks between threads is not possible. These limitations are due
+ * to the standard {@link Lock} and {@link ReadWriteLock} implementations.
*
- * @param <K>
- * @param <M>
+ * <p>
+ * This implementation is <em>thread-safe</em>.
*/
-public class DefaultLockManager<K, M> implements LockManager<K, M> {
+public class DefaultLockManager<K, M> extends AbstractLockManager<K, M> implements
+ LockManager<K, M> {
private Log logger = LogFactory.getLog(getClass());
protected ConcurrentHashMap<KeyEntry<K, M>, ReentrantLock> locks = new ConcurrentHashMap<KeyEntry<K, M>, ReentrantLock>();
- protected Map<Thread, CopyOnWriteArraySet<ReentrantLock>> locksForThreads = new ConcurrentHashMap<Thread, CopyOnWriteArraySet<ReentrantLock>>();
-
- protected Map<Thread, Long> effectiveGlobalTimeouts = new ConcurrentHashMap<Thread, Long>();
-
- @Override
- public void endWork() {
- release();
- }
-
- @Override
- public void startWork(long timeout, TimeUnit unit) {
- if (isWorking()) {
- throw new IllegalStateException("work has already been started");
- }
- locksForThreads.put(Thread.currentThread(), new CopyOnWriteArraySet<ReentrantLock>());
-
- long timeoutMSecs = unit.toMillis(timeout);
- long now = System.currentTimeMillis();
- long effectiveTimeout = now + timeoutMSecs;
- effectiveGlobalTimeouts.put(Thread.currentThread(), effectiveTimeout);
- }
-
- // TODO
- protected boolean checkForDeadlock() {
- return false;
-
- }
-
- protected long computeRemainingTime(Thread thread) {
- long timeout = effectiveGlobalTimeouts.get(thread);
- long now = System.currentTimeMillis();
- long remaining = timeout - now;
- return remaining;
- }
-
protected ReentrantLock create() {
return new ReentrantLock();
}
- protected static class KeyEntry<K, M> {
-
- private K k;
-
- private M m;
-
- public KeyEntry(K k, M m) {
- this.k = k;
- this.m = m;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj instanceof KeyEntry) {
- KeyEntry otherEntry = (KeyEntry) obj;
- return (otherEntry.k.equals(k) && otherEntry.m.equals(m));
- }
- return false;
- }
-
- public int hashCode() {
- return k.hashCode() + m.hashCode();
- }
- }
-
- public boolean isWorking() {
- return locksForThreads.get(Thread.currentThread()) != null;
- }
-
- @Override
- public void lock(M managedResource, K key, boolean exclusive) throws LockException {
- long remainingTime = computeRemainingTime(Thread.currentThread());
-
- boolean locked = tryLockInternal(managedResource, key, exclusive, remainingTime,
- TimeUnit.MILLISECONDS);
- if (!locked) {
- throw new LockException(Code.TIMED_OUT, key);
- }
- }
-
- @Override
- public boolean tryLock(M managedResource, K key, boolean exclusive) {
- return tryLockInternal(managedResource, key, exclusive, 0, TimeUnit.MILLISECONDS);
- }
-
protected boolean tryLockInternal(M managedResource, K key, boolean exclusive, long time,
TimeUnit unit) throws LockException {
reportTimeout(Thread.currentThread());
@@ -135,7 +59,7 @@
ReentrantLock existingLock = locks.putIfAbsent(entry, lock);
if (existingLock != null)
lock = existingLock;
- Set<ReentrantLock> locks = locksForThreads.get(Thread.currentThread());
+ Set<Lock> locks = locksForThreads.get(Thread.currentThread());
if (locks == null) {
throw new IllegalStateException("lock() can only be called after startWork()");
}
@@ -170,23 +94,16 @@
}
protected void release() {
- Set<ReentrantLock> locks = locksForThreads.get(Thread.currentThread());
+ Set<Lock> locks = locksForThreads.get(Thread.currentThread());
// graceful reaction...
if (locks == null) {
return;
}
- for (ReentrantLock lock : locks) {
- int holdCount = lock.getHoldCount();
+ for (Lock lock : locks) {
+ int holdCount = ((ReentrantLock) lock).getHoldCount();
logger.debug("Locks held by this thread: " + holdCount);
- while (true) {
- try {
- lock.unlock();
- } catch (IllegalMonitorStateException imse) {
- // We are lacking information on whether we have a read
- // lock and if so how many.
- // XXX Just free as many as possible.
- break;
- }
+ for (int i = 0; i < holdCount; i++) {
+ lock.unlock();
}
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java Thu Aug 16 16:13:11 2007
@@ -17,6 +17,7 @@
package org.apache.commons.transaction.locking;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
/**
*
@@ -26,6 +27,9 @@
* {@link #lock(Object, Object, boolean)} and
* {@link #tryLock(Object, Object, boolean)}.
*
+ * <p>
+ * You can plug in your own lock manager version most easily. However, for
+ * advanced features this will most likely require a custom implementation of {@link Lock} as well.
*
*
* @param <K>
@@ -40,7 +44,7 @@
* @param unit
* the time unit of the {@code timeout} argument
*/
- public void startWork(long timeout, TimeUnit unit);
+ void startWork(long timeout, TimeUnit unit);
/**
* Ends a block of work that has been started in
@@ -48,14 +52,14 @@
* All registered locks will be unregistered from this lock manager.
*
*/
- public void endWork();
+ void endWork();
/**
* @param managedResource
* resource for on which this block of work shall be done
*/
- public void lock(M managedResource, K key, boolean exclusive) throws LockException;
+ void lock(M managedResource, K key, boolean exclusive) throws LockException;
- public boolean tryLock(M managedResource, K key, boolean exclusive);
+ boolean tryLock(M managedResource, K key, boolean exclusive);
}
Added: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/RWLockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/RWLockManager.java?view=auto&rev=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/RWLockManager.java (added)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/RWLockManager.java Thu Aug 16 16:13:11 2007
@@ -0,0 +1,178 @@
+/*
+ * 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.commons.transaction.locking;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+import org.apache.commons.transaction.locking.LockException.Code;
+import org.apache.commons.transaction.locking.locks.ResourceRWLock;
+
+public class RWLockManager<K, M> extends AbstractLockManager<K, M> implements LockManager<K, M> {
+
+ protected ConcurrentHashMap<KeyEntry<K, M>, ResourceRWLock> locks = new ConcurrentHashMap<KeyEntry<K, M>, ResourceRWLock>();
+
+ protected ResourceRWLock create(String name) {
+ return new ResourceRWLock(name);
+ }
+
+ protected boolean tryLockInternal(M managedResource, K key, boolean exclusive, long time,
+ TimeUnit unit) throws LockException {
+ reportTimeout(Thread.currentThread());
+
+ KeyEntry<K, M> entry = new KeyEntry<K, M>(key, managedResource);
+
+ String resourceName = entry.toString();
+
+ ResourceRWLock rwlock = create(resourceName);
+ ResourceRWLock existingLock = locks.putIfAbsent(entry, rwlock);
+ if (existingLock != null)
+ rwlock = existingLock;
+ Set<Lock> locksForThisThread = locksForThreads.get(Thread.currentThread());
+ if (locksForThisThread == null) {
+ throw new IllegalStateException("lock() can only be called after startWork()");
+ }
+
+ Lock lock = exclusive ? rwlock.writeLock() : rwlock.readLock();
+
+ boolean locked;
+ if (time == 0) {
+ locked = lock.tryLock();
+ } else {
+ rwlock.registerWaiter();
+ try {
+ locked = doTrickyYetEfficientLockOnlyIfThisCanNotCauseADeadlock(lock, unit
+ .toMillis(time));
+ } finally {
+ rwlock.unregisterWaiter();
+ }
+ }
+ if (locked)
+ locksForThisThread.add(lock);
+
+ return locked;
+ }
+
+ protected boolean doTrickyYetEfficientLockOnlyIfThisCanNotCauseADeadlock(Lock lock,
+ long timeMsecs) throws LockException {
+
+ // This algorithm is devided into three parts:
+ // Note: We can be interrupted most of the time
+ //
+ // (I) prewait:
+ // Wait a fraktion of the time to see if we can acquire
+ // the lock in short time. If we can all is good and we exit
+ // signalling success. If not we need to get into a more resource
+ // consuming phase.
+ //
+ // (II) clearing of timed out threads / deadlock detection:
+ // As we have not been able to acquire the lock, yet, maybe there is
+ // deadlock. Clear all threads already timed out and afterwards
+ // check for a deadlock state. If there is one report it with an
+ // exception. If not we enter the final phase.
+ //
+ // (III) real wait:
+ // Everything is under control, we were just a little bit too
+ // impatient. So wait for the remaining time and see if the can get
+ // the lock
+ //
+
+ try {
+ boolean locked;
+
+ // (I) prewait
+
+ long startTime = System.currentTimeMillis();
+
+ // TODO this heuristic divisor really should be configurable
+ long preWaitTime = timeMsecs / 10;
+ locked = lock.tryLock(preWaitTime, TimeUnit.MILLISECONDS);
+ if (locked)
+ return true;
+
+ // (II) deadlock detect
+ cancelAllTimedOut();
+ if (wouldDeadlock(Thread.currentThread(), new HashSet<Thread>())) {
+ throw new LockException(LockException.Code.WOULD_DEADLOCK);
+ }
+
+ // (III) real wait
+ long now = System.currentTimeMillis();
+ long remainingWaitTime = timeMsecs - (now - startTime);
+ if (remainingWaitTime < 0)
+ return false;
+
+ locked = lock.tryLock(remainingWaitTime, TimeUnit.MILLISECONDS);
+ return locked;
+ } catch (InterruptedException e) {
+ throw new LockException(Code.INTERRUPTED);
+ }
+
+ }
+
+ protected boolean wouldDeadlock(Thread thread, Set<Thread> path) {
+ path.add(thread);
+ // these are our locks
+ // Note: No need to make a copy as we can be sure to iterate on our
+ // private
+ // version, as this is a CopyOnWriteArraySet!
+ Set<Lock> locks = locksForThreads.get(thread);
+ // check is necessary as the possibly offending thread might have ended
+ // before this check completes
+ if (locks != null) {
+ for (Lock lock : locks) {
+ // these are the ones waiting for one of our locks
+ // and if they wait, they wait because of me!
+ Collection<Thread> conflicts = ((ResourceRWLock.InnerLock) lock)
+ .getResourceRWLock().getQueuedThreads();
+ for (Thread conflictThread : conflicts) {
+ // this means, we have found a cycle in the wait graph
+ if (path.contains(conflictThread)) {
+ return true;
+ } else if (wouldDeadlock(conflictThread, path)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ path.remove(thread);
+ return false;
+ }
+
+ protected void cancelAllTimedOut() {
+ Set<Thread> threads = effectiveGlobalTimeouts.keySet();
+ for (Thread thread : threads) {
+ if (hasTimedOut(thread)) {
+ // TODO #1: We need to record this thread has timed out to
+ // produce
+ // a meaningful exception when it tries to continue its work
+ // TODO #2: If would be even better if we could actively release
+ // its locks, but only the thread that acquired a lock can
+ // release it. An extended implementation of ReentrantLock would
+ // help.
+ thread.interrupt();
+ }
+
+ }
+ }
+
+}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/locks/ResourceRWLock.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/locks/ResourceRWLock.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/locks/ResourceRWLock.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/locks/ResourceRWLock.java Thu Aug 16 16:13:11 2007
@@ -16,7 +16,9 @@
*/
package org.apache.commons.transaction.locking.locks;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
@@ -31,7 +33,8 @@
* <ul>
* <li>each thread can hold at most one lock level, i.e. either none, read, or
* write.
- * <li>ownership is (also partially) transferable from one thread to another (not in this initial implementation)
+ * <li>ownership is (also partially) transferable from one thread to another
+ * (not in this initial implementation)
* <li>upgrade from read-lock to write-lock is supported
* <li>information which thread holds which locks is available
* </ul>
@@ -40,26 +43,64 @@
private static final long serialVersionUID = -5452408535686743324L;
- private final ResourceRWLock.ReadLock readerLock;
+ private final ResourceRWLock.ReadLock readLock;
- private final ResourceRWLock.WriteLock writerLock;
+ private final ResourceRWLock.WriteLock writeLock;
private final Sync sync = new Sync();
+ private final String resourceName;
+
+ private final Collection<Thread> waiterThreads = new ConcurrentSkipListSet<Thread>(
+ threadComparator);
+
public ResourceRWLock() {
- readerLock = new ReadLock();
- writerLock = new WriteLock();
+ this(null);
+ }
+
+ public ResourceRWLock(String resourceName) {
+ this.resourceName = resourceName;
+ this.readLock = new ReadLock();
+ this.writeLock = new WriteLock();
}
public ResourceRWLock.WriteLock writeLock() {
- return writerLock;
+ return writeLock;
}
public ResourceRWLock.ReadLock readLock() {
- return readerLock;
+ return readLock;
}
- class ReadLock implements Lock {
+ public Collection<Thread> getQueuedThreads() {
+ ArrayList<Thread> list = new ArrayList<Thread>(sync.getQueuedThreads());
+ list.addAll(waiterThreads);
+ return list;
+ }
+
+ public void registerWaiter() {
+ Thread current = Thread.currentThread();
+ waiterThreads.add(current);
+ }
+
+ public void unregisterWaiter() {
+ Thread current = Thread.currentThread();
+ waiterThreads.remove(current);
+ }
+
+ public class InnerLock {
+ public ResourceRWLock getResourceRWLock() {
+ return ResourceRWLock.this;
+ }
+
+ }
+
+ public String toString() {
+ return readLock().toString();
+ }
+
+ class ReadLock extends InnerLock implements Lock {
+
public void lock() {
sync.acquireShared(1);
}
@@ -84,20 +125,27 @@
throw new UnsupportedOperationException();
}
- public String toString() {
+ String simpleString() {
StringBuffer buf = new StringBuffer();
- buf.append(super.toString()).append("[Read locks = ");
- buf.append("]");
+ buf.append("Read lock on ");
+ buf.append(resourceName == null ? super.toString() : resourceName);
+ buf.append("[");
Collection<Thread> readerThreads = sync.readerThreads;
for (Thread thread : readerThreads) {
buf.append(thread.getName());
buf.append(" ");
}
+ buf.append("]");
return buf.toString();
}
+
+ public String toString() {
+ return simpleString() + "\n" + getResourceRWLock().writeLock.simpleString();
+ }
}
- class WriteLock implements Lock {
+ class WriteLock extends InnerLock implements Lock {
+
public void lock() {
sync.acquire(1);
}
@@ -122,19 +170,24 @@
return sync.newCondition();
}
- public String toString() {
+ String simpleString() {
Thread o = sync.getOwner();
- return super.toString()
+ return "Write lock on " + (resourceName == null ? super.toString() : resourceName)
+ ((o == null) ? "[Unlocked]" : "[Locked by thread " + o.getName() + "]");
}
+ public String toString() {
+ return simpleString() + "\n" + getResourceRWLock().readLock.simpleString();
+ }
+
}
static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 8791542812047042797L;
- private final Collection<Thread> readerThreads = new ConcurrentSkipListSet<Thread>();
+ private final Collection<Thread> readerThreads = new ConcurrentSkipListSet<Thread>(
+ threadComparator);
private final int NO_LOCK = 0;
@@ -257,5 +310,19 @@
}
}
+
+ private static final Comparator<Thread> threadComparator = new Comparator<Thread>() {
+
+ @Override
+ public int compare(Thread o1, Thread o2) {
+ int h1 = o1.getName().hashCode();
+ int h2 = o2.getName().hashCode();
+
+ if (h1 == h2)
+ return 0;
+ return (h1 < h2 ? -1 : 1);
+ }
+
+ };
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/package.html
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/package.html?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/package.html (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/package.html Thu Aug 16 16:13:11 2007
@@ -1,118 +1,6 @@
<html>
<body>
<h1>Locking</h1>
-
-<p>Beginning with version 1.1 the Commons Transaction package features
-extended transaction support mechanisms. They are grouped around the
-new <a
-href="../apidocs/org/apache/commons/transaction/locking/LockManager2.html">lock
-manager</a> and its <a
-href="../apidocs/org/apache/commons/transaction/locking/GenericLockManager.html">implementation</a>
-that should be used as the only interface to access locks
-associated to some sort of transaction. To do so it offers you locking
-methods like <a
-href="../apidocs/org/apache/commons/transaction/locking/LockManager2.html#lock(java.lang.Object,%20java.lang.Object,%20int,%20int,%20boolean,%20long)">lock</a>
-and <a
-href="../apidocs/org/apache/commons/transaction/locking/LockManager2.html#tryLock(java.lang.Object,%20java.lang.Object,%20int,%20boolean)">tryLock</a>.</p>
-
-<p>It is important for this manager to be central and have
-knowledge of all locking operations to perform tasks like <a href="deadlock.html">deadlock
-detection</a>, incorporate <a
-href="../apidocs/org/apache/commons/transaction/locking/LockManager2.html#startGlobalTimeout(java.lang.Object,%20long)">global
-transaction timeouts</a> and add convenience methods to <a
-href="../apidocs/org/apache/commons/transaction/locking/LockManager2.html#releaseAll(java.lang.Object)">release
-all locks</a> of a transaction.</p>
-
-<p>Additional to the <a href="preference.html">preference feature</a>
-in the new <a href="../apidocs/org/apache/commons/transaction/locking/MultiLevelLock2.html">lock</a>, its <a
-href="../apidocs/org/apache/commons/transaction/locking/GenericLock.html">implementation</a>
-has some internal means to record all requests that wait for
-(partially) acquiring a lock in the sequence they occur. This is
-used by the deadlock detection in the lock manager. Another possible
-use is in custom implementations of fair scheduling lock
-mechanisms. Even though the specific protected methods
-<a
-href="../apidocs/org/apache/commons/transaction/locking/GenericLock.html#registerWaiter(org.apache.commons.transaction.locking.GenericLock.LockOwner)">registerWaiter</a>
-and
-<a
-href="../apidocs/org/apache/commons/transaction/locking/GenericLock.html#unregisterWaiter(org.apache.commons.transaction.locking.GenericLock.LockOwner)">unregisterWaiter</a>
-and field
-<a
-href="../apidocs/org/apache/commons/transaction/locking/GenericLock.html#waitingOwners">waitingOwners</a> are not made available
-through interfaces subclasses can still use them and even make their means public.</p>
-
-<h2>Deadlocks</h2>
- <p>A deadlock describes the scenario where two or more threads are
- mutually blocking each other. Each waits for the other to release its
- lock which will never happen as no thread can perform any
- action. Consider the following figure for illustration this. Thread #1 holds locks
-a, b and c and waits for lock d. Thread #2 holds locks d and e and
-waits for lock b. As lock d is owner by Thread #2, Thread #1 can not
- continue before Thread #2 releases it. Now Thread #2 waits for lock b
- which is turn is owner by Thread #1.</p>
-
-<center>
-<img src="deadlock1.png"/>
-</center>
-
-<p> Neither thread will ever be able
- to release any locks as both executions are blocked: the whole scene is dead!
-</p>
-
-<h3>How does deadlock detection work?</h3>
-
-<p>Theoretically, deadlock detection is pretty simple. First you have threads
-that own locks, then you have threads that wait for locks to
-acquire. Now you can think of a directed graph where you have
-threads and locks being nodes and each lock ownership and lock wait is
-a vertex that connects those nodes. When you have a cycle in that
-graph, i.e. when you can traverse the (directed!) graph starting from a certain
-node and you can reach that node again, you have a deadlock. This
-means a thread waits for itself to release a lock to
-finally acquire a desired lock. Obviously, this will never happen
-without taking additional actions from the outside. Such an action
-could be to release the block for one thread telling it that there was
-a deadlock and the thread shall resolve it now it is able to perform actions.</p>
-<p>This can be illustrated with the above figure. Traversing the graph starting from Thread #1 going
-over lock b, Thread #2, lock d and finally Thread #1 (again) reveals
-the cycle and thus the deadlock. Note that of course you could just as
-well have started with Thread #2 and would have revealed the same cycle.
-</p>
-
-<h3>How does deadlock detection work for Commons Transaction?</h3>
-
-<p>In case of commons transaction things are a bit more complicated
-as a lock can have multiple (compatible) threads or better to say
-owners - lock ownership is not directly tied to the thread accessing
-the lock, but to an Object called owner. It can well be possible
-that an owner can both (partially) own a lock and wait for it as
-well. This may sound confusing, but actually becomes pretty obvious
-when you look at this example. There is a read/write lock and both owner #1
-and #2 hold a read lock which of course is compatible. Now when owner
-#1 ties to acquire a write lock it will have to wait for owner #2 to
-release its read lock. In such a case owner #1 both (partially) holds
-the lock and waits for it as illustrated by the following figure.</p>
-
-<center>
-<img src="deadlock2.png"/>
-</center>
-
-<p>The above algorithm would detect a deadlock. This of course is not there
-as owner #1 is blocked, but owner #2 is not and may finally release the read
-lock. This means the scenario is not dead.</p>
-<p>Obviously, the wait set of the lock is the problem. The graph does not
-tell us why owner #1 is waiting. In fact it does not wait for owner #1, but
-only for owner #2. Lookig at the next figure we see this. Both owners
-only own part of the lock and owner #1 is waiting for that part of
-the lock that owner #2 owns.
-</p>
-
-<center>
-<img src="deadlock3.png"/>
-</center>
-
-<p>Thus the algorithm has to be modified in such a way that only the
- real conflicting parts of the locks are taken into consideration. </p>
-
+<p>
</body>
</html>
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java Thu Aug 16 16:13:11 2007
@@ -27,8 +27,8 @@
import org.apache.commons.transaction.AbstractTransactionalResourceManager;
import org.apache.commons.transaction.AbstractTransactionalResourceManager.AbstractTxContext;
-import org.apache.commons.transaction.locking.DefaultLockManager;
import org.apache.commons.transaction.locking.LockManager;
+import org.apache.commons.transaction.locking.RWLockManager;
/**
* Map featuring transactional control.
@@ -47,6 +47,9 @@
* This implementation wraps a map of type {@link ConcurrentHashMap}. All
* internal synchronization is delegated to this class.
*
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ *
* @see OptimisticTxMap
* @see PessimisticTxMap
* @see ConcurrentHashMap
@@ -61,7 +64,7 @@
}
public BasicTxMap(String name) {
- this(name, new DefaultLockManager<Object, Object>());
+ this(name, new RWLockManager<Object, Object>());
}
public Map<K, V> getWrappedMap() {
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java Thu Aug 16 16:13:11 2007
@@ -27,9 +27,9 @@
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.apache.commons.transaction.locking.DefaultLockManager;
import org.apache.commons.transaction.locking.LockException;
import org.apache.commons.transaction.locking.LockManager;
+import org.apache.commons.transaction.locking.RWLockManager;
/**
* Map featuring transactional control.
@@ -44,6 +44,9 @@
*
* <p>This implementation wraps a map of type {@link ConcurrentHashMap}.
*
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ *
* @see BasicTxMap
* @see PessimisticTxMap
* @see ConcurrentHashMap
@@ -60,7 +63,7 @@
private long accessTimeout = 1000 * 30; // 30 seconds
public OptimisticTxMap(String name) {
- this(name, new DefaultLockManager<Object, Object>());
+ this(name, new RWLockManager<Object, Object>());
}
public OptimisticTxMap(String name, LockManager<Object, Object> lm) {
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java Thu Aug 16 16:13:11 2007
@@ -23,8 +23,8 @@
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.apache.commons.transaction.locking.DefaultLockManager;
import org.apache.commons.transaction.locking.LockManager;
+import org.apache.commons.transaction.locking.RWLockManager;
/**
* Map featuring transactional control.
@@ -42,6 +42,9 @@
* <p>
* This implementation wraps a map of type {@link ConcurrentHashMap}.
*
+ * <p>
+ * This implementation is <em>thread-safe</em>.
+ *
* @see BasicTxMap
* @see OptimisticTxMap
* @see ConcurrentHashMap
@@ -51,7 +54,7 @@
private ReadWriteLock globalLock = new ReentrantReadWriteLock();
public PessimisticTxMap(String name) {
- this(name, new DefaultLockManager<Object, Object>());
+ this(name, new RWLockManager<Object, Object>());
}
public PessimisticTxMap(String name, LockManager<Object, Object> lm) {
@@ -90,7 +93,6 @@
LockingTxContext txContext = (LockingTxContext) getActiveTx();
if (txContext != null) {
txContext.writeLock(key);
- // XXX fake intention lock (prohibits global WRITE)
}
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceException.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceException.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceException.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceException.java Thu Aug 16 16:13:11 2007
@@ -16,7 +16,6 @@
*/
package org.apache.commons.transaction.resource;
-import java.io.IOException;
public class ResourceException extends Exception {
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceManager.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/ResourceManager.java Thu Aug 16 16:13:11 2007
@@ -16,9 +16,24 @@
*/
package org.apache.commons.transaction.resource;
-
+/**
+ * Interface for a manager on resources. All meaningful work is done using the
+ * interface for the resource.
+ */
public interface ResourceManager<R> {
+ /**
+ * Gets the resource denoted by the path
+ *
+ * @param path the path of the resource
+ * @return the resource denoted by the path
+ * @throws ResourceException in case anything goes fatally wrong
+ */
R getResource(String path) throws ResourceException;
+ /**
+ * Gets the root path of this manager.
+ *
+ * @return the root path or <code>null</code> if no applicable
+ */
String getRootPath();
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/StreamableResource.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/StreamableResource.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/StreamableResource.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/resource/StreamableResource.java Thu Aug 16 16:13:11 2007
@@ -16,21 +16,61 @@
*/
package org.apache.commons.transaction.resource;
+import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
+/**
+ * Interface for a resource that has a stream and properties. The resource is
+ * organized in a hierarchy.
+ *
+ * <p>
+ * This can be a direct match for {@link File}.
+ *
+ */
public interface StreamableResource {
+
+ /**
+ * Gets the full path of this resource
+ *
+ * @return the full path
+ */
String getPath();
+
+ /**
+ * Gets the name, i.e. the last segment of the {@link #getPath() path}.
+ *
+ * @return the name
+ */
String getName();
+ /**
+ * Checks whether this resource is a directory, i.e. whether it can have
+ * children. Note that a resource can be both a directory and a file.
+ *
+ * @return <code>true</code> if this resource can have children
+ */
boolean isDirectory();
+ /**
+ * Checks whether this resource is a file, i.e. whether it contains a
+ * content stream. Note that a resource can be both a directory and a file.
+ *
+ * @return <code>true</code> if this resource contains a content stream
+ */
boolean isFile();
+ /**
+ * Gets the children of the resource.
+ *
+ * @return a list of children (empty if this is a file), never <code>null</code>
+ * @throws ResourceException in case anything goes fatally wrong
+ */
List<? extends StreamableResource> getChildren() throws ResourceException;
StreamableResource getParent() throws ResourceException;
+
StreamableResource getChild(String name) throws ResourceException;
InputStream readStream() throws ResourceException;
@@ -52,8 +92,9 @@
Object getProperty(String name);
void setProperty(String name, Object newValue);
+
void removeProperty(String name);
-
+
void readLock();
void writeLock();
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java Thu Aug 16 16:13:11 2007
@@ -16,6 +16,8 @@
*/
package org.apache.commons.transaction.file;
+import static junit.framework.Assert.fail;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -25,12 +27,9 @@
import java.util.concurrent.TimeUnit;
import junit.framework.JUnit4TestAdapter;
-import static junit.framework.Assert.*;
-import org.junit.Test;
import org.apache.commons.transaction.file.FileResourceManager.FileResource;
-import org.apache.commons.transaction.locking.DefaultLockManager;
-import org.apache.commons.transaction.locking.LockManager;
+import org.junit.Test;
public class TxFileResourceManagerTest {
@@ -40,6 +39,10 @@
return new JUnit4TestAdapter(TxFileResourceManagerTest.class);
}
+ public static void main(java.lang.String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
private static final void createFiles(String[] filePaths) {
createFiles(filePaths, null, null);
}
@@ -190,8 +193,5 @@
}
}
-
- public static void main(java.lang.String[] args) {
- junit.textui.TestRunner.run(suite());
- }
+
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java Thu Aug 16 16:13:11 2007
@@ -94,4 +94,103 @@
}
}
+ @Test
+ public void testConflict() {
+ log.info("Checking concurrent conflict resolvation features");
+
+ final PessimisticTxMap<String, String> txMap1 = new PessimisticTxMap<String, String>(
+ "txMap1");
+ final Map map1 = txMap1.getWrappedMap();
+
+ final RendezvousBarrier restart = new RendezvousBarrier("restart", TIMEOUT);
+
+ int conflictingRuns = 0;
+ int runs = 25;
+
+ for (int i = 0; i < runs; i++) {
+ System.out.print(".");
+
+ final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier("deadlock" + i,
+ TIMEOUT);
+
+ Thread thread1 = new Thread(new Runnable() {
+ public void run() {
+ txMap1.startTransaction(5, TimeUnit.SECONDS);
+ try {
+ // first both threads get a lock, this one on key2
+ txMap1.put("key2", "value2");
+ synchronized (deadlockBarrier1) {
+ deadlockBarrier1.meet();
+ deadlockBarrier1.reset();
+ }
+ // if I am first, the other thread will be dead, i.e.
+ // exactly one
+ txMap1.put("key1", "value2");
+ txMap1.commitTransaction();
+ } catch (LockException le) {
+ assertEquals(le.getCode(), LockException.Code.WOULD_DEADLOCK);
+ deadlockCnt++;
+ txMap1.rollbackTransaction();
+ } catch (InterruptedException ie) {
+ } finally {
+ try {
+ synchronized (restart) {
+ restart.meet();
+ restart.reset();
+ }
+ } catch (InterruptedException ie) {
+ }
+
+ }
+ }
+ }, "Thread1");
+
+ thread1.start();
+
+ txMap1.startTransaction(5, TimeUnit.SECONDS);
+ try {
+ // first both threads get a lock, this one on key1
+ txMap1.get("key1");
+ synchronized (deadlockBarrier1) {
+ try {
+ deadlockBarrier1.meet();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ deadlockBarrier1.reset();
+ }
+ // if I am first, the other thread will be dead, i.e. exactly
+ // one
+ txMap1.get("key2");
+ txMap1.commitTransaction();
+ } catch (LockException le) {
+ assertEquals(le.getCode(), LockException.Code.WOULD_DEADLOCK);
+ deadlockCnt++;
+ txMap1.rollbackTransaction();
+ } finally {
+ try {
+ synchronized (restart) {
+ restart.meet();
+ restart.reset();
+ }
+ } catch (InterruptedException ie) {
+ }
+
+ }
+
+ // XXX in special scenarios the current implementation might cause
+ // both owners to be deadlock victims
+ if (deadlockCnt != 1) {
+ // log.warn("More than one thread was deadlock victim!");
+ conflictingRuns++;
+ }
+ assertTrue(deadlockCnt >= 1);
+ deadlockCnt = 0;
+ }
+ System.out.println();
+ System.out.println("Of the " + runs + " there were " + conflictingRuns
+ + " runs that rolled back both transactions!");
+ }
+
}
Modified: commons/proper/transaction/branches/TRANSACTION_2/xdocs/index.xml
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/xdocs/index.xml?view=diff&rev=566884&r1=566883&r2=566884
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/xdocs/index.xml (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/xdocs/index.xml Thu Aug 16 16:13:11 2007
@@ -75,9 +75,6 @@
can do that for you:
<ul>
<li>
- benefit from automatic deadlock recovery
- </li>
- <li>
be sure you never forget to release a
lock again
</li>
@@ -89,6 +86,9 @@
have an out-of-the-box solution for
hierarchical locks
</li>
+ <li>
+ plug in your own custom or advanced solutions
+ </li>
</ul>
</li>
<li>need transactional access to maps</li>
@@ -122,8 +122,7 @@
</b>
Interfaces and implementations for lock
managers. Lock managers help you to keep control
- over your locks. All current implementations
- also perform deadlock detection.
+ over your locks.
</li>
<li>