/*
* 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.ignite.internal.processors.datastructures;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import javax.cache.event.CacheEntryEvent;
import javax.cache.event.CacheEntryListenerException;
import javax.cache.event.CacheEntryUpdatedListener;
import javax.cache.event.EventType;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.MutableEntry;
import org.apache.ignite.IgniteAtomicLong;
import org.apache.ignite.IgniteAtomicReference;
import org.apache.ignite.IgniteAtomicSequence;
import org.apache.ignite.IgniteAtomicStamped;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteCountDownLatch;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLock;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteQueue;
import org.apache.ignite.IgniteSemaphore;
import org.apache.ignite.IgniteSet;
import org.apache.ignite.cache.CacheEntryEventSerializableFilter;
import org.apache.ignite.configuration.AtomicConfiguration;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.CollectionConfiguration;
import org.apache.ignite.events.DiscoveryEvent;
import org.apache.ignite.events.Event;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.IgniteInterruptedCheckedException;
import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
import org.apache.ignite.internal.processors.cache.CacheType;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheInternal;
import org.apache.ignite.internal.processors.cache.GridCacheUtils;
import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
import org.apache.ignite.internal.util.lang.IgniteClosureX;
import org.apache.ignite.internal.util.lang.IgniteInClosureX;
import org.apache.ignite.internal.util.lang.IgniteOutClosureX;
import org.apache.ignite.internal.util.typedef.CI1;
import org.apache.ignite.internal.util.typedef.CIX1;
import org.apache.ignite.internal.util.typedef.CX1;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.GPR;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
import org.jetbrains.annotations.Nullable;
import org.jsr166.ConcurrentHashMap8;
import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.ATOMIC_LONG;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.ATOMIC_REF;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.ATOMIC_SEQ;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.ATOMIC_STAMPED;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.COUNT_DOWN_LATCH;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.QUEUE;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.REENTRANT_LOCK;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.SEMAPHORE;
import static org.apache.ignite.internal.processors.datastructures.DataStructuresProcessor.DataStructureType.SET;
import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
/**
* Manager of data structures.
*/
public final class DataStructuresProcessor extends GridProcessorAdapter implements IgniteChangeGlobalStateSupport {
/** */
public static final CacheDataStructuresConfigurationKey DATA_STRUCTURES_KEY =
new CacheDataStructuresConfigurationKey();
/** */
private static final CacheDataStructuresCacheKey DATA_STRUCTURES_CACHE_KEY =
new CacheDataStructuresCacheKey();
/** Initial capacity. */
private static final int INITIAL_CAPACITY = 10;
/** Initialization latch. */
private volatile CountDownLatch initLatch = new CountDownLatch(1);
/** Initialization failed flag. */
private boolean initFailed;
/** Cache contains only {@code GridCacheInternal,GridCacheInternal}. */
private IgniteInternalCache<GridCacheInternal, GridCacheInternal> dsView;
/** Internal storage of all dataStructures items (sequence, atomic long etc.). */
private final ConcurrentMap<GridCacheInternal, GridCacheRemovable> dsMap;
/** Cache contains only {@code GridCacheAtomicValue}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheAtomicLongValue> atomicLongView;
/** Cache contains only {@code GridCacheCountDownLatchValue}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheCountDownLatchValue> cntDownLatchView;
/** Cache contains only {@code GridCacheSemaphoreState}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheSemaphoreState> semView;
/** Cache contains only {@code GridCacheLockState}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheLockState> reentrantLockView;
/** Cache contains only {@code GridCacheAtomicReferenceValue}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheAtomicReferenceValue> atomicRefView;
/** Cache contains only {@code GridCacheAtomicStampedValue}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheAtomicStampedValue> atomicStampedView;
/** Cache contains only entry {@code GridCacheSequenceValue}. */
private IgniteInternalCache<GridCacheInternalKey, GridCacheAtomicSequenceValue> seqView;
/** Cache context for atomic data structures. */
private GridCacheContext dsCacheCtx;
/** Atomic data structures configuration. */
private final AtomicConfiguration atomicCfg;
/** */
private IgniteInternalCache<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>> utilityCache;
/** */
private IgniteInternalCache<CacheDataStructuresCacheKey, List<CacheCollectionInfo>> utilityDataCache;
/** */
private volatile UUID qryId;
/** Listener. */
private final GridLocalEventListener lsnr = new GridLocalEventListener() {
@Override public void onEvent(final Event evt) {
// This may require cache operation to execute,
// therefore cannot use event notification thread.
ctx.closure().callLocalSafe(
new Callable<Object>() {
@Override public Object call() throws Exception {
DiscoveryEvent discoEvt = (DiscoveryEvent)evt;
UUID leftNodeId = discoEvt.eventNode().id();
for (GridCacheRemovable ds : dsMap.values()) {
if (ds instanceof GridCacheSemaphoreEx)
((GridCacheSemaphoreEx)ds).onNodeRemoved(leftNodeId);
else if (ds instanceof GridCacheLockEx)
((GridCacheLockEx)ds).onNodeRemoved(leftNodeId);
}
return null;
}
},
false);
}
};
/**
* @param ctx Context.
*/
public DataStructuresProcessor(GridKernalContext ctx) {
super(ctx);
dsMap = new ConcurrentHashMap8<>(INITIAL_CAPACITY);
atomicCfg = ctx.config().getAtomicConfiguration();
}
/** {@inheritDoc} */
@Override public void start(boolean activeOnStart) throws IgniteCheckedException {
super.start(activeOnStart);
if (!activeOnStart)
return;
ctx.event().addLocalEventListener(lsnr, EVT_NODE_LEFT, EVT_NODE_FAILED);
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public void onKernalStart(boolean activeOnStart) throws IgniteCheckedException {
if (ctx.config().isDaemon() || !ctx.state().active())
return;
onKernalStart0(activeOnStart);
}
/**
*
*/
private void onKernalStart0(boolean activeOnStart){
if (!activeOnStart && ctx.state().active())
ctx.event().addLocalEventListener(lsnr, EVT_NODE_LEFT, EVT_NODE_FAILED);
utilityCache = (IgniteInternalCache)ctx.cache().utilityCache();
utilityDataCache = (IgniteInternalCache)ctx.cache().utilityCache();
assert utilityCache != null;
if (atomicCfg != null) {
IgniteInternalCache atomicsCache = ctx.cache().atomicsCache();
assert atomicsCache != null;
dsView = atomicsCache;
cntDownLatchView = atomicsCache;
semView = atomicsCache;
reentrantLockView = atomicsCache;
atomicLongView = atomicsCache;
atomicRefView = atomicsCache;
atomicStampedView = atomicsCache;
seqView = atomicsCache;
dsCacheCtx = atomicsCache.context();
}
initLatch.countDown();
}
/**
* @throws IgniteCheckedException If failed.
*/
private void startQuery() throws IgniteCheckedException {
if (qryId == null) {
synchronized (this) {
if (qryId == null) {
qryId = dsCacheCtx.continuousQueries().executeInternalQuery(
new DataStructuresEntryListener(),
new DataStructuresEntryFilter(),
dsCacheCtx.isReplicated() && dsCacheCtx.affinityNode(),
false,
false
);
}
}
}
}
/** {@inheritDoc} */
@Override public void onKernalStop(boolean cancel) {
super.onKernalStop(cancel);
for (GridCacheRemovable ds : dsMap.values()) {
if (ds instanceof GridCacheSemaphoreEx)
((GridCacheSemaphoreEx)ds).stop();
if (ds instanceof GridCacheLockEx)
((GridCacheLockEx)ds).onStop();
}
if (initLatch.getCount() > 0) {
initFailed = true;
initLatch.countDown();
}
if (qryId != null)
dsCacheCtx.continuousQueries().cancelInternalQuery(qryId);
}
/** {@inheritDoc} */
@Override public void onActivate(GridKernalContext ctx) throws IgniteCheckedException {
if (log.isDebugEnabled())
log.debug("Activate data structure processor [nodeId=" + ctx.localNodeId() +
" topVer=" + ctx.discovery().topologyVersionEx() + " ]");
this.initFailed = false;
this.initLatch = new CountDownLatch(1);
this.qryId = null;
ctx.event().addLocalEventListener(lsnr, EVT_NODE_LEFT, EVT_NODE_FAILED);
onKernalStart0(true);
for (Map.Entry<GridCacheInternal, GridCacheRemovable> e : dsMap.entrySet()) {
GridCacheRemovable v = e.getValue();
if (v instanceof IgniteChangeGlobalStateSupport)
((IgniteChangeGlobalStateSupport)v).onActivate(ctx);
}
}
/** {@inheritDoc} */
@Override public void onDeActivate(GridKernalContext ctx) throws IgniteCheckedException {
if (log.isDebugEnabled())
log.debug("DeActivate data structure processor [nodeId=" + ctx.localNodeId() +
" topVer=" + ctx.discovery().topologyVersionEx() + " ]");
ctx.event().removeLocalEventListener(lsnr, EVT_NODE_LEFT, EVT_NODE_FAILED);
onKernalStop(false);
for (Map.Entry<GridCacheInternal, GridCacheRemovable> e : dsMap.entrySet()) {
GridCacheRemovable v = e.getValue();
if (v instanceof IgniteChangeGlobalStateSupport)
((IgniteChangeGlobalStateSupport)v).onDeActivate(ctx);
}
}
/**
* @param key Key.
* @param obj Object.
*/
void onRemoved(GridCacheInternal key, GridCacheRemovable obj) {
dsMap.remove(key, obj);
}
/** {@inheritDoc} */
@Override public IgniteInternalFuture<?> onReconnected(boolean clusterRestarted) throws IgniteCheckedException {
for (Map.Entry<GridCacheInternal, GridCacheRemovable> e : dsMap.entrySet()) {
GridCacheRemovable obj = e.getValue();
if (clusterRestarted) {
obj.onRemoved();
dsMap.remove(e.getKey(), obj);
}
else
obj.needCheckNotRemoved();
}
for (GridCacheContext cctx : ctx.cache().context().cacheContexts())
cctx.dataStructures().onReconnected(clusterRestarted);
return null;
}
/**
* Gets a sequence from cache or creates one if it's not cached.
*
* @param name Sequence name.
* @param initVal Initial value for sequence. If sequence already cached, {@code initVal} will be ignored.
* @param create If {@code true} sequence will be created in case it is not in cache.
* @return Sequence.
* @throws IgniteCheckedException If loading failed.
*/
public final IgniteAtomicSequence sequence(final String name,
final long initVal,
final boolean create)
throws IgniteCheckedException
{
A.notNull(name, "name");
awaitInitialization();
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteAtomicSequence>() {
@Override public IgniteAtomicSequence applyx() throws IgniteCheckedException {
GridCacheInternalKey key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheAtomicSequenceValue seqVal = cast(dsView.get(key), GridCacheAtomicSequenceValue.class);
// Check that sequence hasn't been created in other thread yet.
GridCacheAtomicSequenceEx seq = cast(dsMap.get(key), GridCacheAtomicSequenceEx.class);
if (seq != null) {
assert seqVal != null;
return seq;
}
if (seqVal == null && !create)
return null;
// We should use offset because we already reserved left side of range.
long off = atomicCfg.getAtomicSequenceReserveSize() > 1 ?
atomicCfg.getAtomicSequenceReserveSize() - 1 : 1;
long upBound;
long locCntr;
if (seqVal == null) {
locCntr = initVal;
upBound = locCntr + off;
// Global counter must be more than reserved region.
seqVal = new GridCacheAtomicSequenceValue(upBound + 1);
}
else {
locCntr = seqVal.get();
upBound = locCntr + off;
// Global counter must be more than reserved region.
seqVal.set(upBound + 1);
}
// Update global counter.
dsView.put(key, seqVal);
// Only one thread can be in the transaction scope and create sequence.
seq = new GridCacheAtomicSequenceImpl(name,
key,
seqView,
dsCacheCtx,
atomicCfg.getAtomicSequenceReserveSize(),
locCntr,
upBound);
dsMap.put(key, seq);
tx.commit();
return seq;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to make atomic sequence: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, ATOMIC_SEQ, null), create, IgniteAtomicSequence.class);
}
/**
* Removes sequence from cache.
*
* @param name Sequence name.
* @throws IgniteCheckedException If removing failed.
*/
final void removeSequence(final String name) throws IgniteCheckedException {
assert name != null;
awaitInitialization();
checkAtomicsConfiguration();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
dsCacheCtx.gate().enter();
try {
dsView.remove(new GridCacheInternalKeyImpl(name));
}
finally {
dsCacheCtx.gate().leave();
}
return null;
}
}, name, ATOMIC_SEQ, null);
}
/**
* Gets an atomic long from cache or creates one if it's not cached.
*
* @param name Name of atomic long.
* @param initVal Initial value for atomic long. If atomic long already cached, {@code initVal}
* will be ignored.
* @param create If {@code true} atomic long will be created in case it is not in cache.
* @return Atomic long.
* @throws IgniteCheckedException If loading failed.
*/
public final IgniteAtomicLong atomicLong(final String name,
final long initVal,
final boolean create) throws IgniteCheckedException {
A.notNull(name, "name");
awaitInitialization();
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteAtomicLong>() {
@Override public IgniteAtomicLong applyx() throws IgniteCheckedException {
final GridCacheInternalKey key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheAtomicLongValue val = cast(dsView.get(key), GridCacheAtomicLongValue.class);
// Check that atomic long hasn't been created in other thread yet.
GridCacheAtomicLongEx a = cast(dsMap.get(key), GridCacheAtomicLongEx.class);
if (a != null) {
assert val != null;
return a;
}
if (val == null && !create)
return null;
if (val == null) {
val = new GridCacheAtomicLongValue(initVal);
dsView.put(key, val);
}
a = new GridCacheAtomicLongImpl(name, key, atomicLongView, dsCacheCtx);
dsMap.put(key, a);
tx.commit();
return a;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to make atomic long: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, ATOMIC_LONG, null), create, IgniteAtomicLong.class);
}
/**
* @param c Closure creating data structure instance.
* @param dsInfo Data structure info.
* @param create Create flag.
* @param cls Expected data structure class.
* @return Data structure instance.
* @throws IgniteCheckedException If failed.
*/
@Nullable private <T> T getAtomic(final IgniteOutClosureX<T> c,
final DataStructureInfo dsInfo,
final boolean create,
Class<? extends T> cls)
throws IgniteCheckedException
{
Map<String, DataStructureInfo> dsMap = utilityCache.get(DATA_STRUCTURES_KEY);
if (!create && (dsMap == null || !dsMap.containsKey(dsInfo.name)))
return null;
IgniteCheckedException err = validateDataStructure(dsMap, dsInfo, create);
if (err != null)
throw err;
final GridCacheInternalKey key = new GridCacheInternalKeyImpl(dsInfo.name);
// Check type of structure received by key from local cache.
T dataStructure = cast(this.dsMap.get(key), cls);
if (dataStructure != null)
return dataStructure;
return retryTopologySafe(new IgniteOutClosureX<T>() {
@Override public T applyx() throws IgniteCheckedException {
if (!create)
return c.applyx();
try (GridNearTxLocal tx = utilityCache.txStartEx(PESSIMISTIC, REPEATABLE_READ)) {
IgniteCheckedException err =
utilityCache.invoke(DATA_STRUCTURES_KEY, new AddAtomicProcessor(dsInfo)).get();
if (err != null)
throw err;
T dataStructure = c.applyx();
tx.commit();
return dataStructure;
}
}
});
}
/**
* Removes atomic long from cache.
*
* @param name Atomic long name.
* @throws IgniteCheckedException If removing failed.
*/
final void removeAtomicLong(final String name) throws IgniteCheckedException {
assert name != null;
assert dsCacheCtx != null;
awaitInitialization();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
dsCacheCtx.gate().enter();
try {
dsView.remove(new GridCacheInternalKeyImpl(name));
}
finally {
dsCacheCtx.gate().leave();
}
return null;
}
}, name, ATOMIC_LONG, null);
}
/**
* @param c Closure.
* @param name Data structure name.
* @param type Data structure type.
* @param afterRmv Optional closure to run after data structure removed.
* @throws IgniteCheckedException If failed.
*/
private <T> void removeDataStructure(final IgniteOutClosureX<T> c,
String name,
DataStructureType type,
@Nullable final IgniteInClosureX<T> afterRmv)
throws IgniteCheckedException
{
Map<String, DataStructureInfo> dsMap = utilityCache.get(DATA_STRUCTURES_KEY);
if (dsMap == null || !dsMap.containsKey(name))
return;
final DataStructureInfo dsInfo = new DataStructureInfo(name, type, null);
IgniteCheckedException err = validateDataStructure(dsMap, dsInfo, false);
if (err != null)
throw err;
retryTopologySafe(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
try (GridNearTxLocal tx = utilityCache.txStartEx(PESSIMISTIC, REPEATABLE_READ)) {
T2<Boolean, IgniteCheckedException> res =
utilityCache.invoke(DATA_STRUCTURES_KEY, new RemoveDataStructureProcessor(dsInfo)).get();
IgniteCheckedException err = res.get2();
if (err != null)
throw err;
assert res.get1() != null;
boolean exists = res.get1();
if (!exists)
return null;
T rmvInfo = c.applyx();
tx.commit();
if (afterRmv != null && rmvInfo != null)
afterRmv.applyx(rmvInfo);
return null;
}
}
});
}
/**
* Gets an atomic reference from cache or creates one if it's not cached.
*
* @param name Name of atomic reference.
* @param initVal Initial value for atomic reference. If atomic reference already cached, {@code initVal}
* will be ignored.
* @param create If {@code true} atomic reference will be created in case it is not in cache.
* @return Atomic reference.
* @throws IgniteCheckedException If loading failed.
*/
@SuppressWarnings("unchecked")
public final <T> IgniteAtomicReference<T> atomicReference(final String name,
final T initVal,
final boolean create)
throws IgniteCheckedException
{
A.notNull(name, "name");
awaitInitialization();
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteAtomicReference>() {
@Override public IgniteAtomicReference<T> applyx() throws IgniteCheckedException {
GridCacheInternalKey key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheAtomicReferenceValue val = cast(dsView.get(key),
GridCacheAtomicReferenceValue.class);
// Check that atomic reference hasn't been created in other thread yet.
GridCacheAtomicReferenceEx ref = cast(dsMap.get(key),
GridCacheAtomicReferenceEx.class);
if (ref != null) {
assert val != null;
return ref;
}
if (val == null && !create)
return null;
if (val == null) {
val = new GridCacheAtomicReferenceValue(initVal);
dsView.put(key, val);
}
ref = new GridCacheAtomicReferenceImpl(name, key, atomicRefView, dsCacheCtx);
dsMap.put(key, ref);
tx.commit();
return ref;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to make atomic reference: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, ATOMIC_REF, null), create, IgniteAtomicReference.class);
}
/**
* Removes atomic reference from cache.
*
* @param name Atomic reference name.
* @throws IgniteCheckedException If removing failed.
*/
final void removeAtomicReference(final String name) throws IgniteCheckedException {
assert name != null;
assert dsCacheCtx != null;
awaitInitialization();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
dsCacheCtx.gate().enter();
try {
dsView.remove(new GridCacheInternalKeyImpl(name));
}
finally {
dsCacheCtx.gate().leave();
}
return null;
}
}, name, ATOMIC_REF, null);
}
/**
* Gets an atomic stamped from cache or creates one if it's not cached.
*
* @param name Name of atomic stamped.
* @param initVal Initial value for atomic stamped. If atomic stamped already cached, {@code initVal}
* will be ignored.
* @param initStamp Initial stamp for atomic stamped. If atomic stamped already cached, {@code initStamp}
* will be ignored.
* @param create If {@code true} atomic stamped will be created in case it is not in cache.
* @return Atomic stamped.
* @throws IgniteCheckedException If loading failed.
*/
@SuppressWarnings("unchecked")
public final <T, S> IgniteAtomicStamped<T, S> atomicStamped(final String name, final T initVal,
final S initStamp, final boolean create) throws IgniteCheckedException {
A.notNull(name, "name");
awaitInitialization();
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteAtomicStamped>() {
@Override public IgniteAtomicStamped<T, S> applyx() throws IgniteCheckedException {
GridCacheInternalKeyImpl key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheAtomicStampedValue val = cast(dsView.get(key),
GridCacheAtomicStampedValue.class);
// Check that atomic stamped hasn't been created in other thread yet.
GridCacheAtomicStampedEx stmp = cast(dsMap.get(key),
GridCacheAtomicStampedEx.class);
if (stmp != null) {
assert val != null;
return stmp;
}
if (val == null && !create)
return null;
if (val == null) {
val = new GridCacheAtomicStampedValue(initVal, initStamp);
dsView.put(key, val);
}
stmp = new GridCacheAtomicStampedImpl(name, key, atomicStampedView, dsCacheCtx);
dsMap.put(key, stmp);
tx.commit();
return stmp;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to make atomic stamped: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, ATOMIC_STAMPED, null), create, IgniteAtomicStamped.class);
}
/**
* Removes atomic stamped from cache.
*
* @param name Atomic stamped name.
* @throws IgniteCheckedException If removing failed.
*/
final void removeAtomicStamped(final String name) throws IgniteCheckedException {
assert name != null;
assert dsCacheCtx != null;
awaitInitialization();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
dsCacheCtx.gate().enter();
try {
dsView.remove(new GridCacheInternalKeyImpl(name));
}
finally {
dsCacheCtx.gate().leave();
}
return null;
}
}, name, ATOMIC_STAMPED, null);
}
/**
* Gets a queue from cache or creates one if it's not cached.
*
* @param name Name of queue.
* @param cap Max size of queue.
* @param cfg Non-null queue configuration if new queue should be created.
* @return Instance of queue.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
public final <T> IgniteQueue<T> queue(final String name,
int cap,
@Nullable final CollectionConfiguration cfg)
throws IgniteCheckedException {
A.notNull(name, "name");
awaitInitialization();
String cacheName = null;
if (cfg != null) {
if (cap <= 0)
cap = Integer.MAX_VALUE;
cacheName = compatibleConfiguration(cfg);
}
DataStructureInfo dsInfo = new DataStructureInfo(name,
QUEUE,
cfg != null ? new QueueInfo(cacheName, cfg.isCollocated(), cap) : null);
final int cap0 = cap;
final boolean create = cfg != null;
return getCollection(new IgniteClosureX<GridCacheContext, IgniteQueue<T>>() {
@Override public IgniteQueue<T> applyx(GridCacheContext ctx) throws IgniteCheckedException {
return ctx.dataStructures().queue(name, cap0, create && cfg.isCollocated(), create);
}
}, dsInfo, create);
}
/**
* @param cfg Collection configuration.
* @param name Cache name.
* @return Cache configuration.
*/
private CacheConfiguration cacheConfiguration(CollectionConfiguration cfg, String name) {
CacheConfiguration ccfg = new CacheConfiguration();
ccfg.setName(name);
ccfg.setBackups(cfg.getBackups());
ccfg.setCacheMode(cfg.getCacheMode());
ccfg.setAtomicityMode(cfg.getAtomicityMode());
ccfg.setNodeFilter(cfg.getNodeFilter());
ccfg.setWriteSynchronizationMode(FULL_SYNC);
ccfg.setRebalanceMode(SYNC);
return ccfg;
}
/**
* @param cfg Collection configuration.
* @return Cache name.
* @throws IgniteCheckedException If failed.
*/
private String compatibleConfiguration(CollectionConfiguration cfg) throws IgniteCheckedException {
List<CacheCollectionInfo> caches = utilityDataCache.context().affinityNode() ?
utilityDataCache.localPeek(DATA_STRUCTURES_CACHE_KEY, null, null) :
utilityDataCache.get(DATA_STRUCTURES_CACHE_KEY);
String cacheName = findCompatibleConfiguration(cfg, caches);
if (cacheName == null)
cacheName = utilityDataCache.invoke(DATA_STRUCTURES_CACHE_KEY, new AddDataCacheProcessor(cfg)).get();
assert cacheName != null;
CacheConfiguration newCfg = cacheConfiguration(cfg, cacheName);
if (ctx.cache().cache(cacheName) == null) {
ctx.cache().dynamicStartCache(newCfg,
cacheName,
null,
CacheType.INTERNAL,
false,
true,
true).get();
}
assert ctx.cache().cache(cacheName) != null : cacheName;
return cacheName;
}
/**
* @param name Queue name.
* @param cctx Queue cache context.
* @throws IgniteCheckedException If failed.
*/
public void removeQueue(final String name, final GridCacheContext cctx) throws IgniteCheckedException {
assert name != null;
assert cctx != null;
awaitInitialization();
IgniteOutClosureX<GridCacheQueueHeader> rmv = new IgniteOutClosureX<GridCacheQueueHeader>() {
@Override public GridCacheQueueHeader applyx() throws IgniteCheckedException {
return (GridCacheQueueHeader)cctx.cache().withNoRetries().getAndRemove(new GridCacheQueueHeaderKey(name));
}
};
CIX1<GridCacheQueueHeader> afterRmv = new CIX1<GridCacheQueueHeader>() {
@Override public void applyx(GridCacheQueueHeader hdr) throws IgniteCheckedException {
if (hdr.empty())
return;
GridCacheQueueAdapter.removeKeys(cctx.cache(),
hdr.id(),
name,
hdr.collocated(),
hdr.head(),
hdr.tail(),
0);
}
};
removeDataStructure(rmv, name, QUEUE, afterRmv);
}
/**
* @param c Closure creating collection.
* @param dsInfo Data structure info.
* @param create Create flag.
* @return Collection instance.
* @throws IgniteCheckedException If failed.
*/
@Nullable private <T> T getCollection(final IgniteClosureX<GridCacheContext, T> c,
final DataStructureInfo dsInfo,
boolean create)
throws IgniteCheckedException
{
awaitInitialization();
Map<String, DataStructureInfo> dsMap = utilityCache.get(DATA_STRUCTURES_KEY);
if (!create && (dsMap == null || !dsMap.containsKey(dsInfo.name)))
return null;
IgniteCheckedException err = validateDataStructure(dsMap, dsInfo, create);
if (err != null)
throw err;
if (!create) {
DataStructureInfo oldInfo = dsMap.get(dsInfo.name);
assert oldInfo.info instanceof CollectionInfo : oldInfo.info;
String cacheName = ((CollectionInfo)oldInfo.info).cacheName;
GridCacheContext cacheCtx = ctx.cache().getOrStartCache(cacheName).context();
return c.applyx(cacheCtx);
}
return retryTopologySafe(new IgniteOutClosureX<T>() {
@Override public T applyx() throws IgniteCheckedException {
try (GridNearTxLocal tx = utilityCache.txStartEx(PESSIMISTIC, REPEATABLE_READ)) {
T2<String, IgniteCheckedException> res =
utilityCache.invoke(DATA_STRUCTURES_KEY, new AddCollectionProcessor(dsInfo)).get();
IgniteCheckedException err = res.get2();
if (err != null)
throw err;
String cacheName = res.get1();
final GridCacheContext cacheCtx = ctx.cache().internalCache(cacheName).context();
T col = c.applyx(cacheCtx);
tx.commit();
return col;
}
}
});
}
/**
* Awaits for processor initialization.
*/
private void awaitInitialization() {
if (initLatch.getCount() > 0) {
try {
U.await(initLatch);
if (initFailed)
throw new IllegalStateException("Failed to initialize data structures processor.");
}
catch (IgniteInterruptedCheckedException e) {
throw new IllegalStateException("Failed to initialize data structures processor " +
"(thread has been interrupted).", e);
}
}
}
/**
* @param dsMap Map with data structure information.
* @param info New data structure information.
* @param create Create flag.
* @return {@link IgniteException} if validation failed.
*/
@Nullable private static IgniteCheckedException validateDataStructure(
@Nullable Map<String, DataStructureInfo> dsMap,
DataStructureInfo info,
boolean create)
{
if (dsMap == null)
return null;
DataStructureInfo oldInfo = dsMap.get(info.name);
if (oldInfo != null)
return oldInfo.validate(info, create);
return null;
}
/**
* Gets or creates count down latch. If count down latch is not found in cache,
* it is created using provided name and count parameter.
*
* @param name Name of the latch.
* @param cnt Initial count.
* @param autoDel {@code True} to automatically delete latch from cache when
* its count reaches zero.
* @param create If {@code true} latch will be created in case it is not in cache,
* if it is {@code false} all parameters except {@code name} are ignored.
* @return Count down latch for the given name or {@code null} if it is not found and
* {@code create} is false.
* @throws IgniteCheckedException If operation failed.
*/
public IgniteCountDownLatch countDownLatch(final String name,
final int cnt,
final boolean autoDel,
final boolean create)
throws IgniteCheckedException
{
A.notNull(name, "name");
awaitInitialization();
if (create)
A.ensure(cnt >= 0, "count can not be negative");
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteCountDownLatch>() {
@Override public IgniteCountDownLatch applyx() throws IgniteCheckedException {
GridCacheInternalKey key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheCountDownLatchValue val = cast(dsView.get(key), GridCacheCountDownLatchValue.class);
// Check that count down hasn't been created in other thread yet.
GridCacheCountDownLatchEx latch = cast(dsMap.get(key), GridCacheCountDownLatchEx.class);
if (latch != null) {
assert val != null;
return latch;
}
if (val == null && !create)
return null;
if (val == null) {
val = new GridCacheCountDownLatchValue(cnt, autoDel);
dsView.put(key, val);
}
latch = new GridCacheCountDownLatchImpl(name, val.initialCount(),
val.autoDelete(),
key,
cntDownLatchView,
dsCacheCtx);
dsMap.put(key, latch);
tx.commit();
return latch;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to create count down latch: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, COUNT_DOWN_LATCH, null), create, GridCacheCountDownLatchEx.class);
}
/**
* Removes count down latch from cache.
*
* @param name Name of the latch.
* @throws IgniteCheckedException If operation failed.
*/
public void removeCountDownLatch(final String name) throws IgniteCheckedException {
assert name != null;
assert dsCacheCtx != null;
awaitInitialization();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
GridCacheInternal key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
// Check correctness type of removable object.
GridCacheCountDownLatchValue val =
cast(dsView.get(key), GridCacheCountDownLatchValue.class);
if (val != null) {
if (val.get() > 0) {
throw new IgniteCheckedException("Failed to remove count down latch " +
"with non-zero count: " + val.get());
}
dsView.remove(key);
tx.commit();
}
else
tx.setRollbackOnly();
return null;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, name, COUNT_DOWN_LATCH, null);
}
/**
* Gets or creates semaphore. If semaphore is not found in cache,
* it is created using provided name and count parameter.
*
* @param name Name of the semaphore.
* @param cnt Initial count.
* @param failoverSafe {@code True} FailoverSafe parameter.
* @param create If {@code true} semaphore will be created in case it is not in cache,
* if it is {@code false} all parameters except {@code name} are ignored.
* @return Semaphore for the given name or {@code null} if it is not found and
* {@code create} is false.
* @throws IgniteCheckedException If operation failed.
*/
public IgniteSemaphore semaphore(final String name, final int cnt, final boolean failoverSafe, final boolean create)
throws IgniteCheckedException {
A.notNull(name, "name");
awaitInitialization();
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteSemaphore>() {
@Override public IgniteSemaphore applyx() throws IgniteCheckedException {
GridCacheInternalKey key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheSemaphoreState val = cast(dsView.get(key), GridCacheSemaphoreState.class);
// Check that semaphore hasn't been created in other thread yet.
GridCacheSemaphoreEx sem = cast(dsMap.get(key), GridCacheSemaphoreEx.class);
if (sem != null) {
assert val != null;
return sem;
}
if (val == null && !create)
return null;
if (val == null) {
val = new GridCacheSemaphoreState(cnt, new HashMap<UUID, Integer>(), failoverSafe);
dsView.put(key, val);
}
GridCacheSemaphoreEx sem0 = new GridCacheSemaphoreImpl(
name,
key,
semView,
dsCacheCtx);
dsMap.put(key, sem0);
tx.commit();
return sem0;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to create semaphore: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, SEMAPHORE, null), create, GridCacheSemaphoreEx.class);
}
/**
* Removes semaphore from cache.
*
* @param name Name of the semaphore.
* @throws IgniteCheckedException If operation failed.
*/
public void removeSemaphore(final String name) throws IgniteCheckedException {
assert name != null;
assert dsCacheCtx != null;
awaitInitialization();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
GridCacheInternal key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
// Check correctness type of removable object.
GridCacheSemaphoreState val = cast(dsView.get(key), GridCacheSemaphoreState.class);
if (val != null) {
if (val.getCount() < 0)
throw new IgniteCheckedException("Failed to remove semaphore with blocked threads. ");
dsView.remove(key);
tx.commit();
}
else
tx.setRollbackOnly();
return null;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, name, SEMAPHORE, null);
}
/**
* Gets or creates reentrant lock. If reentrant lock is not found in cache,
* it is created using provided name, failover mode, and fairness mode parameters.
*
* @param name Name of the reentrant lock.
* @param failoverSafe Flag indicating behaviour in case of failure.
* @param fair Flag indicating fairness policy of this lock.
* @param create If {@code true} reentrant lock will be created in case it is not in cache.
* @return ReentrantLock for the given name or {@code null} if it is not found and
* {@code create} is false.
* @throws IgniteCheckedException If operation failed.
*/
public IgniteLock reentrantLock(final String name, final boolean failoverSafe, final boolean fair, final boolean create)
throws IgniteCheckedException {
A.notNull(name, "name");
awaitInitialization();
checkAtomicsConfiguration();
startQuery();
return getAtomic(new IgniteOutClosureX<IgniteLock>() {
@Override public IgniteLock applyx() throws IgniteCheckedException {
GridCacheInternalKey key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
GridCacheLockState val = cast(dsView.get(key), GridCacheLockState.class);
// Check that reentrant lock hasn't been created in other thread yet.
GridCacheLockEx reentrantLock = cast(dsMap.get(key), GridCacheLockEx.class);
if (reentrantLock != null) {
assert val != null;
return reentrantLock;
}
if (val == null && !create)
return null;
if (val == null) {
val = new GridCacheLockState(0, dsCacheCtx.nodeId(), 0, failoverSafe, fair);
dsView.put(key, val);
}
GridCacheLockEx reentrantLock0 = new GridCacheLockImpl(
name,
key,
reentrantLockView,
dsCacheCtx);
dsMap.put(key, reentrantLock0);
tx.commit();
return reentrantLock0;
}
catch (Error | Exception e) {
dsMap.remove(key);
U.error(log, "Failed to create reentrant lock: " + name, e);
throw e;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, new DataStructureInfo(name, REENTRANT_LOCK, null), create, GridCacheLockEx.class);
}
/**
* Removes reentrant lock from cache.
*
* @param name Name of the reentrant lock.
* @param broken Flag indicating the reentrant lock is broken and should be removed unconditionally.
* @throws IgniteCheckedException If operation failed.
*/
public void removeReentrantLock(final String name, final boolean broken) throws IgniteCheckedException {
assert name != null;
assert dsCacheCtx != null;
awaitInitialization();
removeDataStructure(new IgniteOutClosureX<Void>() {
@Override public Void applyx() throws IgniteCheckedException {
GridCacheInternal key = new GridCacheInternalKeyImpl(name);
dsCacheCtx.gate().enter();
try (GridNearTxLocal tx = CU.txStartInternal(dsCacheCtx, dsView, PESSIMISTIC, REPEATABLE_READ)) {
// Check correctness type of removable object.
GridCacheLockState val = cast(dsView.get(key), GridCacheLockState.class);
if (val != null) {
if (val.get() > 0 && !broken)
throw new IgniteCheckedException("Failed to remove reentrant lock with blocked threads. ");
dsView.remove(key);
tx.commit();
}
else
tx.setRollbackOnly();
return null;
}
finally {
dsCacheCtx.gate().leave();
}
}
}, name, REENTRANT_LOCK, null);
}
/**
*
*/
static class DataStructuresEntryFilter implements CacheEntryEventSerializableFilter<Object, Object> {
/** */
private static final long serialVersionUID = 0L;
/** {@inheritDoc} */
@Override public boolean evaluate(CacheEntryEvent<?, ?> evt) throws CacheEntryListenerException {
if (evt.getEventType() == EventType.CREATED || evt.getEventType() == EventType.UPDATED)
return evt.getValue() instanceof GridCacheCountDownLatchValue ||
evt.getValue() instanceof GridCacheSemaphoreState ||
evt.getValue() instanceof GridCacheLockState;
else {
assert evt.getEventType() == EventType.REMOVED : evt;
return true;
}
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DataStructuresEntryFilter.class, this);
}
}
/**
*
*/
private class DataStructuresEntryListener implements
CacheEntryUpdatedListener<GridCacheInternalKey, GridCacheInternal> {
/** {@inheritDoc} */
@Override public void onUpdated(
Iterable<CacheEntryEvent<? extends GridCacheInternalKey, ? extends GridCacheInternal>> evts)
throws CacheEntryListenerException
{
for (CacheEntryEvent<? extends GridCacheInternalKey, ? extends GridCacheInternal> evt : evts) {
if (evt.getEventType() == EventType.CREATED || evt.getEventType() == EventType.UPDATED) {
GridCacheInternal val0 = evt.getValue();
if (val0 instanceof GridCacheCountDownLatchValue) {
GridCacheInternalKey key = evt.getKey();
// Notify latch on changes.
final GridCacheRemovable latch = dsMap.get(key);
GridCacheCountDownLatchValue val = (GridCacheCountDownLatchValue)val0;
if (latch instanceof GridCacheCountDownLatchEx) {
final GridCacheCountDownLatchEx latch0 = (GridCacheCountDownLatchEx)latch;
latch0.onUpdate(val.get());
if (val.get() == 0 && val.autoDelete()) {
dsMap.remove(key);
IgniteInternalFuture<?> removeFut = ctx.closure().runLocalSafe(new GPR() {
@Override public void run() {
try {
removeCountDownLatch(latch0.name());
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to remove count down latch: " + latch0.name(), e);
}
finally {
ctx.cache().context().txContextReset();
}
}
});
removeFut.listen(new CI1<IgniteInternalFuture<?>>() {
@Override public void apply(IgniteInternalFuture<?> f) {
try {
f.get();
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to remove count down latch: " + latch0.name(), e);
}
latch.onRemoved();
}
});
}
}
else if (latch != null) {
U.error(log, "Failed to cast object " +
"[expected=" + IgniteCountDownLatch.class.getSimpleName() +
", actual=" + latch.getClass() + ", value=" + latch + ']');
}
}
else if (val0 instanceof GridCacheSemaphoreState) {
GridCacheInternalKey key = evt.getKey();
// Notify semaphore on changes.
final GridCacheRemovable sem = dsMap.get(key);
GridCacheSemaphoreState val = (GridCacheSemaphoreState)val0;
if (sem instanceof GridCacheSemaphoreEx) {
final GridCacheSemaphoreEx semaphore0 = (GridCacheSemaphoreEx)sem;
semaphore0.onUpdate(val);
}
else if (sem != null) {
U.error(log, "Failed to cast object " +
"[expected=" + IgniteSemaphore.class.getSimpleName() +
", actual=" + sem.getClass() + ", value=" + sem + ']');
}
}
else if (val0 instanceof GridCacheLockState) {
GridCacheInternalKey key = evt.getKey();
// Notify reentrant lock on changes.
final GridCacheRemovable reentrantLock = dsMap.get(key);
GridCacheLockState val = (GridCacheLockState)val0;
if (reentrantLock instanceof GridCacheLockEx) {
final GridCacheLockEx lock0 = (GridCacheLockEx)reentrantLock;
lock0.onUpdate(val);
}
else if (reentrantLock != null) {
U.error(log, "Failed to cast object " +
"[expected=" + IgniteLock.class.getSimpleName() +
", actual=" + reentrantLock.getClass() + ", value=" + reentrantLock + ']');
}
}
}
else {
assert evt.getEventType() == EventType.REMOVED : evt;
GridCacheInternal key = evt.getKey();
// Entry's val is null if entry deleted.
GridCacheRemovable obj = dsMap.remove(key);
if (obj != null)
obj.onRemoved();
}
}
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DataStructuresEntryListener.class, this);
}
}
/**
* Gets a set from cache or creates one if it's not cached.
*
* @param name Set name.
* @param cfg Set configuration if new set should be created.
* @return Set instance.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
@Nullable public <T> IgniteSet<T> set(final String name,
@Nullable final CollectionConfiguration cfg)
throws IgniteCheckedException {
A.notNull(name, "name");
awaitInitialization();
String cacheName = null;
if (cfg != null)
cacheName = compatibleConfiguration(cfg);
DataStructureInfo dsInfo = new DataStructureInfo(name,
SET,
cfg != null ? new CollectionInfo(cacheName, cfg.isCollocated()) : null);
final boolean create = cfg != null;
return getCollection(new CX1<GridCacheContext, IgniteSet<T>>() {
@Override public IgniteSet<T> applyx(GridCacheContext cctx) throws IgniteCheckedException {
return cctx.dataStructures().set(name, create ? cfg.isCollocated() : false, create);
}
}, dsInfo, create);
}
/**
* @param name Set name.
* @param cctx Set cache context.
* @throws IgniteCheckedException If failed.
*/
public void removeSet(final String name, final GridCacheContext cctx) throws IgniteCheckedException {
assert name != null;
assert cctx != null;
awaitInitialization();
IgniteOutClosureX<GridCacheSetHeader> rmv = new IgniteOutClosureX<GridCacheSetHeader>() {
@Override public GridCacheSetHeader applyx() throws IgniteCheckedException {
return (GridCacheSetHeader)cctx.cache().withNoRetries().getAndRemove(new GridCacheSetHeaderKey(name));
}
};
CIX1<GridCacheSetHeader> afterRmv = new CIX1<GridCacheSetHeader>() {
@Override public void applyx(GridCacheSetHeader hdr) throws IgniteCheckedException {
cctx.dataStructures().removeSetData(hdr.id());
}
};
removeDataStructure(rmv, name, SET, afterRmv);
}
/**
* @param log Logger.
* @param call Callable.
* @return Callable result.
* @throws IgniteCheckedException If all retries failed.
*/
public static <R> R retry(IgniteLogger log, Callable<R> call) throws IgniteCheckedException {
try {
return GridCacheUtils.retryTopologySafe(call);
}
catch (IgniteCheckedException e) {
throw e;
}
catch (Exception e) {
throw new IgniteCheckedException(e);
}
}
/**
* Tries to cast the object to expected type.
*
* @param obj Object which will be casted.
* @param cls Class
* @param <R> Type of expected result.
* @return Object has casted to expected type.
* @throws IgniteCheckedException If {@code obj} has different to {@code cls} type.
*/
@SuppressWarnings("unchecked")
@Nullable private <R> R cast(@Nullable Object obj, Class<R> cls) throws IgniteCheckedException {
if (obj == null)
return null;
if (cls.isInstance(obj))
return (R)obj;
else
throw new IgniteCheckedException("Failed to cast object [expected=" + cls +
", actual=" + obj.getClass() + ']');
}
/** {@inheritDoc} */
@Override public void printMemoryStats() {
X.println(">>> ");
X.println(">>> Data structure processor memory stats [igniteInstanceName=" + ctx.igniteInstanceName() +
", cache=" + (dsCacheCtx != null ? dsCacheCtx.name() : null) + ']');
X.println(">>> dsMapSize: " + dsMap.size());
}
/**
* @throws IgniteException If atomics configuration is not provided.
*/
private void checkAtomicsConfiguration() throws IgniteException {
if (atomicCfg == null)
throw new IgniteException("Atomic data structure can not be created, " +
"need to provide IgniteAtomicConfiguration.");
}
/**
* @param cfg Collection configuration.
* @param infos Data structure caches.
* @return Name of the cache with compatible configuration or null.
*/
private static String findCompatibleConfiguration(CollectionConfiguration cfg, List<CacheCollectionInfo> infos) {
if (infos == null)
return null;
for (CacheCollectionInfo col : infos) {
if (col.cfg.getAtomicityMode() == cfg.getAtomicityMode() &&
col.cfg.getCacheMode() == cfg.getCacheMode() &&
col.cfg.getBackups() == cfg.getBackups() &&
col.cfg.getOffHeapMaxMemory() == cfg.getOffHeapMaxMemory() &&
((col.cfg.getNodeFilter() == null && cfg.getNodeFilter() == null) ||
(col.cfg.getNodeFilter() != null && col.cfg.getNodeFilter().equals(cfg.getNodeFilter()))))
return col.cacheName;
}
return null;
}
/**
* @param c Closure to run.
* @throws IgniteCheckedException If failed.
* @return Closure return value.
*/
private static <T> T retryTopologySafe(IgniteOutClosureX<T> c) throws IgniteCheckedException {
for (int i = 0; i < GridCacheAdapter.MAX_RETRIES; i++) {
try {
return c.applyx();
}
catch (IgniteCheckedException e) {
if (i == GridCacheAdapter.MAX_RETRIES - 1)
throw e;
ClusterTopologyCheckedException topErr = e.getCause(ClusterTopologyCheckedException.class);
if (topErr == null || (topErr instanceof ClusterTopologyServerNotFoundException))
throw e;
IgniteInternalFuture<?> fut = topErr.retryReadyFuture();
if (fut != null)
fut.get();
}
}
assert false;
return null;
}
/**
*
*/
enum DataStructureType {
/** */
ATOMIC_LONG(IgniteAtomicLong.class.getSimpleName()),
/** */
ATOMIC_REF(IgniteAtomicReference.class.getSimpleName()),
/** */
ATOMIC_SEQ(IgniteAtomicSequence.class.getSimpleName()),
/** */
ATOMIC_STAMPED(IgniteAtomicStamped.class.getSimpleName()),
/** */
COUNT_DOWN_LATCH(IgniteCountDownLatch.class.getSimpleName()),
/** */
QUEUE(IgniteQueue.class.getSimpleName()),
/** */
SET(IgniteSet.class.getSimpleName()),
/** */
SEMAPHORE(IgniteSemaphore.class.getSimpleName()),
/** */
REENTRANT_LOCK(IgniteLock.class.getSimpleName());
/** */
private static final DataStructureType[] VALS = values();
/** */
private String name;
/**
* @param name Name.
*/
DataStructureType(String name) {
this.name = name;
}
/**
* @return Data structure public class name.
*/
public String className() {
return name;
}
/**
* @param ord Ordinal value.
* @return Enumerated value or {@code null} if ordinal out of range.
*/
@Nullable public static DataStructureType fromOrdinal(int ord) {
return ord >= 0 && ord < VALS.length ? VALS[ord] : null;
}
}
/**
*
*/
static class CollectionInfo implements Externalizable {
/** */
private static final long serialVersionUID = 0L;
/** */
private boolean collocated;
/** */
private String cacheName;
/**
* Required by {@link Externalizable}.
*/
public CollectionInfo() {
// No-op.
}
/**
* @param cacheName Collection cache name.
* @param collocated Collocated flag.
*/
public CollectionInfo(String cacheName, boolean collocated) {
this.cacheName = cacheName;
this.collocated = collocated;
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
collocated = in.readBoolean();
cacheName = U.readString(in);
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
out.writeBoolean(collocated);
U.writeString(out, cacheName);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(CollectionInfo.class, this);
}
}
/**
*
*/
static class CacheCollectionInfo implements Externalizable {
/** */
private static final long serialVersionUID = 0L;
/** */
private String cacheName;
/** */
private CollectionConfiguration cfg;
/**
* Required by {@link Externalizable}.
*/
public CacheCollectionInfo() {
// No-op.
}
/**
* @param cacheName Collection cache name.
* @param cfg CollectionConfiguration.
*/
public CacheCollectionInfo(String cacheName, CollectionConfiguration cfg) {
this.cacheName = cacheName;
this.cfg = cfg;
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
cfg = (CollectionConfiguration)in.readObject();
cacheName = U.readString(in);
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(cfg);
U.writeString(out, cacheName);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(CacheCollectionInfo.class, this);
}
}
/**
*
*/
static class QueueInfo extends CollectionInfo {
/** */
private static final long serialVersionUID = 0L;
/** */
private int cap;
/**
* Required by {@link Externalizable}.
*/
public QueueInfo() {
// No-op.
}
/**
* @param collocated Collocated flag.
* @param cap Queue capacity.
* @param cacheName Cache name.
*/
public QueueInfo(String cacheName, boolean collocated, int cap) {
super(cacheName, collocated);
this.cap = cap;
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
cap = in.readInt();
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeInt(cap);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(QueueInfo.class, this, "super", super.toString());
}
}
/**
*
*/
static class DataStructureInfo implements Externalizable {
/** */
private static final long serialVersionUID = 0L;
/** */
private String name;
/** */
private DataStructureType type;
/** */
private Object info;
/**
* Required by {@link Externalizable}.
*/
public DataStructureInfo() {
// No-op.
}
/**
* @param name Data structure name.
* @param type Data structure type.
* @param info Data structure information.
*/
DataStructureInfo(String name, DataStructureType type, Externalizable info) {
this.name = name;
this.type = type;
this.info = info;
}
/**
* @param dsInfo New data structure info.
* @param create Create flag.
* @return Exception if validation failed.
*/
@Nullable IgniteCheckedException validate(DataStructureInfo dsInfo, boolean create) {
if (type != dsInfo.type) {
return new IgniteCheckedException("Another data structure with the same name already created " +
"[name=" + name +
", newType=" + dsInfo.type.className() +
", existingType=" + type.className() + ']');
}
if (create) {
if (type == QUEUE || type == SET) {
CollectionInfo oldInfo = (CollectionInfo)info;
CollectionInfo newInfo = (CollectionInfo)dsInfo.info;
if (oldInfo.collocated != newInfo.collocated) {
return new IgniteCheckedException("Another collection with the same name but different " +
"configuration already created [name=" + name +
", newCollocated=" + newInfo.collocated +
", existingCollocated=" + newInfo.collocated + ']');
}
if (type == QUEUE) {
if (((QueueInfo)oldInfo).cap != ((QueueInfo)newInfo).cap) {
return new IgniteCheckedException("Another queue with the same name but different " +
"configuration already created [name=" + name +
", newCapacity=" + ((QueueInfo)newInfo).cap +
", existingCapacity=" + ((QueueInfo)oldInfo).cap + ']');
}
}
}
}
return null;
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
U.writeString(out, name);
U.writeEnum(out, type);
out.writeObject(info);
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = U.readString(in);
type = DataStructureType.fromOrdinal(in.readByte());
info = in.readObject();
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DataStructureInfo.class, this);
}
}
/**
*
*/
static class AddAtomicProcessor implements
EntryProcessor<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>, IgniteCheckedException>,
Externalizable {
/** */
private static final long serialVersionUID = 0L;
/** */
private DataStructureInfo info;
/**
* @param info Data structure information.
*/
AddAtomicProcessor(DataStructureInfo info) {
assert info != null;
this.info = info;
}
/**
* Required by {@link Externalizable}.
*/
public AddAtomicProcessor() {
// No-op.
}
/** {@inheritDoc} */
@Override public IgniteCheckedException process(
MutableEntry<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>> entry,
Object... args)
throws EntryProcessorException
{
Map<String, DataStructureInfo> map = entry.getValue();
if (map == null) {
map = new HashMap<>();
map.put(info.name, info);
entry.setValue(map);
return null;
}
DataStructureInfo oldInfo = map.get(info.name);
if (oldInfo == null) {
map = new HashMap<>(map);
map.put(info.name, info);
entry.setValue(map);
return null;
}
return oldInfo.validate(info, true);
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
info.writeExternal(out);
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
info = new DataStructureInfo();
info.readExternal(in);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(AddAtomicProcessor.class, this);
}
}
/**
*
*/
static class AddCollectionProcessor implements
EntryProcessor<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>,
T2<String, IgniteCheckedException>>, Externalizable {
/** */
private static final long serialVersionUID = 0L;
/** */
private DataStructureInfo info;
/**
* @param info Data structure information.
*/
AddCollectionProcessor(DataStructureInfo info) {
assert info != null;
assert info.info instanceof CollectionInfo;
this.info = info;
}
/**
* Required by {@link Externalizable}.
*/
public AddCollectionProcessor() {
// No-op.
}
/** {@inheritDoc} */
@Override public T2<String, IgniteCheckedException> process(
MutableEntry<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>> entry,
Object... args)
{
Map<String, DataStructureInfo> map = entry.getValue();
CollectionInfo colInfo = (CollectionInfo)info.info;
if (map == null) {
map = new HashMap<>();
map.put(info.name, info);
entry.setValue(map);
return new T2<>(colInfo.cacheName, null);
}
DataStructureInfo oldInfo = map.get(info.name);
if (oldInfo == null) {
map = new HashMap<>(map);
map.put(info.name, info);
entry.setValue(map);
return new T2<>(colInfo.cacheName, null);
}
return new T2<>(colInfo.cacheName, oldInfo.validate(info, true));
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
info.writeExternal(out);
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
info = new DataStructureInfo();
info.readExternal(in);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(AddCollectionProcessor.class, this);
}
}
/**
*
*/
static class AddDataCacheProcessor implements
EntryProcessor<CacheDataStructuresCacheKey, List<CacheCollectionInfo>, String>, Externalizable {
/** Cache name prefix. */
private static final String CACHE_NAME_PREFIX = "datastructures_";
/** */
private static final long serialVersionUID = 0L;
/** */
private CollectionConfiguration cfg;
/**
* @param cfg Data structure information.
*/
AddDataCacheProcessor(CollectionConfiguration cfg) {
this.cfg = cfg;
}
/**
* Required by {@link Externalizable}.
*/
public AddDataCacheProcessor() {
// No-op.
}
/** {@inheritDoc} */
@Override public String process(
MutableEntry<CacheDataStructuresCacheKey, List<CacheCollectionInfo>> entry,
Object... args)
{
List<CacheCollectionInfo> list = entry.getValue();
if (list == null) {
list = new ArrayList<>();
String newName = CACHE_NAME_PREFIX + 0;
list.add(new CacheCollectionInfo(newName, cfg));
entry.setValue(list);
return newName;
}
String oldName = findCompatibleConfiguration(cfg, list);
if (oldName != null)
return oldName;
String newName = CACHE_NAME_PREFIX + list.size();
List<CacheCollectionInfo> newList = new ArrayList<>(list);
newList.add(new CacheCollectionInfo(newName, cfg));
return newName;
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(cfg);
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
cfg = (CollectionConfiguration)in.readObject();
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(AddDataCacheProcessor.class, this);
}
}
/**
*
*/
static class RemoveDataStructureProcessor implements
EntryProcessor<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>,
T2<Boolean, IgniteCheckedException>>, Externalizable {
/** */
private static final long serialVersionUID = 0L;
/** */
private DataStructureInfo info;
/**
* @param info Data structure information.
*/
RemoveDataStructureProcessor(DataStructureInfo info) {
assert info != null;
this.info = info;
}
/**
* Required by {@link Externalizable}.
*/
public RemoveDataStructureProcessor() {
// No-op.
}
/** {@inheritDoc} */
@Override public T2<Boolean, IgniteCheckedException> process(
MutableEntry<CacheDataStructuresConfigurationKey, Map<String, DataStructureInfo>> entry,
Object... args)
{
Map<String, DataStructureInfo> map = entry.getValue();
if (map == null)
return new T2<>(false, null);
DataStructureInfo oldInfo = map.get(info.name);
if (oldInfo == null)
return new T2<>(false, null);
IgniteCheckedException err = oldInfo.validate(info, false);
if (err == null) {
map = new HashMap<>(map);
map.remove(info.name);
entry.setValue(map);
}
return new T2<>(true, err);
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
info.writeExternal(out);
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
info = new DataStructureInfo();
info.readExternal(in);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(RemoveDataStructureProcessor.class, this);
}
}
}