001: package org.apache.ojb.compare;
002:
003: import java.sql.Connection;
004: import java.sql.PreparedStatement;
005: import java.sql.ResultSet;
006:
007: import org.apache.ojb.broker.OJBException;
008: import org.apache.ojb.broker.TestHelper;
009: import org.apache.ojb.broker.accesslayer.ConnectionFactory;
010: import org.apache.ojb.broker.accesslayer.ConnectionFactoryFactory;
011: import org.apache.ojb.broker.metadata.ClassDescriptor;
012: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
013: import org.apache.ojb.broker.metadata.MetadataManager;
014: import org.apache.ojb.broker.query.Criteria;
015: import org.apache.ojb.broker.query.Query;
016: import org.apache.ojb.broker.query.QueryByCriteria;
017: import org.apache.ojb.broker.util.logging.Logger;
018: import org.apache.ojb.broker.util.logging.LoggerFactory;
019: import org.apache.ojb.junit.PBTestCase;
020:
021: /**
022: * This TestCase contains the OJB performance benchmarks for the
023: * JDBC API. The original testcases have been enhanced with
024: * code dealing with db failover situations.
025: * @author Thomas Mahler
026: */
027: public class PerformanceJdbcFailoverTest extends PBTestCase {
028: private Logger logger = LoggerFactory.getLogger("failover");
029:
030: /**
031: * the number of PerformanceArticle objects to work with.
032: */
033: static int articleCount = 10000;
034:
035: /**
036: * the number of iterations to perform.
037: */
038: static int iterations = 2;
039:
040: /**
041: * the maximum number of retries if db fails.
042: */
043: static int maxRetries = 5;
044:
045: /**
046: * the maximum time to wait on db availability.
047: */
048: static int maxWait = 30;
049:
050: /**
051: * the offset value for PerformanceArticle primary keys
052: */
053: int offsetId = 10000;
054: private PerformanceArticle[] arr;
055: private int actualRetries = 0;
056:
057: /**
058: * BrokerTests constructor comment.
059: * @param name java.lang.String
060: */
061: public PerformanceJdbcFailoverTest(String name)
062:
063: {
064: super (name);
065: }
066:
067: /**
068: * launches the TestCase.
069: * The number of Objects to work with and the number of iterations
070: * to be performed can be adjusted by setting them as commandline parameters.
071: * @param args the String[] holding the commandline parameters.
072: */
073: public static void main(String[] args) {
074: if (args.length > 0) {
075: articleCount = Integer.parseInt(args[0]);
076: }
077: if (args.length > 1) {
078: iterations = Integer.parseInt(args[1]);
079: }
080: if (args.length > 2) {
081: maxRetries = Integer.parseInt(args[2]);
082: }
083: if (args.length > 3) {
084: maxWait = Integer.parseInt(args[3]);
085: }
086:
087: String[] arr = { PerformanceJdbcFailoverTest.class.getName() };
088: junit.textui.TestRunner.main(arr);
089: }
090:
091: /**
092: * setting up the test fixture.
093: */
094: public void setUp() throws Exception {
095: super .setUp();
096:
097: clearTable();
098: arr = new PerformanceArticle[articleCount];
099: for (int i = 0; i < articleCount; i++) {
100: PerformanceArticle a = createArticle(offsetId + i);
101: arr[i] = a;
102: }
103: }
104:
105: /**
106: * tearing down the test fixture.
107: */
108: public void tearDown() throws Exception {
109: super .tearDown();
110: }
111:
112: /**
113: * factory method that createa an PerformanceArticle with a given id.
114: * @return the created PerformanceArticle object
115: * @param id the primary key value for the new object
116: */
117: private PerformanceArticle createArticle(int id) {
118: PerformanceArticle a = new PerformanceArticle();
119: a.setArticleId(new Integer(id));
120: a.setArticleName("New Performance Article " + id);
121: a.setMinimumStock(100);
122: a.setOrderedUnits(17);
123: a.setPrice(100.0);
124: a.setProductGroupId(1);
125: a.setStock(234);
126: a.setSupplierId(4);
127: a.setUnit("bottle");
128: return a;
129: }
130:
131: /**
132: * obtain a JDBC Connection. OJB API is used to make this code portable for
133: * other target dabases and different lookup methods.
134: * @return the Connection to be used
135: */
136: private Connection getConnection() throws Exception {
137: Connection conn = null;
138: long startToWait = System.currentTimeMillis();
139: System.out.print("[");
140: System.out.flush();
141: while (true) {
142: try {
143: // Use OJB API to obtain JDBC Connection. All settings are read from
144: // the repository.xml file.
145: JdbcConnectionDescriptor jcd = MetadataManager
146: .getInstance().connectionRepository()
147: .getDescriptor(TestHelper.DEF_KEY);
148: ConnectionFactory cf = ConnectionFactoryFactory
149: .getInstance().createConnectionFactory();
150: conn = cf.lookupConnection(jcd);
151: System.out.println("] Waited for connection "
152: + (System.currentTimeMillis() - startToWait)
153: + "msecs");
154: break;
155: } catch (Throwable t) {
156: long now = System.currentTimeMillis();
157: if ((now - startToWait) > (1000 * maxWait)) {
158: System.out
159: .print("Timeout exceeded in getConnection(), DB not available!");
160: throw new OJBException(t);
161: } else {
162: if ((now % 1000) == 0) {
163: System.out.print("#");
164: System.out.flush();
165: }
166:
167: }
168: }
169: }
170: return conn;
171: }
172:
173: /**
174: * deletes all PerformanceArticle created by <code>insertNewArticles</code>.
175: */
176: protected void deleteArticles() throws Exception {
177: Connection conn = getConnection();
178:
179: // Use the OJB SqlGenerator to generate SQL Statements. All details about
180: // Table and column names are read from the repository.xml file.
181: ClassDescriptor cld = broker
182: .getClassDescriptor(PerformanceArticle.class);
183: String sql = broker.serviceSqlGenerator()
184: .getPreparedDeleteStatement(cld).getStatement();
185:
186: logger.debug("delete stmt: " + sql);
187:
188: long start = System.currentTimeMillis();
189: try {
190: conn.setAutoCommit(false);
191: PreparedStatement stmt = conn.prepareStatement(sql);
192: for (int i = 0; i < articleCount; i++) {
193: PerformanceArticle a = arr[i];
194: stmt.setInt(1, a.articleId.intValue());
195: stmt.execute();
196: }
197: conn.commit();
198: } catch (Throwable t) {
199: actualRetries++;
200: if (actualRetries <= maxRetries) {
201: logger.error("error during db operations:", t);
202: try {
203: conn.close();
204: } catch (Throwable ignored) {
205: }
206: deleteArticles();
207: } else {
208: logger.error("retry count exceeded!");
209: fail(t.getMessage());
210: }
211: } finally {
212: try {
213: conn.close();
214: } catch (Throwable ignored) {
215: }
216: }
217: long stop = System.currentTimeMillis();
218: logger.info("deleting " + articleCount + " Objects: "
219: + (stop - start) + " msec");
220: }
221:
222: /**
223: * create new PerformanceArticle objects and insert them into the RDBMS.
224: * The number of objects to create is defined by <code>articleCount</code>.
225: */
226: protected void insertNewArticles() throws Exception {
227: Connection conn = getConnection();
228:
229: // Use the OJB SqlGenerator to generate SQL Statements. All details about
230: // Table and column names are read from the repository.xml file.
231: ClassDescriptor cld = broker
232: .getClassDescriptor(PerformanceArticle.class);
233: String sql = broker.serviceSqlGenerator()
234: .getPreparedInsertStatement(cld).getStatement();
235:
236: logger.debug("insert stmt: " + sql);
237:
238: long start = System.currentTimeMillis();
239: try {
240: conn.setAutoCommit(false);
241: PreparedStatement stmt = conn.prepareStatement(sql);
242:
243: for (int i = 0; i < articleCount; i++) {
244: PerformanceArticle a = arr[i];
245:
246: stmt.setInt(1, a.articleId.intValue());
247: stmt.setString(2, a.articleName);
248: stmt.setInt(3, a.supplierId);
249: stmt.setInt(4, a.productGroupId);
250: stmt.setString(5, a.unit);
251: stmt.setDouble(6, a.price);
252: stmt.setInt(7, a.stock);
253: stmt.setInt(8, a.orderedUnits);
254: stmt.setInt(9, a.minimumStock);
255:
256: stmt.execute();
257: }
258: conn.commit();
259: } catch (Throwable t) {
260: actualRetries++;
261: if (actualRetries <= maxRetries) {
262: logger.error("error during db operations:", t);
263: try {
264: conn.close();
265: } catch (Throwable ignored) {
266: }
267: insertNewArticles();
268: } else {
269: logger.error("retry count exceeded!");
270: fail(t.getMessage());
271: }
272: } finally {
273: try {
274: conn.close();
275: } catch (Throwable ignored) {
276: }
277: }
278: long stop = System.currentTimeMillis();
279: logger.info("inserting " + articleCount + " Objects: "
280: + (stop - start) + " msec");
281:
282: }
283:
284: protected void clearTable() throws Exception {
285: Connection conn = getConnection();
286: try {
287: ClassDescriptor cld = broker
288: .getClassDescriptor(PerformanceArticle.class);
289: String table = cld.getFullTableName();
290: String sql = "DELETE FROM " + table;
291: PreparedStatement stmt = conn.prepareStatement(sql);
292: stmt.execute();
293: conn.close();
294: } catch (Throwable t) {
295: actualRetries++;
296: if (actualRetries <= maxRetries) {
297: logger.error("error during db operations:", t);
298: try {
299: conn.close();
300: } catch (Throwable ignored) {
301: }
302: clearTable();
303: } else {
304: logger.error("retry count exceeded!");
305: fail(t.getMessage());
306: }
307: } finally {
308: try {
309: conn.close();
310: } catch (Throwable ignored) {
311: }
312: }
313: }
314:
315: /**
316: * read in all the PerformanceArticles from the RDBMS that have
317: * been inserted by <code>insertNewArticles()</code>.
318: * The lookup is done one by one, that is: a primary key based lookup is used.
319: */
320: protected void readArticles() throws Exception {
321: Connection conn = getConnection();
322:
323: // Use the OJB SqlGenerator to generate SQL Statements. All details about
324: // Table and column names are read from the repository.xml file.
325: ClassDescriptor cld = broker
326: .getClassDescriptor(PerformanceArticle.class);
327: String sql = broker.serviceSqlGenerator()
328: .getPreparedSelectByPkStatement(cld).getStatement();
329: logger.debug("select stmt: " + sql);
330: long start = System.currentTimeMillis();
331:
332: String colId = cld.getFieldDescriptorByName("articleId")
333: .getColumnName();
334: String colName = cld.getFieldDescriptorByName("articleName")
335: .getColumnName();
336: String colSupplier = cld.getFieldDescriptorByName("supplierId")
337: .getColumnName();
338: String colGroup = cld
339: .getFieldDescriptorByName("productGroupId")
340: .getColumnName();
341: String colUnit = cld.getFieldDescriptorByName("unit")
342: .getColumnName();
343: String colPrice = cld.getFieldDescriptorByName("price")
344: .getColumnName();
345: String colStock = cld.getFieldDescriptorByName("stock")
346: .getColumnName();
347: String colOrdered = cld
348: .getFieldDescriptorByName("orderedUnits")
349: .getColumnName();
350: String colMin = cld.getFieldDescriptorByName("minimumStock")
351: .getColumnName();
352:
353: try {
354: conn.setAutoCommit(false);
355: PreparedStatement stmt = conn.prepareStatement(sql);
356: for (int i = 0; i < articleCount; i++) {
357: stmt.setInt(1, offsetId + i);
358: ResultSet rs = stmt.executeQuery();
359: rs.next();
360:
361: PerformanceArticle a = new PerformanceArticle();
362: a.articleId = new Integer(rs.getInt(colId));
363: a.articleName = rs.getString(colName);
364: a.supplierId = rs.getInt(colSupplier);
365: a.productGroupId = rs.getInt(colGroup);
366: a.unit = rs.getString(colUnit);
367: a.price = rs.getFloat(colPrice);
368: a.stock = rs.getInt(colStock);
369: a.orderedUnits = rs.getInt(colOrdered);
370: a.minimumStock = rs.getInt(colMin);
371: }
372: } catch (Throwable t) {
373: actualRetries++;
374: if (actualRetries <= maxRetries) {
375: logger.error("error during db operations:", t);
376: try {
377: conn.close();
378: } catch (Throwable ignored) {
379: }
380: readArticles();
381: } else {
382: logger.error("retry count exceeded!");
383: fail(t.getMessage());
384: }
385: } finally {
386: try {
387: conn.close();
388: } catch (Throwable ignored) {
389: }
390: }
391:
392: long stop = System.currentTimeMillis();
393: logger.info("querying " + articleCount + " Objects: "
394: + (stop - start) + " msec");
395:
396: }
397:
398: /**
399: * read in all the PerformanceArticles from the RDBMS that have
400: * been inserted by <code>insertNewArticles()</code>.
401: * The lookup is done with a cursor fetch,
402: * that is: a between Statement is used to select all inserted PerformanceArticles
403: * and Objects are read in by fetching from the cursor (JDBC ResultSet).
404: */
405: protected void readArticlesByCursor() throws Exception {
406: Connection conn = getConnection();
407:
408: Criteria c = new Criteria();
409: c.addBetween("articleId", new Integer(offsetId), new Integer(
410: offsetId + articleCount));
411: Query query = new QueryByCriteria(PerformanceArticle.class, c);
412:
413: // Use the OJB SqlGenerator to generate SQL Statements. All details about
414: // Table and column names are read from the repository.xml file.
415: ClassDescriptor cld = broker
416: .getClassDescriptor(PerformanceArticle.class);
417: String sql = broker.serviceSqlGenerator()
418: .getPreparedSelectStatement(query, cld).getStatement();
419:
420: logger.debug("select stmt: " + sql);
421: long start = System.currentTimeMillis();
422:
423: String colId = cld.getFieldDescriptorByName("articleId")
424: .getColumnName();
425: String colName = cld.getFieldDescriptorByName("articleName")
426: .getColumnName();
427: String colSupplier = cld.getFieldDescriptorByName("supplierId")
428: .getColumnName();
429: String colGroup = cld
430: .getFieldDescriptorByName("productGroupId")
431: .getColumnName();
432: String colUnit = cld.getFieldDescriptorByName("unit")
433: .getColumnName();
434: String colPrice = cld.getFieldDescriptorByName("price")
435: .getColumnName();
436: String colStock = cld.getFieldDescriptorByName("stock")
437: .getColumnName();
438: String colOrdered = cld
439: .getFieldDescriptorByName("orderedUnits")
440: .getColumnName();
441: String colMin = cld.getFieldDescriptorByName("minimumStock")
442: .getColumnName();
443:
444: int fetchCount = 0;
445: try {
446: conn.setAutoCommit(false);
447: PreparedStatement stmt = conn.prepareStatement(sql);
448: stmt.setInt(1, offsetId);
449: stmt.setInt(2, offsetId + articleCount);
450: ResultSet rs = stmt.executeQuery();
451: while (rs.next()) {
452: fetchCount++;
453:
454: PerformanceArticle a = new PerformanceArticle();
455: a.articleId = new Integer(rs.getInt(colId));
456: a.articleName = rs.getString(colName);
457: a.supplierId = rs.getInt(colSupplier);
458: a.productGroupId = rs.getInt(colGroup);
459: a.unit = rs.getString(colUnit);
460: a.price = rs.getFloat(colPrice);
461: a.stock = rs.getInt(colStock);
462: a.orderedUnits = rs.getInt(colOrdered);
463: a.minimumStock = rs.getInt(colMin);
464: }
465: } catch (Throwable t) {
466: actualRetries++;
467: if (actualRetries <= maxRetries) {
468: logger.error("error during db operations:", t);
469: try {
470: conn.close();
471: } catch (Throwable ignored) {
472: }
473: readArticlesByCursor();
474: } else {
475: logger.error("retry count exceeded!");
476: fail(t.getMessage());
477: }
478: } finally {
479: try {
480: conn.close();
481: } catch (Throwable ignored) {
482: }
483: }
484:
485: long stop = System.currentTimeMillis();
486: logger.info("fetching " + fetchCount + " Objects: "
487: + (stop - start) + " msec");
488:
489: }
490:
491: /**
492: * updates all PerformanceArticles inserted by <code>insertNewArticles()</code>.
493: * All objects are modified and changes are written to the RDBMS with an UPDATE.
494: */
495: protected void updateExistingArticles() throws Exception {
496: Connection conn = getConnection();
497:
498: // Use the OJB SqlGenerator to generate SQL Statements. All details about
499: // Table and column names are read from the repository.xml file.
500: ClassDescriptor cld = broker
501: .getClassDescriptor(PerformanceArticle.class);
502: String sql = broker.serviceSqlGenerator()
503: .getPreparedUpdateStatement(cld).getStatement();
504: logger.debug("update stmt: " + sql);
505:
506: // update all objects
507: for (int i = 0; i < articleCount; i++) {
508: arr[i].setPrice(arr[i].getPrice() * 1.95583);
509: }
510:
511: long start = System.currentTimeMillis();
512: try {
513: conn.setAutoCommit(false);
514: PreparedStatement stmt = conn.prepareStatement(sql);
515: for (int i = 0; i < articleCount; i++) {
516: PerformanceArticle a = arr[i];
517: stmt.setString(1, a.articleName);
518: stmt.setInt(2, a.supplierId);
519: stmt.setInt(3, a.productGroupId);
520: stmt.setString(4, a.unit);
521: stmt.setDouble(5, a.price);
522: stmt.setInt(6, a.stock);
523: stmt.setInt(7, a.orderedUnits);
524: stmt.setInt(8, a.minimumStock);
525: stmt.setInt(9, a.articleId.intValue());
526: stmt.execute();
527: }
528: conn.commit();
529: } catch (Throwable t) {
530: actualRetries++;
531: if (actualRetries <= maxRetries) {
532: logger.error("error during db operations:", t);
533: try {
534: conn.close();
535: } catch (Throwable ignored) {
536: }
537: updateExistingArticles();
538: } else {
539: logger.error("retry count exceeded!");
540: fail(t.getMessage());
541: }
542: } finally {
543: try {
544: conn.close();
545: } catch (Throwable ignored) {
546: }
547: }
548: long stop = System.currentTimeMillis();
549: logger.info("updating " + articleCount + " Objects: "
550: + (stop - start) + " msec");
551:
552: }
553:
554: /**
555: * this method is the driver for the complete Benchmark.
556: * It performs the following steps:
557: *
558: * 1.) n objects are created and inserted to the RDBMS.
559: * 2.) the created objects are modified. Modifications are written to the RDBMS with updates.
560: * 3.) All objects created in 1.) are read in by primary key based SELECT statements.
561: * 4.) Step 3.) is repeated to test caching facilities.
562: * 5.) All objects created in 1.) are read by iterating over a ResultSet.
563: * 6.) All objects created in 1.) are deleted with n separate DELETE Statements.
564: */
565: public void testBenchmark() {
566: try {
567: logger.info("Test for native JDBC");
568: for (int i = 0; i < iterations; i++) {
569: logger.info("");
570:
571: // store all Article objects
572: insertNewArticles();
573:
574: // update all objects
575: updateExistingArticles();
576:
577: // querying
578: readArticles();
579:
580: readArticles();
581:
582: // fetching objects
583: readArticlesByCursor();
584:
585: // delete all objects
586: deleteArticles();
587: }
588: } catch (Throwable t) {
589: logger.error(t);
590: fail(t.getMessage());
591: }
592: }
593:
594: }
|