/*
* 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.cache.distributed.dht;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheEntryPredicate;
import org.apache.ignite.internal.processors.cache.CacheObject;
import org.apache.ignite.internal.processors.cache.GridCacheConcurrentMap;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.jetbrains.annotations.Nullable;
/**
* An implementation of GridCacheConcurrentMap that will delegate all method calls to corresponding local partition.
*/
public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap {
/** Context. */
private final GridCacheContext ctx;
/**
* Constructor.
* @param ctx Context.
*/
public GridCachePartitionedConcurrentMap(GridCacheContext ctx) {
this.ctx = ctx;
}
/**
* @param key Key.
* @param topVer Topology version.
* @param create Create flag.
* @return Local partition.
*/
@Nullable private GridDhtLocalPartition localPartition(
KeyCacheObject key,
AffinityTopologyVersion topVer,
boolean create
) {
int p = key.partition();
if (p == -1)
p = ctx.affinity().partition(key);
return ctx.topology().localPartition(p, topVer, create);
}
/** {@inheritDoc} */
@Nullable @Override public GridCacheMapEntry getEntry(KeyCacheObject key) {
GridDhtLocalPartition part = localPartition(key, AffinityTopologyVersion.NONE, false);
if (part == null)
return null;
return part.getEntry(key);
}
/** {@inheritDoc} */
@Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(AffinityTopologyVersion topVer,
KeyCacheObject key,
boolean create,
boolean touch) {
while (true) {
GridDhtLocalPartition part = localPartition(key, topVer, create);
if (part == null)
return null;
GridCacheMapEntry res = part.putEntryIfObsoleteOrAbsent(topVer, key, create, touch);
if (res != null || !create)
return res;
// Otherwise partition was concurrently evicted and should be re-created on next iteration.
}
}
/** {@inheritDoc} */
@Override public int size() {
int size = 0;
for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions())
size += part.size();
return size;
}
/** {@inheritDoc} */
@Override public int publicSize() {
int size = 0;
for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions())
size += part.publicSize();
return size;
}
/** {@inheritDoc} */
@Override public void incrementPublicSize(GridCacheEntryEx e) {
localPartition(e.key(), AffinityTopologyVersion.NONE, true).incrementPublicSize(e);
}
/** {@inheritDoc} */
@Override public void decrementPublicSize(GridCacheEntryEx e) {
localPartition(e.key(), AffinityTopologyVersion.NONE, true).decrementPublicSize(e);
}
/** {@inheritDoc} */
@Override public boolean removeEntry(GridCacheEntryEx entry) {
GridDhtLocalPartition part = localPartition(entry.key(), AffinityTopologyVersion.NONE, false);
if (part == null)
return false;
return part.removeEntry(entry);
}
/** {@inheritDoc} */
@Override public Set<KeyCacheObject> keySet(final CacheEntryPredicate... filter) {
return new PartitionedSet<KeyCacheObject>() {
@Override protected Set<KeyCacheObject> set(GridDhtLocalPartition part) {
return part.keySet(filter);
}
};
}
/** {@inheritDoc} */
@Override public Iterable<GridCacheMapEntry> entries(final CacheEntryPredicate... filter) {
return new Iterable<GridCacheMapEntry>() {
@Override public Iterator<GridCacheMapEntry> iterator() {
return new PartitionedIterator<GridCacheMapEntry>() {
@Override protected Iterator<GridCacheMapEntry> iterator(GridDhtLocalPartition part) {
return part.entries(filter).iterator();
}
};
}
};
}
/** {@inheritDoc} */
@Override public Iterable<GridCacheMapEntry> allEntries(final CacheEntryPredicate... filter) {
return new Iterable<GridCacheMapEntry>() {
@Override public Iterator<GridCacheMapEntry> iterator() {
return new PartitionedIterator<GridCacheMapEntry>() {
@Override protected Iterator<GridCacheMapEntry> iterator(GridDhtLocalPartition part) {
return part.allEntries(filter).iterator();
}
};
}
};
}
/** {@inheritDoc} */
@Override public Set<GridCacheMapEntry> entrySet(final CacheEntryPredicate... filter) {
return new PartitionedSet<GridCacheMapEntry>() {
@Override protected Set<GridCacheMapEntry> set(GridDhtLocalPartition part) {
return part.entrySet(filter);
}
};
}
/**
* Combined iterator over current local partitions.
*/
private abstract class PartitionedIterator<T> implements Iterator<T> {
/** Partitions iterator. */
private Iterator<GridDhtLocalPartition> partsIter = ctx.topology().currentLocalPartitions().iterator();
/** Current partition iterator. */
private Iterator<T> currIter = partsIter.hasNext() ? iterator(partsIter.next()) :
Collections.<T>emptyIterator();
/**
* @param part Partition.
* @return Iterator over entries of given partition.
*/
protected abstract Iterator<T> iterator(GridDhtLocalPartition part);
/** {@inheritDoc} */
@Override public boolean hasNext() {
if (currIter.hasNext())
return true;
while (partsIter.hasNext()) {
currIter = iterator(partsIter.next());
if (currIter.hasNext())
return true;
}
return false;
}
/** {@inheritDoc} */
@Override public T next() {
if (hasNext())
return currIter.next();
else
throw new NoSuchElementException();
}
/** {@inheritDoc} */
@Override public void remove() {
throw new UnsupportedOperationException("remove");
}
}
/**
* Read-only partitioned set.
*/
private abstract class PartitionedSet<T> extends AbstractSet<T> {
/**
* @param part Partition.
* @return Set of entries from given partition.
*/
protected abstract Set<T> set(GridDhtLocalPartition part);
/** {@inheritDoc} */
@Override public Iterator<T> iterator() {
return new PartitionedIterator<T>() {
@Override protected Iterator<T> iterator(GridDhtLocalPartition part) {
return set(part).iterator();
}
};
}
/** {@inheritDoc} */
@Override public int size() {
int size = 0;
for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions())
size += set(part).size();
return size;
}
/** {@inheritDoc} */
@Override public boolean contains(Object o) {
for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions()) {
if (set(part).contains(o))
return true;
}
return false;
}
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridCachePartitionedConcurrentMap.class, this);
}
}