org.modeshape.jcr.query.engine.process.CountableSequence Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed 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.modeshape.jcr.query.engine.process;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;
import org.mapdb.Serializer;
import org.modeshape.jcr.cache.CachedNode;
import org.modeshape.jcr.cache.CachedNodeSupplier;
import org.modeshape.jcr.query.BufferManager;
import org.modeshape.jcr.query.BufferManager.QueueBuffer;
import org.modeshape.jcr.query.NodeSequence;
import org.modeshape.jcr.query.engine.process.BufferedRows.BufferedRow;
import org.modeshape.jcr.query.engine.process.BufferedRows.BufferedRowFactory;
/**
* A sequence that will return an accurate size for a given NodeSequence by buffering and counting the nodes and then accessing
* the buffered sequence.
*
* @see PartialMemoryCountableSequence
* @author Randall Hauch ([email protected])
*/
public class CountableSequence extends NodeSequence {
private final NodeSequence original;
private final BufferedRowFactory extends BufferedRow> rowFactory;
private final QueueBuffer buffer;
protected final AtomicLong remainingRowCount = new AtomicLong();
protected final String workspaceName;
protected final int width;
private Iterator bufferedRows;
private final AtomicLong batchSize = new AtomicLong();
private long totalSize = -1L;
@SuppressWarnings( "unchecked" )
public CountableSequence( String workspaceName,
NodeSequence original,
BufferManager bufferMgr,
CachedNodeSupplier nodeCache,
boolean useHeap ) {
this.original = original;
this.workspaceName = workspaceName;
this.width = original.width();
assert !original.isEmpty();
assert original.getRowCount() == -1;
assert original.width() != 0;
// Create the row factory ...
this.rowFactory = BufferedRows.serializer(nodeCache, width);
// Create the buffer into which we'll place the rows with null keys ...
Serializer rowSerializer = (Serializer)BufferedRows.serializer(nodeCache, width);
buffer = bufferMgr.createQueueBuffer(rowSerializer).useHeap(useHeap).make();
}
@Override
public int width() {
return width;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public long getRowCount() {
initialize();
assert totalSize >= 0L;
return totalSize;
}
@Override
public final Batch nextBatch() {
initialize();
return doNextBatch();
}
protected Batch doNextBatch() {
return batchFrom(bufferedRows, batchSize.get());
}
@Override
public void close() {
buffer.close();
}
public final void initialize() {
if (bufferedRows == null) {
doInitialize();
}
}
protected void doInitialize() {
// Load all the rows into the buffer ...
totalSize = loadAll(original, buffer, batchSize);
remainingRowCount.set(totalSize);
original.close();
bufferedRows = buffer.iterator();
}
/**
* Load all of the rows from the supplied sequence into the buffer.
*
* @param sequence the node sequence; may not be null
* @param buffer the buffer into which all rows should be loaded; may not be null
* @param batchSize the atomic that should be set with the size of the first batch
* @return the total number of rows
*/
protected long loadAll( NodeSequence sequence,
QueueBuffer buffer,
AtomicLong batchSize ) {
return loadAll(sequence, buffer, batchSize, null, 0);
}
/**
* Load all of the rows from the supplied sequence into the buffer.
*
* @param sequence the node sequence; may not be null
* @param buffer the buffer into which all rows should be loaded; may not be null
* @param batchSize the atomic that should be set with the size of the first batch
* @param inMemoryBatches the queue into which batches that are kept in-memory; may be null, in which case all batches are
* placed into the buffer
* @param numRowsInMemory the approximate number of rows that should be kept in-memory; may be non-positive if all batches are
* to be placed into the buffer
* @return the total number of rows
*/
protected long loadAll( NodeSequence sequence,
QueueBuffer buffer,
AtomicLong batchSize,
Queue inMemoryBatches,
int numRowsInMemory ) {
// Put all of the batches from the sequence into the buffer
Batch batch = sequence.nextBatch();
boolean loadIntoMemory = numRowsInMemory > 0 && inMemoryBatches != null;
long numInMemory = 0L;
while (batch != null && batchSize.get() == 0L) {
if (loadIntoMemory) {
Batch copy = NodeSequence.batchWithCount(batch);
inMemoryBatches.add(copy);
batchSize.set(copy.rowCount());
numInMemory += copy.rowCount();
numRowsInMemory -= batchSize.get();
if (numRowsInMemory <= 0) loadIntoMemory = false;
} else {
while (batch.hasNext()) {
batch.nextRow();
buffer.append(createRow(batch));
batchSize.incrementAndGet();
}
}
batch = sequence.nextBatch();
}
while (batch != null) {
if (loadIntoMemory) {
Batch copy = NodeSequence.batchWithCount(batch);
inMemoryBatches.add(copy);
numInMemory += copy.rowCount();
numRowsInMemory -= copy.rowCount();
if (numRowsInMemory <= 0) loadIntoMemory = false;
} else {
while (batch.hasNext()) {
batch.nextRow();
buffer.append(createRow(batch));
}
}
batch = sequence.nextBatch();
}
return buffer.size() + numInMemory;
}
protected BufferedRow createRow( Batch currentRow ) {
return rowFactory.createRow(currentRow);
}
protected Batch batchFrom( final Iterator rows,
final long maxBatchSize ) {
if (remainingRowCount.get() <= 0 || !rows.hasNext()) return null;
if (maxBatchSize == 0) return NodeSequence.emptyBatch(workspaceName, this.width);
final long rowsInBatch = Math.min(maxBatchSize, remainingRowCount.get());
return new Batch() {
private BufferedRow current;
@Override
public int width() {
return width;
}
@Override
public long rowCount() {
return rowsInBatch;
}
@Override
public String getWorkspaceName() {
return workspaceName;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean hasNext() {
return remainingRowCount.get() > 0 && rows.hasNext();
}
@Override
public void nextRow() {
current = rows.next();
remainingRowCount.decrementAndGet();
}
@Override
public CachedNode getNode() {
return current.getNode();
}
@Override
public CachedNode getNode( int index ) {
return current.getNode(index);
}
@Override
public float getScore() {
return current.getScore();
}
@Override
public float getScore( int index ) {
return current.getScore(index);
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy