// 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(); } }