@Test public void idContainerReadWriteBySingleByte() throws IOException { SingleByteFileSystemAbstraction fileSystem = new SingleByteFileSystemAbstraction(); IdContainer idContainer = new IdContainer( fileSystem, file, 100, false ); idContainer.init(); idContainer.close( 100 ); idContainer = new IdContainer( fileSystem, file, 100, false ); idContainer.init(); assertEquals( 100, idContainer.getInitialHighId() ); fileSystem.close(); idContainer.close( 100 ); }
/** * Creates a new id generator. * * @param fileName The name of the id generator * @param throwIfFileExists if {@code true} will cause an {@link UnderlyingStorageException} to be thrown if * the file already exists. if {@code false} will truncate the file writing the header in it. */ public static void createGenerator( FileSystemAbstraction fs, File fileName, long highId, boolean throwIfFileExists ) { IdContainer.createEmptyIdFile( fs, fileName, highId, throwIfFileExists ); }
@Override public synchronized void delete() { idContainer.delete(); }
this.idContainer = new IdContainer( fs, file, grabSize, aggressiveReuse ); if ( this.idContainer.init() ) this.highId = idContainer.getInitialHighId();
/** * Frees the <CODE>id</CODE> making it a defragged id that will be * returned by next id before any new id (that hasn't been used yet) is * returned. * <p> * This method will throw an <CODE>IOException</CODE> if id is negative or * if id is greater than the highest returned id. However as stated in the * class documentation above the id isn't validated to see if it really is * free. * * @param id * The id to be made available again */ @Override public synchronized void freeId( long id ) { idContainer.assertStillOpen(); if ( IdValidator.isReservedId( id ) ) { return; } if ( id < 0 || id >= highId ) { throw new IllegalArgumentException( "Illegal id[" + id + "], highId is " + highId ); } idContainer.freeId( id ); }
@Test public void shouldReturnTrueOnInitIfAProperFileWasThere() { // Given // A properly created and closed id file IdContainer idContainer = new IdContainer( fs, file, 100, false ); idContainer.init(); idContainer.close( 100 ); // When // An IdContainer is created over it idContainer = new IdContainer( fs, file, 100, false ); // Then // init() should return true assertTrue( idContainer.init() ); idContainer.close( 100 ); }
@Test public void shouldTruncateTheFileIfOverwriting() throws Exception { // GIVEN IdContainer.createEmptyIdFile( fs, file, 30, false ); IdContainer idContainer = new IdContainer( fs, file, 5, false ); idContainer.init(); for ( int i = 0; i < 17; i++ ) { idContainer.freeId( i ); } idContainer.close( 30 ); assertThat( (int) fs.getFileSize( file ), greaterThan( IdContainer.HEADER_SIZE ) ); // WHEN IdContainer.createEmptyIdFile( fs, file, 30, false ); // THEN assertEquals( IdContainer.HEADER_SIZE, (int) fs.getFileSize( file ) ); assertEquals( 30, IdContainer.readHighId( fs, file ) ); idContainer = new IdContainer( fs, file, 5, false ); idContainer.init(); assertEquals( 30, idContainer.getInitialHighId() ); idContainer.close( 30 ); }
@Test public void shouldDeleteIfOpen() { // GIVEN createEmptyFile(); IdContainer idContainer = new IdContainer( fs, file, 100, false ); idContainer.init(); // WHEN idContainer.delete(); // THEN assertFalse( fs.fileExists( file ) ); idContainer.close( 0 ); }
@Test public void shouldForceStickyMark() throws Exception { // GIVEN createEmptyFile(); // WHEN opening the id generator, where the jvm crashes right after IdContainer idContainer = new IdContainer( fs, file, 100, false ); idContainer.init(); // THEN try { IdContainer.readHighId( fs, file ); fail( "Should have thrown, saying something with sticky generator" ); } catch ( InvalidIdGeneratorException e ) { // THEN Good } finally { idContainer.close( 0 ); } }
/** * Initializes the id generator and performs a simple validation. Returns true if the initialization restored * properly on disk state, false otherwise (such as creating an id file from scratch). * Will throw {@link InvalidIdGeneratorException} if the id file is found to be damaged or unclean. */ public boolean init() { boolean result = true; try { if ( !fs.fileExists( file ) ) { createEmptyIdFile( fs, file, 0, false ); result = false; } fileChannel = fs.open( file, OpenMode.READ_WRITE ); initialHighId = readAndValidateHeader(); markAsSticky(); this.freeIdKeeper = new FreeIdKeeper( new OffsetChannel( fileChannel, HEADER_SIZE ), grabSize, aggressiveReuse ); closed = false; } catch ( IOException e ) { throw new UnderlyingStorageException( "Unable to init id file " + file, e ); } return result; }
ReplicatedIdGenerator( FileSystemAbstraction fs, File file, IdType idType, LongSupplier highId, ReplicatedIdRangeAcquirer acquirer, LogProvider logProvider, int grabSize, boolean aggressiveReuse ) { this.idType = idType; this.highId = highId.getAsLong(); this.acquirer = acquirer; this.log = logProvider.getLog( getClass() ); idContainer = new IdContainer( fs, file, grabSize, aggressiveReuse ); idContainer.init(); }
/** * Closes the id generator flushing defragged ids in memory to file. The * file will be truncated to the minimal size required to hold all defragged * ids and it will be marked as clean (not sticky). * <p> * An invoke to the <CODE>nextId</CODE> or <CODE>freeId</CODE> after * this method has been invoked will result in an <CODE>IOException</CODE> * since the highest returned id has been set to a negative value. */ @Override public synchronized void close() { idContainer.close( highId ); }
@Override public synchronized long getDefragCount() { return idContainer.getFreeIdCount(); }
/** * Returns the next "free" id. If a defragged id exist it will be returned * else the next free id that hasn't been used yet is returned. If no id * exist the capacity is exceeded (all values <= max are taken) and a * {@link UnderlyingStorageException} will be thrown. * * @return The next free id * @throws UnderlyingStorageException * If the capacity is exceeded * @throws IllegalStateException if this id generator has been closed */ @Override public synchronized long nextId() { assertStillOpen(); long nextDefragId = idContainer.getReusableId(); if ( nextDefragId != IdContainer.NO_RESULT ) { return nextDefragId; } if ( IdValidator.isReservedId( highId ) ) { highId++; } IdValidator.assertValidId( idType, highId, max ); return highId++; }
@Override public void freeId( long id ) { idContainerLock.lock(); try { idContainer.freeId( id ); } finally { idContainerLock.unlock(); } }
/** * Read the high-id count from the given id-file. * * Note that this method should only be used when the file is not currently in use by an IdGenerator, since this * method does not take any in-memory state into account. * * @param fileSystem The file system to use for accessing the given file. * @param file The path to the id-file from which to read the high-id. * @return The high-id from the given file. * @throws IOException If anything goes wrong when accessing the file, for instance if the file does not exist. */ public static long readHighId( FileSystemAbstraction fileSystem, File file ) throws IOException { return IdContainer.readHighId( fileSystem, file ); }
@Test public void shouldDeleteIfClosed() { // GIVEN createEmptyFile(); IdContainer idContainer = new IdContainer( fs, file, 100, false ); idContainer.init(); idContainer.close( 0 ); // WHEN idContainer.delete(); // THEN assertFalse( fs.fileExists( file ) ); }
@Test public void shouldReturnFalseOnInitIfTheFileWasCreated() { // When // An IdContainer is created with no underlying file IdContainer idContainer = new IdContainer( fs, file, 100, false ); // Then // Init should return false assertFalse( idContainer.init() ); idContainer.close( 100 ); }
this.idContainer = new IdContainer( fs, file, grabSize, aggressiveReuse ); if ( this.idContainer.init() ) this.highId = idContainer.getInitialHighId();
/** * Initializes the id generator and performs a simple validation. Returns true if the initialization restored * properly on disk state, false otherwise (such as creating an id file from scratch). * Will throw {@link InvalidIdGeneratorException} if the id file is found to be damaged or unclean. */ public boolean init() { boolean result = true; try { if ( !fs.fileExists( file ) ) { createEmptyIdFile( fs, file, 0, false ); result = false; } fileChannel = fs.open( file, OpenMode.READ_WRITE ); initialHighId = readAndValidateHeader(); markAsSticky(); this.freeIdKeeper = new FreeIdKeeper( new OffsetChannel( fileChannel, HEADER_SIZE ), grabSize, aggressiveReuse ); closed = false; } catch ( IOException e ) { throw new UnderlyingStorageException( "Unable to init id file " + file, e ); } return result; }