// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.core.store;
import org.openstreetmap.osmosis.core.lifecycle.Completable;
import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
/**
* Adds indexed chunking capabilities to a basic object store allowing groups of
* objects to be written and retrieved later by their chunk index. The number of
* objects and the size of the index is limited only by disk space.
* <p>
* This store is only suitable for single-threaded use because it does not
* provide per-thread readers.
*
* @param <T>
* The class type to be stored.
* @author Brett Henderson
*/
public class ChunkedObjectStore<T extends Storeable> implements Completable {
/**
* Stores all the objects written to this store.
*/
private SegmentedObjectStore<T> objectStore;
/**
* Maintains both the file positions of each chunk and the number of objects
* within each chunk. The file position is written when a new chunk is
* started, and the object count is written when a chunk is completed.
*/
private IndexStore<Long, LongLongIndexElement> indexStore;
private IndexStoreReader<Long, LongLongIndexElement> indexStoreReader;
private long chunkCount;
private boolean chunkInProgress;
private long newChunkFilePosition;
private long chunkObjectCount;
/**
* Creates a new instance.
*
* @param serializationFactory
* The factory defining the object serialisation implementation.
* @param storageFilePrefix
* The prefix of the storage file name.
* @param indexFilePrefix
* The prefix of the index file name.
* @param useCompression
* If true, the storage file will be compressed.
*/
public ChunkedObjectStore(
ObjectSerializationFactory serializationFactory,
String storageFilePrefix,
String indexFilePrefix,
boolean useCompression) {
objectStore = new SegmentedObjectStore<T>(serializationFactory, storageFilePrefix, useCompression);
indexStore = new IndexStore<Long, LongLongIndexElement>(
LongLongIndexElement.class,
new ComparableComparator<Long>(),
indexFilePrefix
);
chunkCount = 0;
chunkInProgress = false;
newChunkFilePosition = 0;
chunkObjectCount = 0;
}
/**
* Adds the specified object to the store.
*
* @param data
* The object to be added.
*/
public void add(T data) {
objectStore.add(data);
chunkObjectCount++;
if (!chunkInProgress) {
// Write the file index of the new chunk.
indexStore.write(new LongLongIndexElement((chunkCount * 2), newChunkFilePosition));
chunkInProgress = true;
}
}
/**
* Stops the current writing operation and begins a new one saving the
* current position against a new interval index.
*/
public void closeChunk() {
// We can only close a chunk if one is in progress.
if (chunkInProgress) {
// Create an interval in the underlying object store and note the
// current position.
newChunkFilePosition = objectStore.closeChunk();
// Flag that no chunk is in progress so that the next add will store
// the start file index.
chunkInProgress = false;
// Write then reset the object count of the current chunk.
indexStore.write(new LongLongIndexElement((chunkCount * 2) + 1, chunkObjectCount));
chunkObjectCount = 0;
// Increment the chunk count.
chunkCount++;
}
}
/**
* Returns the number of chunks managed by this store. This count will
* include the in progress chunk if one exists.
*
* @return The number of chunks.
*/
public long getChunkCount() {
// If a chunk is in progress, the chunk count won't have been updated yet.
if (chunkInProgress) {
return chunkCount + 1;
} else {
return chunkCount;
}
}
/**
* Provides access to the contents of this store.
*
* @param chunk
* The chunk to read objects from.
* @return An iterator providing access to contents of the store.
*/
public ReleasableIterator<T> iterate(long chunk) {
complete();
if (indexStoreReader == null) {
indexStoreReader = indexStore.createReader();
}
// Retrieve the file position and number of objects for the specified
// chunk and iterate.
return objectStore.iterate(
indexStoreReader.get(chunk * 2).getValue(),
indexStoreReader.get(chunk * 2 + 1).getValue()
);
}
/**
* {@inheritDoc}
*/
@Override
public void complete() {
// Any outstanding chunks must be closed before we can complete.
closeChunk();
indexStore.complete();
}
/**
* {@inheritDoc}
*/
public void close() {
objectStore.close();
if (indexStoreReader != null) {
indexStoreReader.close();
indexStoreReader = null;
}
indexStore.close();
}
}