Author: tolusha
Date: 2010-09-02 07:19:11 -0400 (Thu, 02 Sep 2010)
New Revision: 3047
Added:
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/AbstractCacheableLockManager.java
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheLockImpl.java
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableLockManager.java
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableSessionLockManager.java
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/LockData.java
Log:
EXOJCR-831: move classes to dedicated packages
Added:
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/AbstractCacheableLockManager.java
===================================================================
---
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/AbstractCacheableLockManager.java
(rev 0)
+++
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/AbstractCacheableLockManager.java 2010-09-02
11:19:11 UTC (rev 3047)
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.impl.core.lock.cacheable;
+
+import org.exoplatform.management.annotations.Managed;
+import org.exoplatform.management.annotations.ManagedDescription;
+import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
+import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.dataflow.ChangesLogIterator;
+import org.exoplatform.services.jcr.dataflow.CompositeChangesLog;
+import org.exoplatform.services.jcr.dataflow.DataManager;
+import org.exoplatform.services.jcr.dataflow.ItemState;
+import org.exoplatform.services.jcr.dataflow.ItemStateChangesLog;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
+import org.exoplatform.services.jcr.dataflow.TransactionChangesLog;
+import org.exoplatform.services.jcr.dataflow.persistent.ItemsPersistenceListener;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.PropertyData;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.impl.Constants;
+import org.exoplatform.services.jcr.impl.core.SessionDataManager;
+import org.exoplatform.services.jcr.impl.core.lock.LockRemover;
+import org.exoplatform.services.jcr.impl.core.lock.SessionLockManager;
+import org.exoplatform.services.jcr.impl.dataflow.TransientItemData;
+import org.exoplatform.services.jcr.impl.dataflow.TransientPropertyData;
+import
org.exoplatform.services.jcr.impl.dataflow.persistent.WorkspacePersistentDataManager;
+import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
+import org.exoplatform.services.jcr.observation.ExtendedEvent;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.security.IdentityConstants;
+import org.picocontainer.Startable;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+/**
+ * Created by The eXo Platform SAS.
+ *
+ * <br/>Date:
+ *
+ * @author <a href="karpenko.sergiy(a)gmail.com">Karpenko Sergiy</a>
+ * @version $Id: AbstractCacheableLockManagerImpl.java 2806 2010-07-21 08:00:15Z tolusha
$
+ */
+public abstract class AbstractCacheableLockManager implements CacheableLockManager,
ItemsPersistenceListener, Startable
+{
+ /**
+ * The name to property time out.
+ */
+ public static final String TIME_OUT = "time-out";
+
+ /**
+ * Default lock time out. 30min
+ */
+ public static final long DEFAULT_LOCK_TIMEOUT = 1000 * 60 * 30;
+
+ /**
+ * Data manager.
+ */
+ protected final DataManager dataManager;
+
+ /**
+ * Run time lock time out.
+ */
+ protected long lockTimeOut;
+
+ /**
+ * Lock remover thread.
+ */
+ protected LockRemover lockRemover;
+
+ /**
+ * SessionLockManagers that uses this LockManager.
+ */
+ protected Map<String, CacheableSessionLockManager> sessionLockManagers;
+
+ /**
+ * The current Transaction Manager
+ */
+ protected TransactionManager tm;
+
+ /**
+ * Logger
+ */
+ private final Log LOG =
ExoLogger.getLogger("exo.jcr.component.core.AbstractCacheableLockManager");
+
+ protected LockActionNonTxAware<Integer, Object> getNumLocks;
+
+ protected LockActionNonTxAware<Boolean, Object> hasLocks;
+
+ protected LockActionNonTxAware<Boolean, String> isLockLive;
+
+ protected LockActionNonTxAware<Object, LockData> refresh;
+
+ protected LockActionNonTxAware<Boolean, String> lockExist;
+
+ protected LockActionNonTxAware<LockData, String> getLockDataById;
+
+ protected LockActionNonTxAware<List<LockData>, Object> getLockList;
+
+ /**
+ * Constructor.
+ *
+ * @param dataManager - workspace persistent data manager
+ * @param config - workspace entry
+ * @param transactionManager
+ * the transaction manager
+ * @throws RepositoryConfigurationException
+ */
+ public AbstractCacheableLockManager(WorkspacePersistentDataManager dataManager,
WorkspaceEntry config,
+ TransactionManager transactionManager) throws RepositoryConfigurationException
+ {
+ if (config.getLockManager() != null)
+ {
+ if (config.getLockManager().getParameters() != null
+ && config.getLockManager().getParameterValue(TIME_OUT, null) !=
null)
+ {
+ long timeOut = config.getLockManager().getParameterTime(TIME_OUT);
+ lockTimeOut = timeOut > 0 ? timeOut : DEFAULT_LOCK_TIMEOUT;
+ }
+ else
+ {
+ lockTimeOut =
+ config.getLockManager().getTimeout() > 0 ?
config.getLockManager().getTimeout() : DEFAULT_LOCK_TIMEOUT;
+ }
+ }
+ else
+ {
+ lockTimeOut = DEFAULT_LOCK_TIMEOUT;
+ }
+
+ this.dataManager = dataManager;
+ this.sessionLockManagers = new ConcurrentHashMap<String,
CacheableSessionLockManager>();
+ this.tm = transactionManager;
+
+ dataManager.addItemPersistenceListener(this);
+ }
+
+ @Managed
+ @ManagedDescription("Remove the expired locks")
+ public void cleanExpiredLocks()
+ {
+ removeExpired();
+ }
+
+ public long getDefaultLockTimeOut()
+ {
+ return lockTimeOut;
+ }
+
+ @Managed
+ @ManagedDescription("The number of active locks")
+ public int getNumLocks()
+ {
+ try
+ {
+ return executeLockActionNonTxAware(getNumLocks, null);
+ }
+ catch (LockException e)
+ {
+ // ignore me will never occur
+ }
+ return -1;
+ }
+
+ /**
+ * Indicates if some locks have already been created
+ */
+ protected boolean hasLocks()
+ {
+ try
+ {
+ return executeLockActionNonTxAware(hasLocks, null);
+ }
+ catch (LockException e)
+ {
+ // ignore me will never occur
+ }
+ return true;
+ }
+
+ /**
+ * Return new instance of session lock manager.
+ */
+ public SessionLockManager getSessionLockManager(String sessionId, SessionDataManager
transientManager)
+ {
+ CacheableSessionLockManager sessionManager = new
CacheableSessionLockManager(sessionId, this, transientManager);
+ sessionLockManagers.put(sessionId, sessionManager);
+ return sessionManager;
+ }
+
+ /**
+ * Check is LockManager contains lock. No matter it is in pending or persistent
state.
+ *
+ * @param nodeId - locked node id
+ * @return
+ */
+ public boolean isLockLive(String nodeId) throws LockException
+ {
+ try
+ {
+ return executeLockActionNonTxAware(isLockLive, nodeId);
+ }
+ catch (LockException e)
+ {
+ // ignore me will never occur
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTXAware()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onSaveItems(ItemStateChangesLog changesLog)
+ {
+ List<PlainChangesLog> chengesLogList = new
ArrayList<PlainChangesLog>();
+ if (changesLog instanceof TransactionChangesLog)
+ {
+ ChangesLogIterator logIterator =
((TransactionChangesLog)changesLog).getLogIterator();
+
+ while (logIterator.hasNextLog())
+ {
+ chengesLogList.add(logIterator.nextLog());
+ }
+ }
+ else if (changesLog instanceof PlainChangesLog)
+ {
+ chengesLogList.add((PlainChangesLog)changesLog);
+ }
+ else if (changesLog instanceof CompositeChangesLog)
+ {
+ for (ChangesLogIterator iter =
((CompositeChangesLog)changesLog).getLogIterator(); iter.hasNextLog();)
+ {
+ chengesLogList.add(iter.nextLog());
+ }
+ }
+
+ List<LockOperationContainer> containers = new
ArrayList<LockOperationContainer>();
+
+ for (PlainChangesLog currChangesLog : chengesLogList)
+ {
+ String sessionId = currChangesLog.getSessionId();
+
+ String nodeIdentifier;
+ try
+ {
+ switch (currChangesLog.getEventType())
+ {
+ case ExtendedEvent.LOCK :
+ if (currChangesLog.getSize() < 2)
+ {
+ LOG.error("Incorrect changes log of type ExtendedEvent.LOCK
size=" + currChangesLog.getSize()
+ + "<2 \n" + currChangesLog.dump());
+ break;
+ }
+ nodeIdentifier =
currChangesLog.getAllStates().get(0).getData().getParentIdentifier();
+
+ CacheableSessionLockManager session =
sessionLockManagers.get(sessionId);
+ if (session != null &&
session.containsPendingLock(nodeIdentifier))
+ {
+ containers.add(new LockOperationContainer(nodeIdentifier,
currChangesLog.getSessionId(),
+ ExtendedEvent.LOCK));
+ }
+ else
+ {
+ LOG.error("Lock must exist in pending locks.");
+ }
+ break;
+ case ExtendedEvent.UNLOCK :
+ if (currChangesLog.getSize() < 2)
+ {
+ LOG.error("Incorrect changes log of type ExtendedEvent.UNLOCK
size=" + currChangesLog.getSize()
+ + "<2 \n" + currChangesLog.dump());
+ break;
+ }
+
+ containers.add(new
LockOperationContainer(currChangesLog.getAllStates().get(0).getData()
+ .getParentIdentifier(), currChangesLog.getSessionId(),
ExtendedEvent.UNLOCK));
+ break;
+ default :
+ HashSet<String> removedLock = new HashSet<String>();
+ for (ItemState itemState : currChangesLog.getAllStates())
+ {
+ // this is a node and node is locked
+ if (itemState.getData().isNode() &&
lockExist(itemState.getData().getIdentifier()))
+ {
+ nodeIdentifier = itemState.getData().getIdentifier();
+ if (itemState.isDeleted())
+ {
+ removedLock.add(nodeIdentifier);
+ }
+ else if (itemState.isAdded() || itemState.isRenamed() ||
itemState.isUpdated())
+ {
+ removedLock.remove(nodeIdentifier);
+ }
+ }
+ }
+ for (String identifier : removedLock)
+ {
+ containers.add(new LockOperationContainer(identifier,
currChangesLog.getSessionId(),
+ ExtendedEvent.UNLOCK));
+ }
+ break;
+ }
+ }
+ catch (IllegalStateException e)
+ {
+ LOG.error(e.getLocalizedMessage(), e);
+ }
+ }
+
+ // sort locking and unlocking operations to avoid deadlocks
+ Collections.sort(containers);
+ for (LockOperationContainer container : containers)
+ {
+ try
+ {
+ container.apply();
+ }
+ catch (LockException e)
+ {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Class containing operation type (LOCK or UNLOCK) and all the needed information
like node uuid and session id.
+ */
+ private class LockOperationContainer implements
Comparable<LockOperationContainer>
+ {
+
+ private String identifier;
+
+ private String sessionId;
+
+ private int type;
+
+ /**
+ * @param identifier node identifier
+ * @param sessionId id of session
+ * @param type ExtendedEvent type specifying the operation (LOCK or UNLOCK)
+ */
+ public LockOperationContainer(String identifier, String sessionId, int type)
+ {
+ super();
+ this.identifier = identifier;
+ this.sessionId = sessionId;
+ this.type = type;
+ }
+
+ /**
+ * @return node identifier
+ */
+ public String getIdentifier()
+ {
+ return identifier;
+ }
+
+ public void apply() throws LockException
+ {
+ // invoke internalLock in LOCK operation
+ if (type == ExtendedEvent.LOCK)
+ {
+ internalLock(sessionId, identifier);
+ }
+ // invoke internalUnLock in UNLOCK operation
+ else if (type == ExtendedEvent.UNLOCK)
+ {
+ internalUnLock(sessionId, identifier);
+ }
+ }
+
+ /**
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(LockOperationContainer o)
+ {
+ return identifier.compareTo(o.getIdentifier());
+ }
+ }
+
+ /**
+ * Refreshed lock data in cache
+ *
+ * @param newLockData
+ */
+ public void refreshLockData(LockData newLockData) throws LockException
+ {
+ executeLockActionNonTxAware(refresh, newLockData);
+ }
+
+ /**
+ * Remove expired locks. Used from LockRemover.
+ */
+ public synchronized void removeExpired()
+ {
+ final List<String> removeLockList = new ArrayList<String>();
+
+ for (LockData lock : getLockList())
+ {
+ if (!lock.isSessionScoped() && lock.getTimeToDeath() < 0)
+ {
+ removeLockList.add(lock.getNodeIdentifier());
+ }
+ }
+
+ Collections.sort(removeLockList);
+
+ for (String rLock : removeLockList)
+ {
+ removeLock(rLock);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start()
+ {
+ lockRemover = new LockRemover(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop()
+ {
+ lockRemover.halt();
+ lockRemover.interrupt();
+ sessionLockManagers.clear();
+ }
+
+ /**
+ * Copy <code>PropertyData prop<code> to new TransientItemData
+ *
+ * @param prop
+ * @return
+ * @throws RepositoryException
+ */
+ protected TransientItemData copyItemData(PropertyData prop) throws
RepositoryException
+ {
+ if (prop == null)
+ {
+ return null;
+ }
+
+ // make a copy, value may be null for deleting items
+ TransientPropertyData newData =
+ new TransientPropertyData(prop.getQPath(), prop.getIdentifier(),
prop.getPersistedVersion(), prop.getType(),
+ prop.getParentIdentifier(), prop.isMultiValued(), prop.getValues());
+
+ return newData;
+ }
+
+ /**
+ * Internal lock
+ *
+ * @param nodeIdentifier
+ * @throws LockException
+ */
+ protected abstract void internalLock(String sessionId, String nodeIdentifier) throws
LockException;
+
+ /**
+ * Internal unlock.
+ *
+ * @param sessionId
+ * @param nodeIdentifier
+ * @throws LockException
+ */
+ protected abstract void internalUnLock(String sessionId, String nodeIdentifier) throws
LockException;
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean lockExist(String nodeId)
+ {
+ try
+ {
+ return executeLockActionNonTxAware(lockExist, nodeId);
+ }
+ catch (LockException e)
+ {
+ // ignore me will never occur
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLockTokenHash(String token)
+ {
+ String hash = "";
+ try
+ {
+ MessageDigest m = MessageDigest.getInstance("MD5");
+ m.update(token.getBytes(), 0, token.length());
+ hash = new BigInteger(1, m.digest()).toString(16);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ LOG.error("Can't get instanse of MD5 MessageDigest!", e);
+ }
+ return hash;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LockData getExactNodeOrCloseParentLock(NodeData node) throws
RepositoryException
+ {
+ return getExactNodeOrCloseParentLock(node, true);
+ }
+
+ private LockData getExactNodeOrCloseParentLock(NodeData node, boolean checkHasLocks)
throws RepositoryException
+ {
+
+ if (node == null || (checkHasLocks && !hasLocks()))
+ {
+ return null;
+ }
+ LockData retval = null;
+ retval = getLockDataById(node.getIdentifier());
+ if (retval == null)
+ {
+ NodeData parentData =
(NodeData)dataManager.getItemData(node.getParentIdentifier());
+ if (parentData != null)
+ {
+ retval = getExactNodeOrCloseParentLock(parentData, false);
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LockData getExactNodeLock(NodeData node) throws RepositoryException
+ {
+ if (node == null || !hasLocks())
+ {
+ return null;
+ }
+
+ return getLockDataById(node.getIdentifier());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LockData getClosedChild(NodeData node) throws RepositoryException
+ {
+ return getClosedChild(node, true);
+ }
+
+ private LockData getClosedChild(NodeData node, boolean checkHasLocks) throws
RepositoryException
+ {
+
+ if (node == null || (checkHasLocks && !hasLocks()))
+ {
+ return null;
+ }
+ LockData retval = null;
+
+ List<NodeData> childData = dataManager.getChildNodesData(node);
+ for (NodeData nodeData : childData)
+ {
+ retval = getLockDataById(nodeData.getIdentifier());
+ if (retval != null)
+ return retval;
+ }
+ // child not found try to find dipper
+ for (NodeData nodeData : childData)
+ {
+ retval = getClosedChild(nodeData, false);
+ if (retval != null)
+ return retval;
+ }
+ return retval;
+ }
+
+ protected LockData getLockDataById(String nodeId)
+ {
+ try
+ {
+ return executeLockActionNonTxAware(getLockDataById, nodeId);
+ }
+ catch (LockException e)
+ {
+ // ignore me will never occur
+ }
+ return null;
+ }
+
+ protected synchronized List<LockData> getLockList()
+ {
+ try
+ {
+ return executeLockActionNonTxAware(getLockList, null);
+ }
+ catch (LockException e)
+ {
+ // ignore me will never occur
+ }
+ return null;
+ }
+
+ /**
+ * Remove lock, used by Lock remover.
+ *
+ * @param nodeIdentifier String
+ */
+ protected void removeLock(String nodeIdentifier)
+ {
+ try
+ {
+ NodeData nData = (NodeData)dataManager.getItemData(nodeIdentifier);
+
+ //TODO EXOJCR-412, should be refactored in future.
+ //Skip removing, because that node was removed in other node of cluster.
+ if (nData == null)
+ {
+ return;
+ }
+
+ PlainChangesLog changesLog =
+ new PlainChangesLogImpl(new ArrayList<ItemState>(),
IdentityConstants.SYSTEM, ExtendedEvent.UNLOCK);
+
+ ItemData lockOwner =
+ copyItemData((PropertyData)dataManager.getItemData(nData, new
QPathEntry(Constants.JCR_LOCKOWNER, 1)));
+
+ //TODO EXOJCR-412, should be refactored in future.
+ //Skip removing, because that lock was removed in other node of cluster.
+ if (lockOwner == null)
+ {
+ return;
+ }
+
+ changesLog.add(ItemState.createDeletedState(lockOwner));
+
+ ItemData lockIsDeep =
+ copyItemData((PropertyData)dataManager.getItemData(nData, new
QPathEntry(Constants.JCR_LOCKISDEEP, 1)));
+
+ //TODO EXOJCR-412, should be refactored in future.
+ //Skip removing, because that lock was removed in other node of cluster.
+ if (lockIsDeep == null)
+ {
+ return;
+ }
+
+ changesLog.add(ItemState.createDeletedState(lockIsDeep));
+
+ // lock probably removed by other thread
+ if (lockOwner == null && lockIsDeep == null)
+ {
+ return;
+ }
+
+ dataManager.save(new TransactionChangesLog(changesLog));
+ }
+ catch (JCRInvalidItemStateException e)
+ {
+ //TODO EXOJCR-412, should be refactored in future.
+ //Skip property not found in DB, because that lock property was removed in other
node of cluster.
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("The propperty was removed in other node of cluster.",
e);
+ }
+
+ }
+ catch (RepositoryException e)
+ {
+ LOG.error("Error occur during removing lock" +
e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void closeSessionLockManager(String sessionID)
+ {
+ sessionLockManagers.remove(sessionID);
+ }
+
+ /**
+ * Execute the given action outside a transaction. This is needed since the {@link
Cache} used by implementation of {@link CacheableLockManager}
+ * to manage the persistence of its locks thanks to a {@link CacheLoader} and a {@link
CacheLoader} lock the cache {@link Node}
+ * even for read operations which cause deadlock issue when a XA {@link Transaction}
is already opened
+ * @throws LockException when a exception occurs
+ */
+ private <R, A> R executeLockActionNonTxAware(LockActionNonTxAware<R, A>
action, A arg) throws LockException
+ {
+ Transaction tx = null;
+ try
+ {
+ if (tm != null)
+ {
+ try
+ {
+ tx = tm.suspend();
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Cannot suspend the current transaction", e);
+ }
+ }
+ return action.execute(arg);
+ }
+ finally
+ {
+ if (tx != null)
+ {
+ try
+ {
+ tm.resume(tx);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Cannot resume the current transaction", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Actions that are not supposed to be called within a transaction
+ *
+ * Created by The eXo Platform SAS
+ * Author : Nicolas Filotto
+ * nicolas.filotto(a)exoplatform.com
+ * 21 janv. 2010
+ */
+ protected static interface LockActionNonTxAware<R, A>
+ {
+ R execute(A arg) throws LockException;
+ }
+}
Added:
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheLockImpl.java
===================================================================
---
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheLockImpl.java
(rev 0)
+++
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheLockImpl.java 2010-09-02
11:19:11 UTC (rev 3047)
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.impl.core.lock.cacheable;
+
+import org.exoplatform.services.jcr.impl.core.SessionImpl;
+import org.exoplatform.services.jcr.impl.core.lock.LockImpl;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+
+/**
+ * Created by The eXo Platform SAS.
+ *
+ * <br/>Date:
+ *
+ * @author <a href="karpenko.sergiy(a)gmail.com">Karpenko Sergiy</a>
+ * @version $Id: CacheLockImpl.java 111 2008-11-11 11:11:11Z serg $
+ */
+public class CacheLockImpl extends LockImpl
+{
+ private boolean live;
+
+ private LockData lockData;
+
+ private SessionImpl session;
+
+ private CacheableSessionLockManager lockManager;
+
+ /**
+ * Constructor.
+ *
+ * @param session - session owner
+ * @param lockData - LockData
+ * @param lockManager - CacheableLockManager
+ */
+ public CacheLockImpl(SessionImpl session, LockData lockData,
CacheableSessionLockManager lockManager)
+ {
+ this.lockData = lockData;
+ this.session = session;
+ this.lockManager = lockManager;
+ this.live = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLockOwner()
+ {
+ return lockData.getOwner();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLockToken()
+ {
+ return lockManager.getLockToken(lockData.getTokenHash());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isLive() throws LockException
+ {
+ if (!live)
+ {
+ // it is already not alive
+ return false;
+ }
+ live = lockManager.isLockLive(lockData.getNodeIdentifier());
+ return live;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void refresh() throws LockException, RepositoryException
+ {
+ if (!isLive())
+ throw new LockException("Lock is not live");
+
+ LockData newLockData =
+ new LockData(lockData.getNodeIdentifier(), lockData.getTokenHash(),
lockData.isDeep(), lockData
+ .isSessionScoped(), lockData.getOwner(), lockData.getTimeOut());
+
+ lockManager.refresh(newLockData);
+ lockData = newLockData;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Node getNode()
+ {
+ try
+ {
+ return
(Node)session.getTransientNodesManager().getItemByIdentifier(lockData.getNodeIdentifier(),
true);
+ }
+ catch (RepositoryException e)
+ {
+ //TODO
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isDeep()
+ {
+
+ return lockData.isDeep();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isSessionScoped()
+ {
+ return lockData.isSessionScoped();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getTimeToDeath()
+ {
+ return lockData.getTimeToDeath();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void setTimeOut(long timeOut) throws LockException
+ {
+ lockData.setTimeOut(timeOut);
+
+ //reset lock data
+ lockManager.refresh(lockData);
+ }
+}
Added:
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableLockManager.java
===================================================================
---
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableLockManager.java
(rev 0)
+++
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableLockManager.java 2010-09-02
11:19:11 UTC (rev 3047)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.impl.core.lock.cacheable;
+
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.impl.core.lock.WorkspaceLockManager;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+
+/**
+ * Created by The eXo Platform SAS.
+ *
+ * <br/>Date:
+ *
+ * @author <a href="karpenko.sergiy(a)gmail.com">Karpenko Sergiy</a>
+ * @version $Id: CacheableLockManager.java 111 2008-11-11 11:11:11Z serg $
+ */
+public interface CacheableLockManager extends WorkspaceLockManager
+{
+ /**
+ * Is lock live for node by nodeIdentifier.
+ *
+ * @param nodeIdentifier
+ *
+ * @return boolean
+ * @throws LockException
+ */
+ boolean isLockLive(String nodeIdentifier) throws LockException;
+
+ /**
+ * Replace old lockData with new one. Node ID, token can't be replaced.
+ *
+ * @param newLockData
+ * @throws LockException
+ */
+ void refreshLockData(LockData newLockData) throws LockException;
+
+ /**
+ * Get default lock timeout.
+ *
+ * @return long value of timeout
+ */
+ long getDefaultLockTimeOut();
+
+ /**
+ * Return hash for lock token.
+ *
+ * @param lockToken - lock token string
+ * @return - hash string
+ */
+ String getLockTokenHash(String lockToken);
+
+ /**
+ * Return Lock holding node or its parent.
+ *
+ * @param node - NodeData
+ * @return LockData for node or null;
+ * @throws RepositoryException
+ */
+ LockData getExactNodeOrCloseParentLock(NodeData node) throws RepositoryException;
+
+ /**
+ * Return Lock holding any nodes child.
+ *
+ * @param node - NodeData
+ * @return LockData for node or null;
+ * @throws RepositoryException
+ */
+ LockData getClosedChild(NodeData node) throws RepositoryException;
+
+ boolean lockExist(String nodeId) throws RepositoryException;
+
+}
Added:
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableSessionLockManager.java
===================================================================
---
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableSessionLockManager.java
(rev 0)
+++
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/CacheableSessionLockManager.java 2010-09-02
11:19:11 UTC (rev 3047)
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2003-2010 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not,
see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.impl.core.lock.cacheable;
+
+import org.exoplatform.services.jcr.core.ExtendedSession;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.impl.core.NodeImpl;
+import org.exoplatform.services.jcr.impl.core.SessionDataManager;
+import org.exoplatform.services.jcr.impl.core.SessionImpl;
+import org.exoplatform.services.jcr.impl.core.lock.AbstractSessionLockManager;
+import org.exoplatform.services.jcr.impl.core.lock.LockImpl;
+import org.exoplatform.services.jcr.util.IdGenerator;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+
+/**
+ * Created by The eXo Platform SAS.
+ *
+ * <br/>Date:
+ *
+ * @author <a href="karpenko.sergiy(a)gmail.com">Karpenko Sergiy</a>
+ * @version $Id: CacheableSessionLockManager.java 2137 2010-03-25 15:31:56Z
sergiykarpenko $
+ */
+public class CacheableSessionLockManager extends AbstractSessionLockManager
+{
+ /**
+ * Logger
+ */
+ private final Log log =
ExoLogger.getLogger("exo.jcr.component.core.CacheableSessionLockManager");
+
+ /**
+ * Session identifier.
+ */
+ private final String sessionID;
+
+ /**
+ * Lock tokens held by this session.
+ * [token name, tokens hash]
+ */
+ private final Map<String, String> tokens;
+
+ /**
+ * Map of nodes locked in this session. Need to remove session scoped lock on session
close.
+ * [node identifier, lock data]
+ */
+ private final Map<String, LockData> lockedNodes;
+
+ /**
+ * Set of pending locked nodes identifiers.
+ */
+ private final Set<String> pendingLocks;
+
+ /**
+ * Workspace lock manager
+ */
+ private final CacheableLockManager lockManager;
+
+ /**
+ * Constructor.
+ *
+ * @param sessionID - session identifier
+ * @param lockManager - workspace lock manager
+ */
+ public CacheableSessionLockManager(String sessionID, CacheableLockManager
lockManager,
+ SessionDataManager transientManager)
+ {
+ super(transientManager);
+ this.sessionID = sessionID;
+ this.tokens = new HashMap<String, String>();
+ this.lockedNodes = new HashMap<String, LockData>();
+ this.pendingLocks = new HashSet<String>();
+ this.lockManager = lockManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Lock addLock(NodeImpl node, boolean isDeep, boolean isSessionScoped, long
timeOut) throws LockException,
+ RepositoryException
+ {
+
+ String lockToken = IdGenerator.generate();
+
+ NodeData data = (NodeData)node.getData();
+
+ LockData lData = lockManager.getExactNodeOrCloseParentLock(data);
+ if (lData != null)
+ {
+ if (lData.getNodeIdentifier().equals(node.getIdentifier()))
+ {
+ throw new LockException("Node already locked: " +
data.getQPath());
+ }
+ else if (lData.isDeep())
+ {
+ throw new LockException("Parent node has deep lock.");
+ }
+ }
+
+ if (isDeep && lockManager.getClosedChild(data) != null)
+ {
+ throw new LockException("Some child node is locked.");
+ }
+
+ String lockTokenHash = lockManager.getLockTokenHash(lockToken);
+
+ lData =
+ new LockData(node.getIdentifier(), lockTokenHash, isDeep, isSessionScoped,
node.getSession().getUserID(),
+ timeOut > 0 ? timeOut : lockManager.getDefaultLockTimeOut());
+
+ lockedNodes.put(node.getInternalIdentifier(), lData);
+ pendingLocks.add(node.getInternalIdentifier());
+ tokens.put(lockToken, lData.getTokenHash());
+
+ LockImpl lock = new CacheLockImpl(node.getSession(), lData, this);
+
+ return lock;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addLockToken(String lt)
+ {
+ tokens.put(lt, lockManager.getLockTokenHash(lt));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LockImpl getLock(NodeImpl node) throws LockException, RepositoryException
+ {
+ LockData lData =
lockManager.getExactNodeOrCloseParentLock((NodeData)node.getData());
+
+ if (lData == null ||
(!node.getInternalIdentifier().equals(lData.getNodeIdentifier()) &&
!lData.isDeep()))
+ {
+ throw new LockException("Node not locked: " +
node.getData().getQPath());
+ }
+ return new CacheLockImpl(node.getSession(), lData, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] getLockTokens()
+ {
+ String[] arr = new String[tokens.size()];
+ tokens.keySet().toArray(arr);
+ return arr;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean holdsLock(NodeData node) throws RepositoryException
+ {
+ return lockManager.lockExist(node.getIdentifier());//.getExactNodeLock(node) !=
null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected boolean isLockedPersisted(NodeData node) throws LockException
+ {
+ LockData lData = null;
+ try
+ {
+ lData = lockManager.getExactNodeOrCloseParentLock(node);
+ }
+ catch (RepositoryException e)
+ {
+ throw new LockException(e.getMessage(), e);
+ }
+
+ if (lData == null || (!node.getIdentifier().equals(lData.getNodeIdentifier())
&& !lData.isDeep()))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected boolean isPersistedLockHolder(NodeData node) throws RepositoryException
+ {
+ LockData lData = lockManager.getExactNodeOrCloseParentLock(node);
+ return lData != null && isLockHolder(lData);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onCloseSession(ExtendedSession session)
+ {
+ SessionImpl sessionImpl = (SessionImpl)session;
+
+ String[] nodeIds = new String[lockedNodes.size()];
+ lockedNodes.keySet().toArray(nodeIds);
+
+ for (String nodeId : nodeIds)
+ {
+ LockData lock = lockedNodes.remove(nodeId);
+
+ if (lock.isSessionScoped() && !pendingLocks.contains(nodeId))
+ {
+ try
+ {
+ NodeImpl node =
+ ((NodeImpl)sessionImpl.getTransientNodesManager()
+ .getItemByIdentifier(lock.getNodeIdentifier(), false));
+
+ if (node != null)
+ {
+ node.unlock();
+ }
+
+ }
+ catch (UnsupportedRepositoryOperationException e)
+ {
+ log.error(e.getLocalizedMessage());
+ }
+ catch (LockException e)
+ {
+ log.error(e.getLocalizedMessage());
+ }
+ catch (AccessDeniedException e)
+ {
+ log.error(e.getLocalizedMessage());
+ }
+ catch (RepositoryException e)
+ {
+ log.error(e.getLocalizedMessage());
+ }
+ }
+ }
+
+ pendingLocks.clear();
+ tokens.clear();
+ lockedNodes.clear();
+
+ lockManager.closeSessionLockManager(sessionID);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeLockToken(String lt)
+ {
+ tokens.remove(lt);
+ }
+
+ /**
+ * Checks if session has token to this lock data or session is System.
+ *
+ * @param lockData
+ * @return
+ */
+ private boolean isLockHolder(LockData lockData)
+ {
+ return tokens.containsValue(lockData.getTokenHash());
+ }
+
+ /**
+ * Is session contains pending lock for node by nodeId.
+ * @param nodeId - node ID string
+ * @return boolean
+ */
+ public boolean containsPendingLock(String nodeId)
+ {
+ return pendingLocks.contains(nodeId);
+ }
+
+ /**
+ * Returns real token, if session has it.
+ *
+ * @param tokenHash - token hash string
+ * @return lock token string
+ */
+ protected String getLockToken(String tokenHash)
+ {
+ for (String token : tokens.keySet())
+ {
+ if (tokens.get(token).equals(tokenHash))
+ {
+ return token;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return pending lock.
+ *
+ * @param nodeId - ID of locked node
+ * @return pending lock or null
+ */
+ public LockData getPendingLock(String nodeId)
+ {
+ if (pendingLocks.contains(nodeId))
+ {
+ return lockedNodes.get(nodeId);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Check is lock alive. That means lock must exist in LockManager storage (cache or
map, etc).
+ *
+ * @param nodeIdentifier - locked node id
+ * @return
+ */
+ protected boolean isLockLive(String nodeIdentifier) throws LockException
+ {
+
+ if (lockManager.isLockLive(nodeIdentifier))
+ {
+ return true;
+ }
+ else
+ {
+ return pendingLocks.contains(nodeIdentifier);
+ }
+ }
+
+ public void notifyLockPersisted(String nodeIdentifier)
+ {
+ pendingLocks.remove(nodeIdentifier);
+ }
+
+ /**
+ * Notify SessionLockManager that node is unlocked.
+ *
+ * @param nodeIdentifier - unlocked node identifier
+ */
+ public void notifyLockRemoved(String nodeIdentifier)
+ {
+ lockedNodes.remove(nodeIdentifier);
+ }
+
+ /**
+ * Refresh lockData.
+ *
+ * @param newLockData
+ * @throws LockException
+ */
+ protected void refresh(LockData newLockData) throws LockException
+ {
+ lockManager.refreshLockData(newLockData);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected boolean checkPersistedLocks(NodeData node) throws LockException
+ {
+ LockData lData = null;
+ try
+ {
+ lData = lockManager.getExactNodeOrCloseParentLock(node);
+ }
+ catch (RepositoryException e)
+ {
+ throw new LockException(e.getMessage(), e);
+ }
+
+ if (lData == null || (!node.getIdentifier().equals(lData.getNodeIdentifier())
&& !lData.isDeep()))
+ {
+ return true;
+ }
+
+ // lock exist, so lets check is current session is LockHolder
+ return isLockHolder(lData);
+ }
+
+}
Added:
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/LockData.java
===================================================================
---
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/LockData.java
(rev 0)
+++
jcr/branches/1.14-ISPN/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/core/lock/cacheable/LockData.java 2010-09-02
11:19:11 UTC (rev 3047)
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.core.lock.cacheable;
+
+import org.exoplatform.services.jcr.impl.Constants;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Created by The eXo Platform SAS.
+ *
+ * @author <a href="mailto:gennady.azarenkov@exoplatform.com">Gennady
Azarenkov</a>
+ * @version $Id: LockData.java 787 2009-11-20 11:36:15Z nzamosenchuk $
+ */
+
+public class LockData implements Externalizable
+{
+ /**
+ * The time of birth. From this time we start count the time of death. death =
birthday+TIME_OUT;
+ */
+ private long birthday;
+
+ /**
+ * If isDeep is true then the lock applies to this node and all its descendant nodes;
if false,
+ * the lock applies only to this, the holding node.
+ */
+ private boolean deep;
+
+ /**
+ * A lock token is a string that uniquely identifies a particular lock and acts as a
“key”
+ * allowing a user to alter a locked node. LockData stores only token hash.
+ */
+ private String tokenHash;
+
+ /**
+ * Identifier of locked node.
+ */
+ private String nodeIdentifier;
+
+ /**
+ * The owner of the locked node.
+ */
+ private String owner;
+
+ /**
+ * If isSessionScoped is true then this lock will expire upon the expiration of the
current
+ * session (either through an automatic or explicit Session.logout); if false, this
lock does not
+ * expire until explicitly unlocked or automatically unlocked due to a
implementation-specific
+ * limitation, such as a timeout.
+ */
+ private boolean sessionScoped;
+
+ /**
+ * <B>8.4.9 Timing Out</B> An implementation may unlock any lock at any
time due to
+ * implementation-specific criteria, such as time limits on locks.
+ */
+ private long timeOut;
+
+ // Need for Externalizable
+ public LockData()
+ {
+ this.sessionScoped = false;
+ this.deep = false;
+ }
+
+ /**
+ * @param nodeIdentifier
+ * @param lockToken
+ * @param deep
+ * @param sessionScoped
+ * @param owner
+ * @param timeOut
+ * is seconds!
+ */
+ public LockData(String nodeIdentifier, String lockTokenHash, boolean deep, boolean
sessionScoped, String owner,
+ long timeOut)
+ {
+ this(nodeIdentifier, lockTokenHash, deep, sessionScoped, owner, timeOut,
System.currentTimeMillis());
+ }
+
+ /**
+ * @param nodeIdentifier
+ * @param lockToken
+ * @param deep
+ * @param sessionScoped
+ * @param owner
+ * @param timeOut
+ * is seconds!
+ * @param birthday
+ */
+ public LockData(String nodeIdentifier, String lockTokenHash, boolean deep, boolean
sessionScoped, String owner,
+ long timeOut, long birthday)
+ {
+ this.nodeIdentifier = nodeIdentifier;
+ this.tokenHash = lockTokenHash;
+ this.deep = deep;
+ this.sessionScoped = sessionScoped;
+ this.owner = owner;
+ this.timeOut = timeOut;
+ this.birthday = birthday;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (super.equals(obj))
+ {
+ return true;
+ }
+ if (obj instanceof LockData)
+ {
+ return hashCode() == obj.hashCode();
+ }
+ return false;
+ }
+
+ /**
+ * @return the nodeIdentifier
+ */
+ public String getNodeIdentifier()
+ {
+ return nodeIdentifier;
+ }
+
+ /**
+ * @return
+ */
+ public String getOwner()
+ {
+ return owner;
+ }
+
+ /**
+ * @return The time to death in millis
+ */
+ public long getTimeToDeath()
+ {
+ return birthday + timeOut - System.currentTimeMillis();
+ }
+
+ public String getTokenHash()
+ {
+ return tokenHash;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return tokenHash.hashCode();
+ }
+
+ public boolean isDeep()
+ {
+ return deep;
+ }
+
+ /**
+ * @return
+ */
+ public boolean isSessionScoped()
+ {
+ return sessionScoped;
+ }
+
+ /**
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+ {
+ // read boolean
+ this.deep = in.readBoolean();
+ this.sessionScoped = in.readBoolean();
+ // read long
+ this.birthday = in.readLong();
+ this.timeOut = in.readLong();
+ //read strings
+ // read uuid
+ byte[] buf;
+ buf = new byte[in.readInt()];
+ in.readFully(buf);
+ this.nodeIdentifier = new String(buf, Constants.DEFAULT_ENCODING);
+ // read owner
+ buf = new byte[in.readInt()];
+ in.readFully(buf);
+ this.owner = new String(buf, Constants.DEFAULT_ENCODING);
+ // read token
+ buf = new byte[in.readInt()];
+ in.readFully(buf);
+ this.tokenHash = new String(buf, Constants.DEFAULT_ENCODING);
+ }
+
+ /**
+ * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out) throws IOException
+ {
+ // write boolean
+ out.writeBoolean(deep);
+ out.writeBoolean(sessionScoped);
+ // write long
+ out.writeLong(birthday);
+ out.writeLong(timeOut);
+ // write string
+ // node uuid
+ byte[] ptbuf = nodeIdentifier.getBytes(Constants.DEFAULT_ENCODING);
+ out.writeInt(ptbuf.length);
+ out.write(ptbuf);
+ // node owner
+ ptbuf = owner.getBytes(Constants.DEFAULT_ENCODING);
+ out.writeInt(ptbuf.length);
+ out.write(ptbuf);
+ // node token
+ ptbuf = tokenHash.getBytes(Constants.DEFAULT_ENCODING);
+ out.writeInt(ptbuf.length);
+ out.write(ptbuf);
+
+ }
+
+ /**
+ * @return
+ */
+ public long getTimeOut()
+ {
+ return timeOut;
+ }
+
+ public long getBirthDay()
+ {
+ return birthday;
+ }
+
+ public void setTimeOut(long timeOut)
+ {
+ this.timeOut = timeOut;
+ }
+
+}