/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Aug 27, 2008
*/
package com.bigdata.service.proxy;
import java.io.IOException;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.apache.log4j.Logger;
import com.bigdata.striterator.IChunkedOrderedIterator;
import com.bigdata.striterator.IKeyOrder;
/**
* Wraps an {@link IRemoteChunkedIterator} so that it looks like an
* {@link IChunkedOrderedIterator} again.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class WrappedRemoteChunkedIterator<E> implements
IChunkedOrderedIterator<E> {
protected static final Logger log = Logger
.getLogger(WrappedRemoteChunkedIterator.class);
/** The source - the reference is cleared when the iterator is closed. */
private IRemoteChunkedIterator<E> src;
/**
* The #of {@link IRemoteChunk}s read from the source so far.
*/
private long nchunks = 0L;
/**
* If the source will not return any _more_ chunks. There will still be data
* in the current chunk if {@link #a} is non-null and {@link #index} is less
* than the #of elements in {@link #a}.
*/
private boolean exhausted;
/** The elements in the current chunk. */
private E[] a;
/** The index into the current chunk. */
private int index;
/** The {@link IKeyOrder} for the source iterator (may be null). */
private IKeyOrder<E> keyOrder;
/**
*
* @param src
* The source.
*/
public WrappedRemoteChunkedIterator(final IRemoteChunkedIterator<E> src) {
if (src == null)
throw new IllegalArgumentException();
// save reference.
this.src = src;
try {
// pre-fetch the first chunk.
readChunkFromSource();
} catch (IOException ex) {
/*
* Attempt to close the source if there was a problem reading the
* first chunk. However, only log a message if the src.close() fails
* since we want the [ex] from above to get thrown out of this ctor.
*/
try {
src.close();
} catch(IOException ex2) {
log.warn("Could not close source", ex2);
}
// clear the reference.
this.src = null;
// wrap and throw the original exception.
throw new RuntimeException(ex);
}
}
public IKeyOrder<E> getKeyOrder() {
return keyOrder;
}
/**
* Reads a chunk from the source.
*
* @throws IOException
* If there is an RMI problem.
*/
private void readChunkFromSource() throws IOException {
// read a chunk from the source.
final IRemoteChunk<E> chunk = src.nextChunk();
if (nchunks == 0) {
// save once.
keyOrder = chunk.getKeyOrder();
}
exhausted = chunk.isExhausted();
a = chunk.getChunk();
index = 0;
if (log.isInfoEnabled()) {
log.info("nchunks=" + nchunks + ", sourceExhausted=" + exhausted
+ ", elementsInChunk=" + a.length);
}
nchunks++;
}
/**
* Variant traps and masquerades the {@link IOException}.
*/
private void readChunkFromSource2() {
try {
readChunkFromSource();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public boolean hasNext() {
if (exhausted && (a == null || index >= a.length)) {
/*
* The source is exhausted and either there are no elements in a[]
* or we have exhausted all of the elements in a[].
*/
return false;
}
/*
* There is something remaining.
*/
if (index >= a.length) {
// pre-fetch the next chunk if we are done with the last one.
readChunkFromSource2();
}
return true;
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
final E e = a[index++];
if (log.isDebugEnabled()) {
log.debug("e=" + e + ", index=" + index + ", chunkSize=" + a.length);
}
return e;
}
@SuppressWarnings("unchecked")
public E[] nextChunk() {
if (!hasNext()) {
throw new NoSuchElementException();
}
final E[] ret;
if (index == 0) {
/*
* Nothing has been returned to the caller by next() so we can just
* return the backing array in this case.
*/
ret = a;
if (log.isDebugEnabled()) {
log.debug("returning entire chunk: chunkSize=" + a.length);
}
} else {
/*
* Create and return a new E[] containing only the elements
* remaining in the current chunk.
*/
final int remaining = a.length - index;
/*
* Dynamically instantiation an array of the same component type
* as the objects that we are visiting.
*/
ret = (E[]) java.lang.reflect.Array.newInstance(a.getClass()
.getComponentType(), remaining);
System.arraycopy(a, index, ret, 0, remaining);
if (log.isDebugEnabled()) {
log.debug("returning remainder of chunk: remaining=" + remaining);
}
}
// indicate that all elements in the current have been consumed.
index = a.length;
return ret;
}
public E[] nextChunk(IKeyOrder<E> keyOrder) {
if (keyOrder == null)
throw new IllegalArgumentException();
final E[] chunk = nextChunk();
if (!keyOrder.equals(getKeyOrder())) {
// sort into the required order.
Arrays.sort(chunk, 0, chunk.length, keyOrder.getComparator());
}
return chunk;
}
/**
* @throws UnsupportedOperationException
* always.
*/
public void remove() {
throw new UnsupportedOperationException();
}
public void close() {
if (src != null) {
if(log.isInfoEnabled()) {
log.info("Closing remote iterator");
}
try {
src.close();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// clear now that it's been closed.
src = null;
}
}
}
}