/* * Copyright 2009 Udai Gupta, Ralf Joachim * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.castor.cpa.test.test07; import java.sql.SQLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.cpa.test.framework.CPATestCase; import org.castor.cpa.test.framework.xml.types.DatabaseEngineType; import org.exolab.castor.jdo.Database; import org.exolab.castor.jdo.JDOManager; import org.exolab.castor.jdo.PersistenceException; /** * This is a concurrent stress test. This test creates one writing thread * that continuously creates, loads and modifies data objects. It loads * and test if the modifications has succeed, loads again and removes * data objects and creates a new object with the same identity again....; * multiple read threads are created and continuously read the data objects * from transactions and commit the transactions without modifying any * data object. The 'read and commit' actions essentially lock and unlock * the data object. These tests pass if all modifications to data objects * via the write thread is properly persisted and there is no deadlock * occurring. Passing the tests confirm Castor JDO properly lock and release * objects. * <p> * Tests are performed on all four different cache types. (note, these tests * may failed if the number of JDBC connections available to Castor JDO is * too small. To resolve the problem, reduce the number of read threads or * increase the available connections.) */ public final class TestCacheLeakage extends CPATestCase { /** * The <a href="http://jakarta.apache.org/commons/logging/">Jakarta * Commons Logging</a> instance used for all logging. */ private static final Log LOG = LogFactory.getLog(TestCacheLeakage.class); private static final int COUNT_LIMITED = 0; private static final int TIME_LIMITED = 1; private static final int NO_CACHE = 2; private static final int UNLIMITED = 3; /** Number of target data objects to be created and deleted */ private static final int NUM_OF_CREATE_DELETE = 10; /** Number of trial of loads and releases on each object */ private static final int NUM_OF_READ = 50; /** Number of load and releases of read threads to race on target */ private static final int NUM_OF_READ_THREADS = 5; private static final String DBNAME = "test07"; private static final String MAPPING = "/org/castor/cpa/test/test07/mapping.xml"; private Database _db; private JDOManager _jdo; /** The java class of the target data objects */ private static Class<?> _classType; /** The cache type used in the current test */ private static int _cacheType; /** Indicates leakage detected */ private static boolean _errLeak; /** Indicates error detected */ private static boolean _errCount; /** * Constructor * * @param category the test suite of these test cases */ public TestCacheLeakage(final String name) { super(name); _errLeak = false; _errCount = false; } // Test are only included/excluded for engines that have been tested with this test suite. public boolean include(final DatabaseEngineType engine) { return (engine == DatabaseEngineType.DERBY) || (engine == DatabaseEngineType.HSQL) || (engine == DatabaseEngineType.MYSQL) || (engine == DatabaseEngineType.ORACLE) || (engine == DatabaseEngineType.POSTGRESQL); } /** * Get a JDO Database and get a JDBC connection */ public void setUp() throws Exception { _jdo = getJDOManager(DBNAME, MAPPING); _db = _jdo.getDatabase(); } /** * Run the stress test for four different cache setting */ public void testCacheLeakage() throws Exception { _cacheType = TIME_LIMITED; runOnce(); _cacheType = NO_CACHE; runOnce(); _cacheType = UNLIMITED; runOnce(); _cacheType = COUNT_LIMITED; runOnce(); assertTrue("Element leak not detected!", !_errLeak); assertTrue("Race condition not happened!", !_errCount); } public void tearDown() throws PersistenceException, SQLException { if (_db.isActive()) { _db.rollback(); } _db.close(); } /** * Helper class to run the stress tests once for a cache type. */ public void runOnce() throws Exception { // clear the table _db.begin(); int del = _db.getJdbcConnection().createStatement().executeUpdate( "DELETE FROM test07_race"); _db.commit(); LOG.debug("row(s) deleted in table core_race: " + del); // set the className and classType to be used switch (_cacheType) { case COUNT_LIMITED: _classType = RaceCount.class; break; case TIME_LIMITED: _classType = RaceTime.class; break; case NO_CACHE: _classType = RaceNone.class; break; case UNLIMITED: _classType = RaceUnlimited.class; break; default: LOG.error("Unknown cache type"); } CreateDeleteThread cdThread = new CreateDeleteThread( this, _jdo, _cacheType, NUM_OF_CREATE_DELETE); ReadThread[] rThread = new ReadThread[NUM_OF_READ_THREADS]; for (int i = 0; i < NUM_OF_READ_THREADS; i++) { rThread[i] = new ReadThread(this, cdThread, _jdo, NUM_OF_READ); rThread[i].start(); } cdThread.start(); // Polling the test case to see if it is finished try { while (!cdThread.isDone()) { Thread.sleep(500); } // Joins all the finished threads cdThread.join(); for (int i = 0; i < NUM_OF_READ_THREADS; i++) { rThread[i].join(); } } catch (InterruptedException ex) { fail(ex.toString()); } } public static Class<?> getClassType() { return _classType; } public static void setClassType(final Class<?> classType) { _classType = classType; } public static int getCacheType() { return _cacheType; } public static void setCacheType(final int cacheType) { _cacheType = cacheType; } public static boolean isErrLeak() { return _errLeak; } public static void setErrLeak(final boolean errLeak) { _errLeak = errLeak; } public static boolean isErrCount() { return _errCount; } public static void setErrCount(final boolean errCount) { _errCount = errCount; } }