/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id.enhanced;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.junit.Test;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertEquals;
/**
* {@inheritDoc}
*
* @author Steve Ebersole
*/
@SuppressWarnings({ "deprecation" })
public class OptimizerUnitTest extends BaseUnitTestCase {
@Test
public void testBasicNoOptimizerUsage() {
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1 );
Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
for ( int i = 1; i < 11; i++ ) {
final Long next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 10, sequence.getTimesCalled() );
assertEquals( 10, sequence.getCurrentValue() );
// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
sequence = new SourceMock( 0 );
optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
for ( int i = 1; i < 11; i++ ) {
final Long next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 11, sequence.getTimesCalled() ); // an extra time to get to 1 initially
assertEquals( 10, sequence.getCurrentValue() );
}
@Test
public void testBasicHiLoOptimizerUsage() {
int increment = 10;
Long next;
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1 );
Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
for ( int i = 1; i <= increment; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 1, sequence.getTimesCalled() ); // once to initialze state
assertEquals( 1, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 2, sequence.getCurrentValue() );
// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
sequence = new SourceMock( 0 );
optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
for ( int i = 1; i <= increment; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // here have have an extra call to get to 1 initially
assertEquals( 1, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 2, sequence.getCurrentValue() );
}
@Test
public void testBasicPooledOptimizerUsage() {
Long next;
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1, 10 );
Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 10 );
for ( int i = 1; i < 11; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // twice to initialize state
assertEquals( 11, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 21, sequence.getCurrentValue() );
}
@Test
public void testSubsequentPooledOptimizerUsage() {
// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
// cheat by telling the sequence to start with 1000
final SourceMock sequence = new SourceMock( 1001, 3, 5 );
// but tell the optimizer the start-with is 1
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
assertEquals( 5, sequence.getTimesCalled() );
assertEquals( 1001, sequence.getCurrentValue() );
Long next = (Long) optimizer.generate( sequence );
assertEquals( 1001, next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+1), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+2), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
// force a "clock over"
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+3), next.intValue() );
assertEquals( (5+2), sequence.getTimesCalled() );
assertEquals( (1001+6), sequence.getCurrentValue() );
}
@Test
public void testBasicPooledLoOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( 2, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( 3, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
// // force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( (1+3), sequence.getCurrentValue() );
}
@Test
public void testSubsequentPooledLoOptimizerUsage() {
// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
// cheat by telling the sequence to start with 1000
final SourceMock sequence = new SourceMock( 1001, 3, 5 );
// but tell the optimizer the start-with is 1
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
assertEquals( 5, sequence.getTimesCalled() );
assertEquals( 1001, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+1), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+2), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
// // force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+3), next.intValue() );
assertEquals( (5+2), sequence.getTimesCalled() );
assertEquals( (1001+6), sequence.getCurrentValue() );
}
@Test
public void testRecoveredPooledOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 4, sequence.getCurrentValue() );
// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
final Optimizer optimizer2 = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
next = ( Long ) optimizer2.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 7, sequence.getCurrentValue() );
}
@Test
public void testRecoveredPooledLoOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3, 1 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
final Optimizer optimizer2 = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3, 1 );
next = ( Long ) optimizer2.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 4, sequence.getCurrentValue() );
}
private static class SourceMock implements AccessCallback {
private IdentifierGeneratorHelper.BasicHolder value = new IdentifierGeneratorHelper.BasicHolder( Long.class );
private long initialValue;
private int increment;
private int timesCalled = 0;
public SourceMock(long initialValue) {
this( initialValue, 1 );
}
public SourceMock(long initialValue, int increment) {
this( initialValue, increment, 0 );
}
public SourceMock(long initialValue, int increment, int timesCalled) {
this.increment = increment;
this.timesCalled = timesCalled;
if ( timesCalled != 0 ) {
this.value.initialize( initialValue );
this.initialValue = 1;
}
else {
this.value.initialize( -1 );
this.initialValue = initialValue;
}
}
public IntegralDataTypeHolder getNextValue() {
try {
if ( timesCalled == 0 ) {
initValue();
return value.copy();
}
else {
return value.add( increment ).copy();
}
}
finally {
timesCalled++;
}
}
private void initValue() {
this.value.initialize( initialValue );
}
public int getTimesCalled() {
return timesCalled;
}
public long getCurrentValue() {
return value == null ? -1 : value.getActualLongValue();
}
}
}