/** * @param nodeId Primary node ID. * @param req Request. * @return Remote transaction. * @throws GridException If failed. * @throws GridDistributedLockCancelledException If lock has been cancelled. */ @SuppressWarnings({"RedundantTypeArguments"}) @Nullable public GridNearTxRemote<K, V> startRemoteTxForFinish( UUID nodeId, GridDhtTxFinishRequest<K, V> req) throws GridException, GridDistributedLockCancelledException { GridNearTxRemote<K, V> tx = null; ClassLoader ldr = ctx.deploy().globalLoader(); if (ldr != null) { for (GridCacheTxEntry<K, V> txEntry : req.nearWrites()) { GridDistributedCacheEntry<K, V> entry = null; while (true) { try { entry = peekExx(txEntry.key()); if (entry != null) { entry.keyBytes(txEntry.keyBytes()); // Handle implicit locks for pessimistic transactions. tx = ctx.tm().tx(req.version()); if (tx != null) { if (tx.local()) return null; if (tx.markFinalizing()) tx.addWrite(txEntry.key(), txEntry.keyBytes()); else return null; } else { tx = new GridNearTxRemote<K, V>( nodeId, req.nearNodeId(), req.threadId(), req.version(), null, PESSIMISTIC, req.isolation(), req.isInvalidate(), 0, txEntry.key(), txEntry.keyBytes(), txEntry.value(), txEntry.valueBytes(), ctx); if (tx.empty()) return tx; tx = ctx.tm().onCreated(tx); if (tx == null || !ctx.tm().onStarted(tx)) throw new GridCacheTxRollbackException( "Failed to acquire lock " + "(transaction has been completed): " + req.version()); if (!tx.markFinalizing()) return null; } // Add remote candidate before reordering. if (txEntry.explicitVersion() == null) entry.addRemote( req.nearNodeId(), nodeId, req.threadId(), req.version(), 0, tx.ec(), /*tx*/ true, tx.implicitSingle()); // Remote candidates for ordered lock queuing. entry.addRemoteCandidates( Collections.<GridCacheMvccCandidate<K>>emptyList(), req.version(), req.committedVersions(), req.rolledbackVersions()); } // Double-check in case if sender node left the grid. if (ctx.discovery().node(req.nearNodeId()) == null) { if (log.isDebugEnabled()) log.debug("Node requesting lock left grid (lock request will be ignored): " + req); if (tx != null) tx.rollback(); return null; } // Entry is legit. break; } catch (GridCacheEntryRemovedException ignored) { assert entry.obsoleteVersion() != null : "Obsolete flag not set on removed entry: " + entry; if (log.isDebugEnabled()) log.debug("Received entry removed exception (will retry on renewed entry): " + entry); if (tx != null) { tx.clearEntry(entry.key()); if (log.isDebugEnabled()) log.debug( "Cleared removed entry from remote transaction (will retry) [entry=" + entry + ", tx=" + tx + ']'); } } } } } else { String err = "Failed to acquire deployment class loader for message: " + req; U.warn(log, err); throw new GridException(err); } return tx; }
/** Clears all readers (usually when partition becomes invalid and ready for eviction). */ @Override public void clearReaders() { synchronized (mux) { readers = Collections.emptyList(); } }
/** * Task session. * * @author 2012 Copyright (C) GridGain Systems * @version 4.0.2c.12042012 */ public class GridTaskSessionImpl extends GridMetadataAwareAdapter implements GridTaskSessionInternal { /** */ private final String taskName; /** */ private final GridDeployment dep; /** */ private final String taskClsName; /** */ private final GridUuid sesId; /** */ private final long startTime; /** */ private final long endTime; /** */ private final UUID taskNodeId; /** */ private final GridKernalContext ctx; /** */ private Collection<GridJobSibling> siblings; /** */ private final Map<Object, Object> attrs = new HashMap<Object, Object>(1); /** */ private List<GridTaskSessionAttributeListener> lsnrs = Collections.emptyList(); /** */ private ClassLoader clsLdr; /** */ private boolean closed; /** */ private String topSpi; /** */ private String cpSpi; /** */ private String failSpi; /** */ private String loadSpi; /** */ private final Object mux = new Object(); /** * @param taskNodeId Task node ID. * @param taskName Task name. * @param dep Deployment. * @param taskClsName Task class name. * @param sesId Task session ID. * @param startTime Task execution start time. * @param endTime Task execution end time. * @param siblings Collection of siblings. * @param attrs Session attributes. * @param ctx Grid Kernal Context. */ public GridTaskSessionImpl( UUID taskNodeId, String taskName, @Nullable GridDeployment dep, String taskClsName, GridUuid sesId, long startTime, long endTime, Collection<GridJobSibling> siblings, Map<Object, Object> attrs, GridKernalContext ctx) { assert taskNodeId != null; assert taskName != null; assert sesId != null; assert attrs != null; assert ctx != null; this.taskNodeId = taskNodeId; this.taskName = taskName; this.dep = dep; // Note that class name might be null here if task was not explicitly // deployed. this.taskClsName = taskClsName; this.sesId = sesId; this.startTime = startTime; this.endTime = endTime; this.siblings = siblings != null ? Collections.unmodifiableCollection(siblings) : null; this.ctx = ctx; this.attrs.putAll(attrs); } /** {@inheritDoc} */ @Override public GridTaskSessionInternal session() { return this; } /** {@inheritDoc} */ @Nullable @Override public GridUuid getJobId() { return null; } /** {@inheritDoc} */ @Override public void onClosed() { synchronized (mux) { closed = true; mux.notifyAll(); } } /** {@inheritDoc} */ @Override public boolean isClosed() { synchronized (mux) { return closed; } } /** @return Task node ID. */ @Override public UUID getTaskNodeId() { return taskNodeId; } /** {@inheritDoc} */ @Override public long getStartTime() { return startTime; } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <K, V> V waitForAttribute(K key) throws InterruptedException { return (V) waitForAttribute(key, 0); } /** {@inheritDoc} */ @Override public boolean waitForAttribute(Object key, Object val) throws InterruptedException { return waitForAttribute(key, val, 0); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <K, V> V waitForAttribute(K key, long timeout) throws InterruptedException { A.notNull(key, "key"); if (timeout == 0) timeout = Long.MAX_VALUE; long now = System.currentTimeMillis(); // Prevent overflow. long end = now + timeout < 0 ? Long.MAX_VALUE : now + timeout; // Don't wait longer than session timeout. if (end > endTime) end = endTime; synchronized (mux) { while (!closed && !attrs.containsKey(key) && now < end) { mux.wait(end - now); now = System.currentTimeMillis(); } if (closed) throw new InterruptedException("Session was closed: " + this); return (V) attrs.get(key); } } /** {@inheritDoc} */ @Override public boolean waitForAttribute(Object key, Object val, long timeout) throws InterruptedException { A.notNull(key, "key"); if (timeout == 0) timeout = Long.MAX_VALUE; long now = System.currentTimeMillis(); // Prevent overflow. long end = now + timeout < 0 ? Long.MAX_VALUE : now + timeout; // Don't wait longer than session timeout. if (end > endTime) end = endTime; synchronized (mux) { boolean isFound = false; while (!closed && !(isFound = isAttributeSet(key, val)) && now < end) { mux.wait(end - now); now = System.currentTimeMillis(); } if (closed) throw new InterruptedException("Session was closed: " + this); return isFound; } } /** * {@inheritDoc} * * @param keys Attribute keys. */ @Override public Map<?, ?> waitForAttributes(Collection<?> keys) throws InterruptedException { return waitForAttributes(keys, 0); } /** {@inheritDoc} */ @Override public boolean waitForAttributes(Map<?, ?> attrs) throws InterruptedException { return waitForAttributes(attrs, 0); } /** {@inheritDoc} */ @Override public Map<?, ?> waitForAttributes(Collection<?> keys, long timeout) throws InterruptedException { A.notNull(keys, "keys"); if (keys.isEmpty()) return Collections.emptyMap(); if (timeout == 0) timeout = Long.MAX_VALUE; long now = System.currentTimeMillis(); // Prevent overflow. long end = now + timeout < 0 ? Long.MAX_VALUE : now + timeout; // Don't wait longer than session timeout. if (end > endTime) end = endTime; synchronized (mux) { while (!closed && !attrs.keySet().containsAll(keys) && now < end) { mux.wait(end - now); now = System.currentTimeMillis(); } if (closed) throw new InterruptedException("Session was closed: " + this); Map<Object, Object> retVal = new HashMap<Object, Object>(keys.size()); for (Object key : keys) retVal.put(key, attrs.get(key)); return retVal; } } /** {@inheritDoc} */ @Override public boolean waitForAttributes(Map<?, ?> attrs, long timeout) throws InterruptedException { A.notNull(attrs, "attrs"); if (attrs.isEmpty()) { return true; } if (timeout == 0) timeout = Long.MAX_VALUE; long now = System.currentTimeMillis(); // Prevent overflow. long end = now + timeout < 0 ? Long.MAX_VALUE : now + timeout; // Don't wait longer than session timeout. if (end > endTime) end = endTime; synchronized (mux) { boolean isFound = false; while (!closed && !(isFound = this.attrs.entrySet().containsAll(attrs.entrySet())) && now < end) { mux.wait(end - now); now = System.currentTimeMillis(); } if (closed) throw new InterruptedException("Session was closed: " + this); return isFound; } } /** {@inheritDoc} */ @Override public String getTaskName() { return taskName; } /** * Returns task class name. * * @return Task class name. */ public String getTaskClassName() { return taskClsName; } /** {@inheritDoc} */ @Override public GridUuid getId() { return sesId; } /** {@inheritDoc} */ @Override public long getEndTime() { return endTime; } /** @return Task version. */ public String getUserVersion() { return dep == null ? "" : dep.userVersion(); } /** {@inheritDoc} */ @Override public ClassLoader getClassLoader() { synchronized (mux) { return clsLdr; } } /** @param clsLdr Class loader. */ public void setClassLoader(ClassLoader clsLdr) { assert clsLdr != null; synchronized (mux) { this.clsLdr = clsLdr; } } /** {@inheritDoc} */ @Override public boolean isTaskNode() { return taskNodeId.equals(ctx.discovery().localNode().id()); } /** {@inheritDoc} */ @Override public Collection<GridJobSibling> refreshJobSiblings() throws GridException { return getJobSiblings(); } /** {@inheritDoc} */ @Override public Collection<GridJobSibling> getJobSiblings() throws GridException { synchronized (mux) { return siblings; } } /** @param siblings Siblings. */ public void setJobSiblings(Collection<GridJobSibling> siblings) { synchronized (mux) { this.siblings = Collections.unmodifiableCollection(siblings); } } /** @param siblings Siblings. */ public void addJobSiblings(Collection<GridJobSibling> siblings) { assert isTaskNode(); synchronized (mux) { Collection<GridJobSibling> tmp = new ArrayList<GridJobSibling>(this.siblings); tmp.addAll(siblings); this.siblings = Collections.unmodifiableCollection(tmp); } } /** {@inheritDoc} */ @Override public GridJobSibling getJobSibling(GridUuid jobId) throws GridException { A.notNull(jobId, "jobId"); Collection<GridJobSibling> tmp = getJobSiblings(); for (GridJobSibling sibling : tmp) if (sibling.getJobId().equals(jobId)) return sibling; return null; } /** {@inheritDoc} */ @Override public void setAttribute(Object key, Object val) throws GridException { A.notNull(key, "key"); setAttributes(Collections.singletonMap(key, val)); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <K, V> V getAttribute(K key) { A.notNull(key, "key"); synchronized (mux) { return (V) attrs.get(key); } } /** {@inheritDoc} */ @Override public void setAttributes(Map<?, ?> attrs) throws GridException { A.notNull(attrs, "attrs"); if (attrs.isEmpty()) return; // Note that there is no mux notification in this block. // The reason is that we wait for ordered attributes to // come back from task prior to notification. The notification // will happen in 'setInternal(...)' method. synchronized (mux) { this.attrs.putAll(attrs); } if (isTaskNode()) ctx.task().setAttributes(this, attrs); } /** {@inheritDoc} */ @Override public Map<Object, Object> getAttributes() { synchronized (mux) { return U.sealMap(attrs); } } /** @param attrs Attributes to set. */ public void setInternal(Map<?, ?> attrs) { A.notNull(attrs, "attrs"); if (attrs.isEmpty()) return; List<GridTaskSessionAttributeListener> lsnrs; synchronized (mux) { this.attrs.putAll(attrs); lsnrs = this.lsnrs; mux.notifyAll(); } for (Map.Entry<?, ?> entry : attrs.entrySet()) for (GridTaskSessionAttributeListener lsnr : lsnrs) lsnr.onAttributeSet(entry.getKey(), entry.getValue()); } /** {@inheritDoc} */ @Override public void addAttributeListener(GridTaskSessionAttributeListener lsnr, boolean rewind) { A.notNull(lsnr, "lsnr"); Map<Object, Object> attrs = null; List<GridTaskSessionAttributeListener> lsnrs; synchronized (mux) { lsnrs = new ArrayList<GridTaskSessionAttributeListener>(this.lsnrs.size()); lsnrs.addAll(this.lsnrs); lsnrs.add(lsnr); lsnrs = Collections.unmodifiableList(lsnrs); this.lsnrs = lsnrs; if (rewind) attrs = new HashMap<Object, Object>(this.attrs); } if (rewind) { for (Map.Entry<Object, Object> entry : attrs.entrySet()) { for (GridTaskSessionAttributeListener l : lsnrs) { l.onAttributeSet(entry.getKey(), entry.getValue()); } } } } /** {@inheritDoc} */ @Override public boolean removeAttributeListener(GridTaskSessionAttributeListener lsnr) { A.notNull(lsnr, "lsnr"); synchronized (mux) { List<GridTaskSessionAttributeListener> lsnrs = new ArrayList<GridTaskSessionAttributeListener>(this.lsnrs); boolean rmv = lsnrs.remove(lsnr); this.lsnrs = Collections.unmodifiableList(lsnrs); return rmv; } } /** {@inheritDoc} */ @Override public void saveCheckpoint(String key, Object state) throws GridException { saveCheckpoint(key, state, GridTaskSessionScope.SESSION_SCOPE, 0); } /** {@inheritDoc} */ @Override public void saveCheckpoint(String key, Object state, GridTaskSessionScope scope, long timeout) throws GridException { saveCheckpoint(key, state, scope, timeout, true); } /** {@inheritDoc} */ @Override public void saveCheckpoint( String key, Object state, GridTaskSessionScope scope, long timeout, boolean overwrite) throws GridException { A.notNull(key, "key"); A.ensure(timeout >= 0, "timeout >= 0"); synchronized (mux) { if (closed) throw new GridException("Failed to save checkpoint (session closed): " + this); } ctx.checkpoint().storeCheckpoint(this, key, state, scope, timeout, overwrite); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> T loadCheckpoint(String key) throws GridException { A.notNull(key, "key"); synchronized (mux) { if (closed) throw new GridException("Failed to load checkpoint (session closed): " + this); } return (T) ctx.checkpoint().loadCheckpoint(this, key); } /** {@inheritDoc} */ @Override public boolean removeCheckpoint(String key) throws GridException { A.notNull(key, "key"); synchronized (mux) { if (closed) throw new GridException("Failed to remove checkpoint (session closed): " + this); } return ctx.checkpoint().removeCheckpoint(this, key); } /** {@inheritDoc} */ @Override public Collection<UUID> getTopology() throws GridException { return F.nodeIds(ctx.topology().getTopology(this, ctx.discovery().allNodes())); } /** * @param key Key. * @param val Value. * @return {@code true} if key/value pair was set. */ private boolean isAttributeSet(Object key, Object val) { if (attrs.containsKey(key)) { Object stored = attrs.get(key); if (val == null && stored == null) return true; if (val != null && stored != null) return val.equals(stored); } return false; } /** @return Topology SPI name. */ @Override public String getTopologySpi() { return topSpi; } /** @param topSpi Topology SPI name. */ public void setTopologySpi(String topSpi) { this.topSpi = topSpi; } /** {@inheritDoc} */ @Override public String getCheckpointSpi() { return cpSpi; } /** @param cpSpi Checkpoint SPI name. */ public void setCheckpointSpi(String cpSpi) { this.cpSpi = cpSpi; } /** @return Failover SPI name. */ public String getFailoverSpi() { return failSpi; } /** @param failSpi Failover SPI name. */ public void setFailoverSpi(String failSpi) { this.failSpi = failSpi; } /** @return Load balancing SPI name. */ public String getLoadBalancingSpi() { return loadSpi; } /** @param loadSpi Load balancing SPI name. */ public void setLoadBalancingSpi(String loadSpi) { this.loadSpi = loadSpi; } /** @return Task internal version. */ public long getSequenceNumber() { return dep == null ? 0 : dep.sequenceNumber(); } /** @return Deployment. */ public GridDeployment deployment() { return dep; } /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridTaskSessionImpl.class, this); } }
/** * Replicated cache entry. * * @author 2005-2011 Copyright (C) GridGain Systems, Inc. * @version 3.1.1c.19062011 */ public class GridDhtCacheEntry<K, V> extends GridDistributedCacheEntry<K, V> { /** Gets node value from reader ID. */ private static final GridClosure<ReaderId, UUID> R2N = new C1<ReaderId, UUID>() { @Override public UUID apply(ReaderId e) { return e.nodeId(); } }; /** Reader clients. */ @GridToStringInclude private volatile List<ReaderId> readers = Collections.emptyList(); /** Local partition. */ private final GridDhtLocalPartition<K, V> locPart; /** Transactions future for added readers. */ private volatile GridCacheMultiTxFuture<K, V> txFut; /** * @param ctx Cache context. * @param key Cache key. * @param hash Key hash value. * @param val Entry value. * @param next Next entry in the linked list. * @param ttl Time to live. */ public GridDhtCacheEntry( GridCacheContext<K, V> ctx, K key, int hash, V val, GridCacheMapEntry<K, V> next, long ttl) { super(ctx, key, hash, val, next, ttl); // Record this entry with partition. locPart = ctx.dht().topology().onAdded(this); } /** {@inheritDoc} */ @Override public boolean partitionValid() { return locPart.valid(); } /** {@inheritDoc} */ @Override public boolean markObsolete(GridCacheVersion ver) { boolean rmv; synchronized (mux) { rmv = super.markObsolete(ver); } // Remove this entry from partition mapping. if (rmv) cctx.dht().topology().onRemoved(this); return rmv; } /** * Add local candidate. * * @param nearNodeId Near node ID. * @param nearVer Near version. * @param threadId Owning thread ID. * @param ver Lock version. * @param timeout Timeout to acquire lock. * @param reenter Reentry flag. * @param ec Eventually consistent flag. * @param tx Tx flag. * @return New candidate. * @throws GridCacheEntryRemovedException If entry has been removed. * @throws GridDistributedLockCancelledException If lock was cancelled. */ @Nullable public GridCacheMvccCandidate<K> addDhtLocal( UUID nearNodeId, GridCacheVersion nearVer, long threadId, GridCacheVersion ver, long timeout, boolean reenter, boolean ec, boolean tx) throws GridCacheEntryRemovedException, GridDistributedLockCancelledException { GridCacheMvccCandidate<K> cand; GridCacheMvccCandidate<K> prev; GridCacheMvccCandidate<K> owner; V val; synchronized (mux) { // Check removed locks prior to obsolete flag. checkRemoved(ver); checkObsolete(); prev = mvcc.anyOwner(); boolean emptyBefore = mvcc.isEmpty(); cand = mvcc.addLocal(this, nearNodeId, nearVer, threadId, ver, timeout, reenter, ec, tx, true); owner = mvcc.anyOwner(); boolean emptyAfter = mvcc.isEmpty(); if (prev != owner) mux.notifyAll(); checkCallbacks(emptyBefore, emptyAfter); val = rawGet(); } // Don't link reentries. if (cand != null && !cand.reentry()) // Link with other candidates in the same thread. cctx.mvcc().addNext(cand); checkOwnerChanged(prev, owner, val); return cand; } /** {@inheritDoc} */ @Override public boolean tmLock(GridCacheTxEx<K, V> tx, long timeout) throws GridCacheEntryRemovedException, GridDistributedLockCancelledException { if (tx.local()) { GridDhtTxLocal<K, V> dhtTx = (GridDhtTxLocal<K, V>) tx; // Null is returned if timeout is negative and there is other lock owner. return addDhtLocal( dhtTx.nearNodeId(), dhtTx.nearXidVersion(), tx.threadId(), tx.xidVersion(), timeout, false, tx.ec(), true) != null; } try { addRemote( tx.nodeId(), tx.otherNodeId(), tx.threadId(), tx.xidVersion(), tx.timeout(), tx.ec(), true); return true; } catch (GridDistributedLockCancelledException ignored) { if (log.isDebugEnabled()) log.debug("Attempted to enter tx lock for cancelled ID (will ignore): " + tx); return false; } } /** {@inheritDoc} */ @Override public GridCacheMvccCandidate<K> removeLock() { GridCacheMvccCandidate<K> ret = super.removeLock(); locPart.onUnlock(); return ret; } /** {@inheritDoc} */ @Override public boolean removeLock(GridCacheVersion ver) throws GridCacheEntryRemovedException { boolean ret = super.removeLock(ver); locPart.onUnlock(); return ret; } /** * @return Readers. * @throws GridCacheEntryRemovedException If removed. */ public Collection<UUID> readers() throws GridCacheEntryRemovedException { return F.viewReadOnly(checkReaders(), R2N); } /** * @param nodeId Node ID. * @return reader ID. */ @Nullable private ReaderId readerId(UUID nodeId) { for (ReaderId reader : readers) if (reader.nodeId().equals(nodeId)) return reader; return null; } /** * @param nodeId Reader to add. * @param msgId Message ID. * @return Future for all relevant transactions that were active at the time of adding reader, or * {@code null} if reader was added * @throws GridCacheEntryRemovedException If entry was removed. */ @Nullable public GridFuture<Boolean> addReader(UUID nodeId, long msgId) throws GridCacheEntryRemovedException { // Don't add local node as reader. if (cctx.nodeId().equals(nodeId)) return null; GridNode node = cctx.discovery().node(nodeId); // If remote node has no near cache, don't add it. if (node == null || !U.hasNearCache(node, cctx.dht().near().name())) return null; // If remote node is (primary?) or back up, don't add it as a reader. if (U.nodeIds(cctx.affinity(partition(), CU.allNodes(cctx))).contains(nodeId)) return null; boolean ret = false; GridCacheMultiTxFuture<K, V> txFut; Collection<GridCacheMvccCandidate<K>> cands = null; synchronized (mux) { checkObsolete(); txFut = this.txFut; ReaderId reader = readerId(nodeId); if (reader == null) { reader = new ReaderId(nodeId, msgId); readers = new LinkedList<ReaderId>(readers); readers.add(reader); // Seal. readers = Collections.unmodifiableList(readers); txFut = this.txFut = new GridCacheMultiTxFuture<K, V>(cctx); cands = localCandidates(); ret = true; } else { long id = reader.messageId(); if (id < msgId) reader.messageId(msgId); } } if (ret) { assert txFut != null; if (!F.isEmpty(cands)) { for (GridCacheMvccCandidate<K> c : cands) { GridCacheTxEx<K, V> tx = cctx.tm().<GridCacheTxEx<K, V>>tx(c.version()); if (tx != null) { assert tx.local(); txFut.addTx(tx); } } } txFut.init(); if (!txFut.isDone()) { txFut.listenAsync( new CI1<GridFuture<?>>() { @Override public void apply(GridFuture<?> f) { synchronized (mux) { // Release memory. GridDhtCacheEntry.this.txFut = null; } } }); } else // Release memory. txFut = this.txFut = null; } return txFut; } /** * @param nodeId Reader to remove. * @param msgId Message ID. * @return {@code True} if reader was removed as a result of this operation. * @throws GridCacheEntryRemovedException If entry was removed. */ public boolean removeReader(UUID nodeId, long msgId) throws GridCacheEntryRemovedException { synchronized (mux) { checkObsolete(); ReaderId reader = readerId(nodeId); if (reader == null || reader.messageId() > msgId) return false; readers = new LinkedList<ReaderId>(readers); readers.remove(reader); // Seal. readers = Collections.unmodifiableList(readers); return true; } } /** Clears all readers (usually when partition becomes invalid and ready for eviction). */ @Override public void clearReaders() { synchronized (mux) { readers = Collections.emptyList(); } } /** * @return Collection of readers after check. * @throws GridCacheEntryRemovedException If removed. */ public Collection<ReaderId> checkReaders() throws GridCacheEntryRemovedException { synchronized (mux) { checkObsolete(); if (!readers.isEmpty()) { List<ReaderId> rmv = null; for (ReaderId reader : readers) { if (!cctx.discovery().alive(reader.nodeId())) { if (rmv == null) rmv = new LinkedList<ReaderId>(); rmv.add(reader); } } if (rmv != null) { readers = new LinkedList<ReaderId>(readers); for (ReaderId rdr : rmv) readers.remove(rdr); readers = Collections.unmodifiableList(readers); } } return readers; } } /** {@inheritDoc} */ @Override protected boolean hasReaders() throws GridCacheEntryRemovedException { synchronized (mux) { checkReaders(); return !readers.isEmpty(); } } /** * @param ver Version. * @param mappings Mappings to set. * @return Candidate, if one existed for the version, or {@code null} if candidate was not found. * @throws GridCacheEntryRemovedException If removed. */ public GridCacheMvccCandidate<K> mappings(GridCacheVersion ver, Collection<UUID> mappings) throws GridCacheEntryRemovedException { synchronized (mux) { checkObsolete(); GridCacheMvccCandidate<K> cand = mvcc.candidate(ver); if (cand != null) cand.mappedNodeIds(mappings); return cand; } } /** {@inheritDoc} */ @Override public GridCacheEntry<K, V> wrap(boolean prjAware) { GridCacheContext<K, V> nearCtx = cctx.dht().near().context(); GridCacheProjectionImpl<K, V> prjPerCall = nearCtx.projectionPerCall(); if (prjPerCall != null && prjAware) return new GridPartitionedCacheEntryImpl<K, V>(prjPerCall, nearCtx, key, this); GridCacheEntryImpl<K, V> wrapper = this.wrapper; if (wrapper == null) this.wrapper = wrapper = new GridPartitionedCacheEntryImpl<K, V>(null, nearCtx, key, this); return wrapper; } /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridDhtCacheEntry.class, this, "super", super.toString()); } /** Reader ID. */ private static class ReaderId { /** Node ID. */ private UUID nodeId; /** Message ID. */ private long msgId; /** * @param nodeId Node ID. * @param msgId Message ID. */ ReaderId(UUID nodeId, long msgId) { this.nodeId = nodeId; this.msgId = msgId; } /** @return Node ID. */ UUID nodeId() { return nodeId; } /** @return Message ID. */ long messageId() { return msgId; } /** @param msgId Message ID. */ void messageId(long msgId) { this.msgId = msgId; } /** {@inheritDoc} */ @Override public String toString() { return S.toString(ReaderId.class, this); } } }