1 19 20 package org.apache.avalon.excalibur.datasource.ids.test; 21 22 import java.sql.Connection ; 23 import java.sql.ResultSet ; 24 import java.sql.SQLException ; 25 import java.sql.Statement ; 26 import java.util.HashMap ; 27 28 import org.apache.avalon.excalibur.datasource.DataSourceComponent; 29 import org.apache.avalon.excalibur.datasource.ids.IdGenerator; 30 import org.apache.avalon.excalibur.testcase.CascadingAssertionFailedError; 31 import org.apache.avalon.excalibur.testcase.ExcaliburTestCase; 32 import org.apache.avalon.excalibur.testcase.LatchedThreadGroup; 33 import org.apache.avalon.framework.component.ComponentSelector; 34 35 40 public class TableIdGeneratorMultithreadedJdbcTestCase 41 extends ExcaliburTestCase 42 { 43 private static final String TABLE_KEY = "test"; 44 private static final int ID_COUNT = 1000; 45 private static final int THREAD_COUNT = 50; 46 47 private ComponentSelector m_dbSelector; 48 private DataSourceComponent m_dataSource; 49 50 private ComponentSelector m_idGeneratorSelector; 51 52 protected Object m_semaphore = new Object (); 53 protected IdGenerator m_idGenerator; 54 protected int m_perThreadGets; 55 protected HashMap m_ids; 56 protected Throwable m_throwable; 57 58 61 public TableIdGeneratorMultithreadedJdbcTestCase( String name ) 62 { 63 super( name ); 64 } 65 66 69 public void setUp() throws Exception 70 { 71 super.setUp(); 72 73 m_dbSelector = (ComponentSelector)manager.lookup( DataSourceComponent.ROLE + "Selector" ); 75 m_dataSource = (DataSourceComponent)m_dbSelector.select( "test-db" ); 76 77 try 79 { 80 Connection conn = m_dataSource.getConnection(); 81 try 82 { 83 Statement statement = conn.createStatement(); 84 85 getLogEnabledLogger().debug( "Attempting to drop old ids table" ); 87 try 88 { 89 statement.executeUpdate( "DROP TABLE ids" ); 90 } 91 catch( SQLException e ) 92 { 93 } 95 96 getLogEnabledLogger().debug( "Create new ids table" ); 100 statement.executeUpdate( 101 "CREATE TABLE ids ( " + 102 "table_name varchar(16) NOT NULL, " + 103 "next_id DECIMAL(30) NOT NULL, " + 104 "PRIMARY KEY (table_name))" ); 105 } 106 finally 107 { 108 conn.close(); 109 } 110 } 111 catch( SQLException e ) 112 { 113 getLogEnabledLogger().error( "Unable to initialize database for test.", e ); 114 fail( "Unable to initialize database for test. " + e ); 115 } 116 117 m_idGeneratorSelector = (ComponentSelector)manager.lookup( IdGenerator.ROLE + "Selector" ); 120 121 } 122 123 public void tearDown() throws Exception 124 { 125 if( m_idGeneratorSelector != null ) 127 { 128 manager.release( m_idGeneratorSelector ); 129 130 m_dbSelector = null; 131 } 132 133 try 134 { 135 Connection conn = m_dataSource.getConnection(); 136 try 137 { 138 Statement statement = conn.createStatement(); 139 140 getLogEnabledLogger().debug( "Drop ids table" ); 142 statement.executeUpdate( "DROP TABLE ids" ); 143 } 144 finally 145 { 146 conn.close(); 147 } 148 } 149 catch( SQLException e ) 150 { 151 getLogEnabledLogger().error( "Unable to cleanup database after test.", e ); 152 } 154 155 if( m_dbSelector != null ) 157 { 158 if( m_dataSource != null ) 159 { 160 m_dbSelector.release( m_dataSource ); 161 162 m_dataSource = null; 163 } 164 165 manager.release( m_dbSelector ); 166 167 m_dbSelector = null; 168 } 169 170 super.tearDown(); 171 } 172 173 176 public void testSimpleRequestIdsSize1() throws Exception 177 { 178 getLogEnabledLogger().info( "testSimpleRequestIdsSize1" ); 179 180 IdGenerator idGenerator = 181 (IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize1" ); 182 try 183 { 184 long firstId = 1; 185 int idCount = ID_COUNT; 186 int threadCount = THREAD_COUNT; 187 188 initializeNextLongId( TABLE_KEY, firstId ); 190 191 generalTestCase( idGenerator, firstId, idCount, threadCount ); 192 } 193 finally 194 { 195 m_idGeneratorSelector.release( idGenerator ); 196 } 197 } 198 199 public void testSimpleRequestIdsSize10() throws Exception 200 { 201 getLogEnabledLogger().info( "testSimpleRequestIdsSize10" ); 202 203 IdGenerator idGenerator = 204 (IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize10" ); 205 try 206 { 207 long firstId = 1; 208 int idCount = ID_COUNT; 209 int threadCount = THREAD_COUNT; 210 211 initializeNextLongId( TABLE_KEY, firstId ); 213 214 generalTestCase( idGenerator, firstId, idCount, threadCount ); 215 } 216 finally 217 { 218 m_idGeneratorSelector.release( idGenerator ); 219 } 220 } 221 222 public void testSimpleRequestIdsSize100() throws Exception 223 { 224 getLogEnabledLogger().info( "testSimpleRequestIdsSize100" ); 225 226 IdGenerator idGenerator = 227 (IdGenerator)m_idGeneratorSelector.select( "ids-testSimpleRequestIdsSize100" ); 228 try 229 { 230 long firstId = 1; 231 int idCount = ID_COUNT; 232 int threadCount = THREAD_COUNT; 233 234 initializeNextLongId( TABLE_KEY, firstId ); 236 237 generalTestCase( idGenerator, firstId, idCount, threadCount ); 238 } 239 finally 240 { 241 m_idGeneratorSelector.release( idGenerator ); 242 } 243 } 244 245 public void testBigDecimalRequestIdsSize10() throws Exception 246 { 247 getLogEnabledLogger().info( "testBigDecimalRequestIdsSize10" ); 248 249 if( isBigDecimalImplemented() ) 250 { 251 IdGenerator idGenerator = 252 (IdGenerator)m_idGeneratorSelector.select( "ids-testBigDecimalRequestIdsSize10" ); 253 try 254 { 255 long firstId = 1; 256 int idCount = ID_COUNT; 257 int threadCount = THREAD_COUNT; 258 259 initializeNextLongId( TABLE_KEY, firstId ); 261 262 generalTestCase( idGenerator, firstId, idCount, threadCount ); 263 } 264 finally 265 { 266 m_idGeneratorSelector.release( idGenerator ); 267 } 268 } 269 else 270 { 271 getLogEnabledLogger().warn( "Test Skipped because BigDecimals are not implemented in current driver." ); 272 } 273 } 274 275 278 286 private void generalTestCase( final IdGenerator idGenerator, 287 final long firstId, 288 final int idCount, 289 final int threadCount ) 290 { 291 if( idCount % threadCount != 0 ) 292 { 293 fail( "idCount must be evenly divisible by threadCount" ); 294 } 295 296 m_idGenerator = idGenerator; 297 m_perThreadGets = idCount / threadCount; 298 m_ids = new HashMap (); 299 300 Runnable runnable = new Runnable () 302 { 303 public void run() 304 { 305 boolean duplicatesFound = false; 306 307 for( int i = 0; i < m_perThreadGets; i++ ) 308 { 309 try 310 { 311 long id = m_idGenerator.getNextLongId(); 312 313 synchronized( m_semaphore ) 314 { 315 Long lId = new Long ( id ); 316 317 if( m_ids.get( lId ) != null ) 319 { 320 getLogEnabledLogger().error( "Obtained a duplicate id: " + id ); 321 duplicatesFound = true; 322 } 323 else 324 { 325 m_ids.put( lId, lId ); 327 } 328 } 329 } 330 catch( Throwable t ) 331 { 332 synchronized( m_semaphore ) 333 { 334 if( m_throwable == null ) 335 { 336 m_throwable = t; 337 } 338 } 339 return; 340 } 341 } 342 343 if( duplicatesFound ) 344 { 345 fail( "IdGenerator returned duplicate ids." ); 346 } 347 } 348 }; 349 350 LatchedThreadGroup group = new LatchedThreadGroup( runnable, threadCount ); 351 group.enableLogging( getLogEnabledLogger() ); 352 353 long duration; 355 try 356 { 357 duration = group.go(); 358 } 359 catch( Throwable t ) 360 { 361 if( m_throwable == null ) 363 { 364 m_throwable = t; 365 } 366 duration = 0; 367 } 368 369 if( m_throwable != null ) 370 { 371 throw new CascadingAssertionFailedError( "Exception in test thread.", m_throwable ); 372 } 373 374 for( int i = 0; i < idCount; i++ ) 376 { 377 Long id = new Long ( firstId + i ); 378 assertTrue( "The IdGenerator did not return an expected id (" + id + ")", 379 m_ids.get( id ) != null ); 380 } 381 382 getLogEnabledLogger().info( "It took " + duration + "ms. for " + threadCount + 383 " threads to allocate " + idCount + " ids." ); 384 385 assertEquals( "The next_id column in the database did not have the expected value.", 386 firstId + idCount, peekNextLongId( TABLE_KEY ) ); 387 } 388 389 392 private boolean isBigDecimalImplemented() 393 { 394 String tableName = "foorbar_table"; 395 396 initializeNextLongId( tableName, 1 ); 398 399 try 400 { 401 Connection conn = m_dataSource.getConnection(); 402 try 403 { 404 Statement statement = conn.createStatement(); 405 406 ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " + 407 "WHERE table_name = '" + tableName + "'" ); 408 if( rs.next() ) 409 { 410 rs.getBigDecimal( 1 ); 411 } 412 else 413 { 414 fail( tableName + " row not in ids table." ); 415 return false; } 417 } 418 finally 419 { 420 conn.close(); 421 } 422 423 return true; 425 } 426 catch( SQLException e ) 427 { 428 if( e.toString().toLowerCase().indexOf( "implemented" ) > 0 ) 429 { 430 return false; 432 } 433 getLogEnabledLogger().error( "Unable to test for BigDecimal support.", e ); 434 fail( "Unable to test for BigDecimal support. " + e ); 435 return false; } 437 } 438 439 464 private void initializeNextLongId( String tableName, long nextId ) 465 { 466 try 467 { 468 Connection conn = m_dataSource.getConnection(); 469 try 470 { 471 Statement statement = conn.createStatement(); 472 473 statement.executeUpdate( "INSERT INTO ids (table_name, next_id) VALUES ('" + 474 tableName + "', " + nextId + ")" ); 475 } 476 finally 477 { 478 conn.close(); 479 } 480 } 481 catch( SQLException e ) 482 { 483 getLogEnabledLogger().error( "Unable to initialize next_id.", e ); 484 fail( "Unable to initialize next_id. " + e ); 485 } 486 } 487 488 523 private long peekNextLongId( String tableName ) 524 { 525 try 526 { 527 Connection conn = m_dataSource.getConnection(); 528 try 529 { 530 Statement statement = conn.createStatement(); 531 532 ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " + 533 "WHERE table_name = '" + tableName + "'" ); 534 if( rs.next() ) 535 { 536 return rs.getLong( 1 ); 537 } 538 else 539 { 540 fail( tableName + " row not in ids table." ); 541 return -1; } 543 } 544 finally 545 { 546 conn.close(); 547 } 548 } 549 catch( SQLException e ) 550 { 551 getLogEnabledLogger().error( "Unable to peek next_id.", e ); 552 fail( "Unable to peek next_id. " + e ); 553 return -1; } 555 } 556 } 557 558 | Popular Tags |