| /* |
| * 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; |
| } |
| |
| } |