KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > excalibur > datasource > ids > test > TableIdGeneratorMultithreadedJdbcTestCase


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14  * implied.
15  *
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */

19
20 package org.apache.avalon.excalibur.datasource.ids.test;
21
22 import java.sql.Connection JavaDoc;
23 import java.sql.ResultSet JavaDoc;
24 import java.sql.SQLException JavaDoc;
25 import java.sql.Statement JavaDoc;
26 import java.util.HashMap JavaDoc;
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 /**
36  * Test the TableIdGenerator Component.
37  *
38  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
39  */

40 public class TableIdGeneratorMultithreadedJdbcTestCase
41     extends ExcaliburTestCase
42 {
43     private static final String JavaDoc 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 JavaDoc m_semaphore = new Object JavaDoc();
53     protected IdGenerator m_idGenerator;
54     protected int m_perThreadGets;
55     protected HashMap JavaDoc m_ids;
56     protected Throwable JavaDoc m_throwable;
57
58     /*---------------------------------------------------------------
59      * Constructors
60      *-------------------------------------------------------------*/

61     public TableIdGeneratorMultithreadedJdbcTestCase( String JavaDoc name )
62     {
63         super( name );
64     }
65
66     /*---------------------------------------------------------------
67      * TestCase Methods
68      *-------------------------------------------------------------*/

69     public void setUp() throws Exception JavaDoc
70     {
71         super.setUp();
72
73         // Get a reference to a data source
74
m_dbSelector = (ComponentSelector)manager.lookup( DataSourceComponent.ROLE + "Selector" );
75         m_dataSource = (DataSourceComponent)m_dbSelector.select( "test-db" );
76
77         // We need to initialize an ids table in the database for these tests.
78
try
79         {
80             Connection JavaDoc conn = m_dataSource.getConnection();
81             try
82             {
83                 Statement JavaDoc statement = conn.createStatement();
84
85                 // Try to drop the table. It may not exist and throw an exception.
86
getLogEnabledLogger().debug( "Attempting to drop old ids table" );
87                 try
88                 {
89                     statement.executeUpdate( "DROP TABLE ids" );
90                 }
91                 catch( SQLException JavaDoc e )
92                 {
93                     // The table was probably just not there. Ignore this.
94
}
95
96                 // Create the table that we will use in this test.
97
// Different depending on the db. Please add new statements as new databases are
98
// tested.
99
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 JavaDoc e )
112         {
113             getLogEnabledLogger().error( "Unable to initialize database for test.", e );
114             fail( "Unable to initialize database for test. " + e );
115         }
116
117         // Get a reference to an IdGenerator Selector.
118
// Individual IdGenerators are obtained in the tests.
119
m_idGeneratorSelector = (ComponentSelector)manager.lookup( IdGenerator.ROLE + "Selector" );
120
121     }
122
123     public void tearDown() throws Exception JavaDoc
124     {
125         // Free up the IdGenerator Selector
126
if( m_idGeneratorSelector != null )
127         {
128             manager.release( m_idGeneratorSelector );
129
130             m_dbSelector = null;
131         }
132
133         try
134         {
135             Connection JavaDoc conn = m_dataSource.getConnection();
136             try
137             {
138                 Statement JavaDoc statement = conn.createStatement();
139
140                 // Delete the table that we will use in this test.
141
getLogEnabledLogger().debug( "Drop ids table" );
142                 statement.executeUpdate( "DROP TABLE ids" );
143             }
144             finally
145             {
146                 conn.close();
147             }
148         }
149         catch( SQLException JavaDoc e )
150         {
151             getLogEnabledLogger().error( "Unable to cleanup database after test.", e );
152             // Want to continue
153
}
154
155         // Free up the data source
156
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     /*---------------------------------------------------------------
174      * Test Cases
175      *-------------------------------------------------------------*/

176     public void testSimpleRequestIdsSize1() throws Exception JavaDoc
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             // Initialize the counter in the database.
189
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 JavaDoc
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             // Initialize the counter in the database.
212
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 JavaDoc
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             // Initialize the counter in the database.
235
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 JavaDoc
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                 // Initialize the counter in the database.
260
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     /*---------------------------------------------------------------
276      * Utilitity Methods
277      *-------------------------------------------------------------*/

278     /**
279      * General multithreaded test of an IdGenerator
280      *
281      * @param idGenerator the Id Generator to test.
282      * @param firstId the first Id that is expected to be returned by the Id Generator.
283      * @param idCount the number of ids to request in the test.
284      * @param threadCount the number of threads to use to test the Id Generator.
285      */

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 JavaDoc();
299
300         // Create the runnable which will be used by the test.
301
Runnable JavaDoc runnable = new Runnable JavaDoc()
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 JavaDoc lId = new Long JavaDoc( id );
316
317                             // Make sure this id has not already been seen
318
if( m_ids.get( lId ) != null )
319                             {
320                                 getLogEnabledLogger().error( "Obtained a duplicate id: " + id );
321                                 duplicatesFound = true;
322                             }
323                             else
324                             {
325                                 // Store a reference to this id
326
m_ids.put( lId, lId );
327                             }
328                         }
329                     }
330                     catch( Throwable JavaDoc 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         // Run the test.
354
long duration;
355         try
356         {
357             duration = group.go();
358         }
359         catch( Throwable JavaDoc t )
360         {
361             // Throwable could have been thrown by one of the tests.
362
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         // Make sure that all of the expected ids were obtained
375
for( int i = 0; i < idCount; i++ )
376         {
377             Long JavaDoc id = new Long JavaDoc( 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     /**
390      * Tests to see whether or not the current DataSource supports BigDecimal
391      */

392     private boolean isBigDecimalImplemented()
393     {
394         String JavaDoc tableName = "foorbar_table";
395
396         // Add a row that can be selected.
397
initializeNextLongId( tableName, 1 );
398
399         try
400         {
401             Connection JavaDoc conn = m_dataSource.getConnection();
402             try
403             {
404                 Statement JavaDoc statement = conn.createStatement();
405
406                 ResultSet JavaDoc 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; // for compiler
416
}
417             }
418             finally
419             {
420                 conn.close();
421             }
422
423             // Implemented
424
return true;
425         }
426         catch( SQLException JavaDoc e )
427         {
428             if( e.toString().toLowerCase().indexOf( "implemented" ) > 0 )
429             {
430                 // Not implemented
431
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; // for compiler
436
}
437     }
438
439 /* Never used code
440     private void initializeNextBigDecimalId( String tableName, BigDecimal nextId )
441     {
442         try
443         {
444             Connection conn = m_dataSource.getConnection();
445             try
446             {
447                 Statement statement = conn.createStatement();
448
449                 statement.executeUpdate( "INSERT INTO ids (table_name, next_id) VALUES ('" +
450                                          tableName + "', " + nextId.toString() + ")" );
451             }
452             finally
453             {
454                 conn.close();
455             }
456         }
457         catch( SQLException e )
458         {
459             getLogEnabledLogger().error( "Unable to initialize next_id.", e );
460             fail( "Unable to initialize next_id. " + e );
461         }
462     }
463 */

464     private void initializeNextLongId( String JavaDoc tableName, long nextId )
465     {
466         try
467         {
468             Connection JavaDoc conn = m_dataSource.getConnection();
469             try
470             {
471                 Statement JavaDoc 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 JavaDoc e )
482         {
483             getLogEnabledLogger().error( "Unable to initialize next_id.", e );
484             fail( "Unable to initialize next_id. " + e );
485         }
486     }
487
488 /* Never used code
489     private BigDecimal peekNextBigDecimalId( String tableName )
490     {
491         try
492         {
493             Connection conn = m_dataSource.getConnection();
494             try
495             {
496                 Statement statement = conn.createStatement();
497
498                 ResultSet rs = statement.executeQuery( "SELECT next_id FROM ids " +
499                                                        "WHERE table_name = '" + tableName + "'" );
500                 if( rs.next() )
501                 {
502                     return rs.getBigDecimal( 1 );
503                 }
504                 else
505                 {
506                     fail( tableName + " row not in ids table." );
507                     return null; // for compiler
508                 }
509             }
510             finally
511             {
512                 conn.close();
513             }
514         }
515         catch( SQLException e )
516         {
517             getLogEnabledLogger().error( "Unable to peek next_id.", e );
518             fail( "Unable to peek next_id. " + e );
519             return null; // for compiler
520         }
521     }
522 */

523     private long peekNextLongId( String JavaDoc tableName )
524     {
525         try
526         {
527             Connection JavaDoc conn = m_dataSource.getConnection();
528             try
529             {
530                 Statement JavaDoc statement = conn.createStatement();
531
532                 ResultSet JavaDoc 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; // for compiler
542
}
543             }
544             finally
545             {
546                 conn.close();
547             }
548         }
549         catch( SQLException JavaDoc e )
550         {
551             getLogEnabledLogger().error( "Unable to peek next_id.", e );
552             fail( "Unable to peek next_id. " + e );
553             return -1; // for compiler
554
}
555     }
556 }
557
558
Popular Tags