blob: 81a65b1d2e5d32a3a1e8bbaa40884e6f48f708a0 [file] [log] [blame]
/*
* 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.geode.internal.cache;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.geode.cache.EntryDestroyedException;
import org.apache.geode.cache.Region;
import org.apache.geode.internal.cache.LocalRegion.IteratorType;
import org.apache.geode.internal.cache.entries.AbstractRegionEntry;
import org.apache.geode.logging.internal.log4j.api.LogWithToString;
/** Set view of entries */
public class EntriesSet extends AbstractSet implements LogWithToString {
final LocalRegion topRegion;
final boolean recursive;
final IteratorType iterType;
protected final TXStateInterface myTX;
final boolean allowTombstones;
protected final InternalDataView view;
final boolean rememberReads;
private boolean keepSerialized = false;
protected boolean ignoreCopyOnReadForQuery = false;
EntriesSet(LocalRegion region, boolean recursive, IteratorType viewType,
boolean allowTombstones) {
this.topRegion = region;
this.recursive = recursive;
this.iterType = viewType;
this.myTX = region.getTXState();
this.view = this.myTX == null ? region.getSharedDataView() : this.myTX;
this.rememberReads = true;
this.allowTombstones = allowTombstones;
}
protected void checkTX() {
if (this.myTX != null) {
if (!myTX.isInProgress()) {
throw new IllegalStateException(
String.format(
"Region collection was created with transaction %s that is no longer active.",
myTX.getTransactionId()));
}
} else {
if (this.topRegion.isTX()) {
throw new IllegalStateException(
String.format(
"The Region collection is not transactional but is being used in a transaction %s.",
this.topRegion.getTXState().getTransactionId()));
}
}
}
@Override
public Iterator<Object> iterator() {
checkTX();
return new EntriesIterator();
}
private class EntriesIterator implements Iterator<Object> {
final List<LocalRegion> regions;
final int numSubRegions;
int regionsIndex;
LocalRegion currRgn;
// keep track of look-ahead on hasNext() call, used to filter out null
// values
Object nextElem;
Iterator<?> currItr;
Collection<?> additionalKeysFromView;
/** reusable KeyInfo */
protected final KeyInfo keyInfo = new KeyInfo(null, null, null);
@SuppressWarnings("unchecked")
protected EntriesIterator() {
if (recursive) {
// FIFO queue of regions
this.regions = new ArrayList<LocalRegion>(topRegion.subregions(true));
this.numSubRegions = this.regions.size();
} else {
this.regions = null;
this.numSubRegions = 0;
}
createIterator(topRegion);
this.nextElem = moveNext();
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"This iterator does not support modification");
}
@Override
public boolean hasNext() {
return (this.nextElem != null);
}
@Override
public Object next() {
final Object result = this.nextElem;
if (result != null) {
this.nextElem = moveNext();
return result;
}
throw new NoSuchElementException();
}
private Object moveNext() {
// keep looping until:
// we find an element and return it
// OR we run out of elements and return null
for (;;) {
if (this.currItr.hasNext()) {
final Object currKey = this.currItr.next();
final Object result;
this.keyInfo.setKey(currKey);
if (this.additionalKeysFromView != null) {
if (currKey instanceof AbstractRegionEntry) {
this.additionalKeysFromView.remove(((AbstractRegionEntry) currKey).getKey());
} else {
this.additionalKeysFromView.remove(currKey);
}
}
if (iterType == IteratorType.KEYS) {
result =
view.getKeyForIterator(this.keyInfo, this.currRgn, rememberReads, allowTombstones);
if (result != null) {
return result;
}
} else if (iterType == IteratorType.ENTRIES) {
result = view.getEntryForIterator(this.keyInfo, this.currRgn, rememberReads,
allowTombstones);
if (result != null) {
return result;
}
} else {
Region.Entry re = (Region.Entry) view.getEntryForIterator(this.keyInfo, currRgn,
rememberReads, allowTombstones);
if (re != null) {
try {
if (keepSerialized) {
result = ((NonTXEntry) re).getRawValue(); // OFFHEAP: need to either copy into a
// cd or figure out when result will be
// released.
} else if (ignoreCopyOnReadForQuery) {
result = ((NonTXEntry) re).getValue(true);
} else {
if ((re instanceof TXEntry)) {
result = ((TXEntry) re).getValue(allowTombstones);
} else {
result = re.getValue();
}
}
if (result != null && !Token.isInvalidOrRemoved(result)) { // fix for bug 34583
return result;
}
if (result == Token.TOMBSTONE && allowTombstones) {
return result;
}
} catch (EntryDestroyedException ede) {
// Fix for bug 43526, caused by fix to 43064
// Entry is destroyed, continue to the next element.
}
}
// key disappeared or is invalid, go on to next
}
} else if (this.additionalKeysFromView != null) {
this.currItr = this.additionalKeysFromView.iterator();
this.additionalKeysFromView = null;
} else if (this.regionsIndex < this.numSubRegions) {
// advance to next region
createIterator(this.regions.get(this.regionsIndex));
++this.regionsIndex;
} else {
return null;
}
}
}
private void createIterator(final LocalRegion rgn) {
// TX iterates over KEYS.
// NonTX iterates over RegionEntry instances
this.currRgn = rgn;
this.currItr = view.getRegionKeysForIteration(rgn).iterator();
this.additionalKeysFromView = view.getAdditionalKeysForIterator(rgn);
}
}
@Override
public int size() {
checkTX();
if (this.iterType == IteratorType.VALUES) {
// if this is a values-view, then we have to filter out nulls to
// determine the correct size
int s = 0;
for (Iterator<Object> itr = new EntriesIterator(); itr.hasNext(); itr.next()) {
s++;
}
return s;
} else if (this.recursive) {
return this.topRegion.allEntriesSize();
} else {
return view.entryCount(this.topRegion);
}
}
@Override
public Object[] toArray() {
return toArray((Object[]) null);
}
@Override
public Object[] toArray(final Object[] array) {
checkTX();
final ArrayList<Object> temp = new ArrayList<Object>(this.size());
final Iterator<Object> iter = new EntriesIterator();
while (iter.hasNext()) {
temp.add(iter.next());
}
if (array == null) {
return temp.toArray();
} else {
return temp.toArray(array);
}
}
public void setKeepSerialized(boolean keepSerialized) {
this.keepSerialized = keepSerialized;
}
public boolean isKeepSerialized() {
return this.keepSerialized;
}
public void setIgnoreCopyOnReadForQuery(boolean ignoreCopyOnReadForQuery) {
this.ignoreCopyOnReadForQuery = ignoreCopyOnReadForQuery;
}
public boolean isIgnoreCopyOnReadForQuery() {
return this.ignoreCopyOnReadForQuery;
}
}