📄 txlockmanagerimpl.java
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.jackrabbit.webdav.jcr.transaction;import org.apache.jackrabbit.util.Text;import org.apache.jackrabbit.webdav.DavConstants;import org.apache.jackrabbit.webdav.DavException;import org.apache.jackrabbit.webdav.DavResource;import org.apache.jackrabbit.webdav.DavResourceLocator;import org.apache.jackrabbit.webdav.DavServletResponse;import org.apache.jackrabbit.webdav.WebdavResponse;import org.apache.jackrabbit.webdav.jcr.JcrDavException;import org.apache.jackrabbit.webdav.jcr.JcrDavSession;import org.apache.jackrabbit.webdav.lock.ActiveLock;import org.apache.jackrabbit.webdav.lock.LockInfo;import org.apache.jackrabbit.webdav.lock.LockManager;import org.apache.jackrabbit.webdav.lock.Scope;import org.apache.jackrabbit.webdav.lock.Type;import org.apache.jackrabbit.webdav.transaction.TransactionConstants;import org.apache.jackrabbit.webdav.transaction.TransactionInfo;import org.apache.jackrabbit.webdav.transaction.TransactionResource;import org.apache.jackrabbit.webdav.transaction.TxActiveLock;import org.apache.jackrabbit.webdav.transaction.TxLockManager;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.jcr.Item;import javax.jcr.PathNotFoundException;import javax.jcr.RepositoryException;import javax.jcr.Session;import javax.transaction.xa.XAException;import javax.transaction.xa.XAResource;import javax.transaction.xa.Xid;import java.util.HashMap;import java.util.Iterator;import java.util.IdentityHashMap;import java.util.Map;/** * <code>TxLockManagerImpl</code> manages locks with locktype * '{@link TransactionConstants#TRANSACTION dcr:transaction}'. * <p/> */ //todo: removing all expired locks //todo: 'local' and 'global' are not accurate terms in the given context > replace /*todo: the usage of the 'global' transaction is not according to the JTA specification, which explicitely requires any transaction present on a servlet to be completed before the service method returns. Starting/completing transactions on the session object, which is possible with the jackrabbit implementation is a hack.*/ /*todo: review of this transaction part is therefore required. Is there a use-case for those 'global' transactions at all...*/public class TxLockManagerImpl implements TxLockManager { private static Logger log = LoggerFactory.getLogger(TxLockManagerImpl.class); private TransactionMap map = new TransactionMap(); private Map listeners = new IdentityHashMap(); /** * Create a new lock. * * @param lockInfo as present in the request body. * @param resource * @return the lock * @throws DavException if the lock could not be obtained. * @throws IllegalArgumentException if the resource is <code>null</code> or * does not implement {@link TransactionResource} interface. * @see LockManager#createLock(org.apache.jackrabbit.webdav.lock.LockInfo, org.apache.jackrabbit.webdav.DavResource) */ public ActiveLock createLock(LockInfo lockInfo, DavResource resource) throws DavException { if (resource == null || !(resource instanceof TransactionResource)) { throw new IllegalArgumentException("Invalid resource"); } return createLock(lockInfo, (TransactionResource) resource); } /** * Create a new lock. * * @param lockInfo * @param resource * @return the lock * @throws DavException if the request lock has the wrong lock type or if * the lock could not be obtained for any reason. */ private synchronized ActiveLock createLock(LockInfo lockInfo, TransactionResource resource) throws DavException { if (!lockInfo.isDeep() || !TransactionConstants.TRANSACTION.equals(lockInfo.getType())) { throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED); } ActiveLock existing = getLock(lockInfo.getType(), lockInfo.getScope(), resource); if (existing != null) { throw new DavException(DavServletResponse.SC_LOCKED); } // TODO: check for locks on member resources is required as well for lock is always deep! Transaction tx = createTransaction(resource.getLocator(), lockInfo); tx.start(resource); // keep references to this lock addReferences(tx, getMap(resource), resource); return tx.getLock(); } /** * Build the transaction object associated by the lock. * * @param locator * @param lockInfo * @return */ private Transaction createTransaction(DavResourceLocator locator, LockInfo lockInfo) { if (TransactionConstants.GLOBAL.equals(lockInfo.getScope())) { return new GlobalTransaction(locator, new TxActiveLock(lockInfo)); } else { return new LocalTransaction(locator, new TxActiveLock(lockInfo)); } } /** * Refresh the lock indentified by the given lock token. * * @param lockInfo * @param lockToken * @param resource * @return the lock * @throws DavException * @throws IllegalArgumentException if the resource is <code>null</code> or * does not implement {@link TransactionResource} interface. * @see LockManager#refreshLock(org.apache.jackrabbit.webdav.lock.LockInfo, String, org.apache.jackrabbit.webdav.DavResource) */ public ActiveLock refreshLock(LockInfo lockInfo, String lockToken, DavResource resource) throws DavException { if (resource == null || !(resource instanceof TransactionResource)) { throw new IllegalArgumentException("Invalid resource"); } return refreshLock(lockInfo, lockToken, (TransactionResource) resource); } /** * Reset the timeout of the lock identified by the given lock token. * * @param lockInfo * @param lockToken * @param resource * @return * @throws DavException if the lockdid not exist or is expired. */ private synchronized ActiveLock refreshLock(LockInfo lockInfo, String lockToken, TransactionResource resource) throws DavException { TransactionMap responsibleMap = getMap(resource); Transaction tx = responsibleMap.get(lockToken); if (tx == null) { throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "No valid transaction lock found for resource '" + resource.getResourcePath() + "'"); } else if (tx.getLock().isExpired()) { removeExpired(tx, responsibleMap, resource); throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Transaction lock for resource '" + resource.getResourcePath() + "' was already expired."); } else { tx.getLock().setTimeout(lockInfo.getTimeout()); } return tx.getLock(); } /** * Throws UnsupportedOperationException. * * @param lockToken * @param resource * @throws DavException * @see LockManager#releaseLock(String, org.apache.jackrabbit.webdav.DavResource) */ public void releaseLock(String lockToken, DavResource resource) throws DavException { throw new UnsupportedOperationException("A transaction lock can only be release with a TransactionInfo object and a lock token."); } /** * Release the lock identified by the given lock token. * * @param lockInfo * @param lockToken * @param resource * @throws DavException */ public synchronized void releaseLock(TransactionInfo lockInfo, String lockToken, TransactionResource resource) throws DavException { if (resource == null) { throw new IllegalArgumentException("Resource must not be null."); } TransactionMap responsibleMap = getMap(resource); Transaction tx = responsibleMap.get(lockToken); if (tx == null) { throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "No transaction lock found for resource '" + resource.getResourcePath() + "'"); } else if (tx.getLock().isExpired()) { removeExpired(tx, responsibleMap, resource); throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Transaction lock for resource '" + resource.getResourcePath() + "' was already expired."); } else { if (lockInfo.isCommit()) { TransactionListener[] txListeners; synchronized (listeners) { txListeners = (TransactionListener[]) listeners.values().toArray(new TransactionListener[0]); } for (int i = 0; i < txListeners.length; i++) { txListeners[i].beforeCommit(resource, lockToken); } DavException ex = null; try { tx.commit(resource); } catch (DavException e) { ex = e; } for (int i = 0; i < txListeners.length; i++) { txListeners[i].afterCommit(resource, lockToken, ex == null); } if (ex != null) { throw ex; } } else { tx.rollback(resource); } removeReferences(tx, responsibleMap, resource); } } /** * Always returns null * * @param type * @param scope * @param resource * @return null * @see #getLock(Type, Scope, TransactionResource) * @see LockManager#getLock(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope, org.apache.jackrabbit.webdav.DavResource) */ public ActiveLock getLock(Type type, Scope scope, DavResource resource) { return null; } /** * Returns true if the given lock token belongs to a lock that applies to * the given resource, false otherwise. The token may either be retrieved * from the {@link DavConstants#HEADER_LOCK_TOKEN Lock-Token header} or * from the {@link TransactionConstants#HEADER_TRANSACTIONID TransactionId header}. * * @param token * @param resource * @return * @see LockManager#hasLock(String token, DavResource resource) */ public boolean hasLock(String token, DavResource resource) { return getLock(token, null, resource) != null; } /** * Return the lock applied to the given resource or <code>null</code> * * @param type * @param scope * @param resource * @return lock applied to the given resource or <code>null</code> * @see LockManager#getLock(Type, Scope, DavResource) * todo: is it correct to return one that specific lock, the current session is token-holder of? */ public ActiveLock getLock(Type type, Scope scope, TransactionResource resource) { ActiveLock lock = null; if (TransactionConstants.TRANSACTION.equals(type)) { String[] sessionTokens = resource.getSession().getLockTokens(); int i = 0; while (lock == null && i < sessionTokens.length) { String lockToken = sessionTokens[i]; lock = getLock(lockToken, scope, resource); i++; } } return lock; } //-----------------------------< listener support >------------------------- /** * Adds a transaction listener to this <code>TxLockManager</code>. * @param listener the listener to add. */ public void addTransactionListener(TransactionListener listener) { synchronized (listeners) { listeners.put(listener, listener); } } /** * Removes a transaction listener from this <code>TxLockManager</code>. * @param listener the listener to remove. */ public void removeTransactionListener(TransactionListener listener) { synchronized (listeners) { listeners.remove(listener); } } /** * @param lockToken * @param resource * @return */ private ActiveLock getLock(String lockToken, Scope scope, DavResource resource) { if (!(resource instanceof TransactionResource)) { log.warn("TransactionResource expected"); return null; } ActiveLock lock = null; Transaction tx = null; TransactionMap m = map; // check if main-map contains that txId if (m.containsKey(lockToken)) { tx = m.get(lockToken); } else { // look through all the nested tx-maps (i.e. global txs) for the given txId Iterator it = m.values().iterator(); while (it.hasNext() && tx == null) { Transaction txMap = (Transaction) it.next(); if (!txMap.isLocal()) { m = ((TransactionMap) txMap); if (m.containsKey(lockToken)) { tx = ((TransactionMap) txMap).get(lockToken); } } } } if (tx != null) { if (tx.getLock().isExpired()) { removeExpired(tx, m, (TransactionResource) resource); } else if (tx.appliesToResource(resource) && (scope == null || tx.getLock().getScope().equals(scope))) { lock = tx.getLock(); } } return lock; } /** * Return the map that may contain a transaction lock for the given resource. * In case the resource provides a transactionId, the map must be a * repository transaction that is identified by the given id and which in * turn can act as map. * * @param resource * @return responsible map. * @throws DavException if no map could be retrieved. */ private TransactionMap getMap(TransactionResource resource) throws DavException { String txKey = resource.getTransactionId(); if (txKey == null) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -