package com.bigdata.bop.engine; import java.rmi.RemoteException; import java.util.UUID; import com.bigdata.bop.BOp; import com.bigdata.bop.IBindingSet; import com.bigdata.bop.fed.FederatedRunningQuery; import com.bigdata.relation.accesspath.ThickCloseableIterator; import cutthecrap.utils.striterators.ICloseableIterator; /** * A chunk of intermediate results which are ready to be consumed by some * {@link BOp} in a specific query. * <p> * Note: This class is only used in query evaluation for the standalone * database. */ public class LocalChunkMessage implements IChunkMessage<IBindingSet> { /** * */ private static final long serialVersionUID = 1L; /** The query controller. */ private final IQueryClient queryController; /** The service {@link UUID} for the {@link IQueryClient query controller}. */ private final UUID queryControllerId; /** * The query identifier. */ private final UUID queryId; /** * The target {@link BOp}. */ private final int bopId; /** * The index partition which is being targeted for that {@link BOp}. */ private final int partitionId; /** * The #of solutions in the chunk. */ private final int solutionCount; /** * Chunks of binding sets. */ private IBindingSet[][] bindingSetChunks; @Override public IQueryClient getQueryController() { return queryController; } @Override public UUID getQueryControllerId() { return queryControllerId; } @Override public UUID getQueryId() { return queryId; } @Override public int getBOpId() { return bopId; } @Override public int getPartitionId() { return partitionId; } @Override public boolean isLastInvocation() { return false; // Never. } @Override public boolean isMaterialized() { return true; } @Override public int getSolutionCount() { return solutionCount; } public LocalChunkMessage(final IQueryClient queryController, final UUID queryId, final int bopId, final int partitionId, final IBindingSet bset) { this(queryController, queryId, bopId, partitionId, new IBindingSet[][] { new IBindingSet[] { bset } }); } public LocalChunkMessage(final IQueryClient queryController, final UUID queryId, final int bopId, final int partitionId, final IBindingSet[] bsets) { this(queryController, queryId, bopId, partitionId, new IBindingSet[][] { bsets }); } /** * * TODO There are a few unit tests which rely on the ability to push * multiple chunks through the query engine, otherwise this could be changed * to an IBindingSet[] by flattening the caller's IBindingSet[][] when * given. Doing this could simplify some other things in the API since any * chunk would just be an IBindingSet[]. * <p> * See {@link IChunkAccessor} which has some notes concerning this and * related matters. It might be useful to preserve the ability to have a * message with multiple chunks, ie., to deal with data on different driect * buffers. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/475"> * Optimize serialization for query messages on cluster </a> */ public LocalChunkMessage(final IQueryClient queryController, final UUID queryId, final int bopId, final int partitionId, final IBindingSet[][] bindingSetChunks) { if (queryController == null) throw new IllegalArgumentException(); if (queryId == null) throw new IllegalArgumentException(); if (bindingSetChunks == null) throw new IllegalArgumentException(); this.queryController = queryController; try { this.queryControllerId = queryController.getServiceUUID(); } catch (RemoteException e) { throw new RuntimeException(e); } this.queryId = queryId; this.bopId = bopId; this.partitionId = partitionId; this.solutionCount = solutionCount(bindingSetChunks); this.bindingSetChunks = bindingSetChunks; } @Override public String toString() { return getClass().getName() + "{queryId=" + queryId + ",bopId=" + bopId + ",partitionId=" + partitionId + ", solutionCount=" + solutionCount + "}"; } @Override public void materialize(FederatedRunningQuery runningQuery) { // NOP } @Override public void release() { final ChunkAccessor tmp = chunkAccessor; if (tmp != null) { // Close the iterator. tmp.close(); } // Clear the array contents. for (int i = 0; i < bindingSetChunks.length; i++) { bindingSetChunks[i] = null; } } @Override public IChunkAccessor<IBindingSet> getChunkAccessor() { if (chunkAccessor == null) { chunkAccessor = new ChunkAccessor(); } return chunkAccessor; } private volatile transient ChunkAccessor chunkAccessor = null; private class ChunkAccessor implements IChunkAccessor<IBindingSet> { private final ICloseableIterator<IBindingSet[]> source; public ChunkAccessor() { source = newBindingSetIterator(bindingSetChunks); } @Override public ICloseableIterator<IBindingSet[]> iterator() { return source; } public void close() { source.close(); } } /** * Return an {@link ICloseableIterator} that will read a single, chunk * containing all of the specified {@link IBindingSet}s. * * @param bindingSetChunks * the chunks of binding sets. */ private static ThickCloseableIterator<IBindingSet[]> newBindingSetIterator( final IBindingSet[][] bindingSetChunks) { return new ThickCloseableIterator<IBindingSet[]>(bindingSetChunks); } private static int solutionCount(final IBindingSet[][] bindingSetChunks) { int solutionCount = 0; for (int i = 0; i < bindingSetChunks.length; i++) { solutionCount += bindingSetChunks[i].length; } return solutionCount; } }