Return-Path: Delivered-To: apmail-jakarta-jcs-dev-archive@www.apache.org Received: (qmail 27983 invoked from network); 11 Jul 2006 19:36:34 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 11 Jul 2006 19:36:34 -0000 Received: (qmail 18454 invoked by uid 500); 11 Jul 2006 19:36:30 -0000 Delivered-To: apmail-jakarta-jcs-dev-archive@jakarta.apache.org Received: (qmail 18438 invoked by uid 500); 11 Jul 2006 19:36:30 -0000 Mailing-List: contact jcs-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "JCS Developers List" Delivered-To: mailing list jcs-dev@jakarta.apache.org Received: (qmail 18403 invoked by uid 500); 11 Jul 2006 19:36:30 -0000 Delivered-To: apmail-jakarta-jcs-commits@jakarta.apache.org Received: (qmail 18394 invoked by uid 500); 11 Jul 2006 19:36:30 -0000 Delivered-To: apmail-jakarta-jcs-cvs@jakarta.apache.org Received: (qmail 18381 invoked by uid 99); 11 Jul 2006 19:36:29 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 11 Jul 2006 12:36:29 -0700 X-ASF-Spam-Status: No, hits=-8.6 required=10.0 tests=ALL_TRUSTED,INFO_TLD,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 11 Jul 2006 12:36:26 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id A87171A981D; Tue, 11 Jul 2006 12:36:06 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r420957 [1/2] - in /jakarta/jcs/trunk: ./ src/java/org/apache/jcs/auxiliary/ src/java/org/apache/jcs/auxiliary/disk/jdbc/ src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/ src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ src/java/or... Date: Tue, 11 Jul 2006 19:36:03 -0000 To: jcs-cvs@jakarta.apache.org From: asmuts@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060711193606.A87171A981D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: asmuts Date: Tue Jul 11 12:36:02 2006 New Revision: 420957 URL: http://svn.apache.org/viewvc?rev=420957&view=rev Log: I added a MySQL Disk Cache to JCS that extends the base JDBC Disk Cache. The MySQL child has two new features. It can optimize on a schedule and balk. The optimization schedule is a simple list of time in hh:mm:ss format. The optimizer knows how to detect optimization failures and will try to repair the table is the optimization fails. It also logs the table status after optimization and how long it took to perform the optimization. It can also be configured to balk during optimizations. If you are running a JCS server cluster, it can be configured such that nodes will try to get from each other. If the mysql disk cache balks, the primary will to try and fetch data from the failover. If the primary and failover are scheduled to optimize a few minute apart, then there should be no client impact. I added a new pool access class and a table status object that is used to keep track of what processes might be working on a particular table. I also changed the get to use toString rather than assume that the key is a string. Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java jakarta/jcs/trunk/src/test-conf/TestMySQLDiskCache.ccf jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheHsqlBackedUnitTest.java jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheUnitTest.java jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizerManualTester.java jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParserUtilUnitTest.java Modified: jakarta/jcs/trunk/project.xml jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AuxiliaryCacheAttributes.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/lateral/socket/tcp/LateralTCPSender.java Modified: jakarta/jcs/trunk/project.xml URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/project.xml?rev=420957&r1=420956&r2=420957&view=diff ============================================================================== --- jakarta/jcs/trunk/project.xml (original) +++ jakarta/jcs/trunk/project.xml Tue Jul 11 12:36:02 2006 @@ -5,7 +5,7 @@ 3 JCS jcs - 1.2.7.7 + 1.2.7.8 Apache Software Foundation http://jakarta.apache.org/ @@ -162,6 +162,13 @@ http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html + + + + mysql + mysql-connector-java + 3.0.10 + Modified: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java?rev=420957&r1=420956&r2=420957&view=diff ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java (original) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java Tue Jul 11 12:36:02 2006 @@ -3,13 +3,14 @@ import org.apache.jcs.engine.behavior.ICacheEventQueue; /** + * This has common attributes used by all auxiliaries. + *

* @author aaronsm * */ public abstract class AbstractAuxiliaryCacheAttributes implements AuxiliaryCacheAttributes { - /** * cacheName */ @@ -50,9 +51,9 @@ return this.cacheName; } - /* - * (non-Javadoc) - * + /** + * This is the name of the auxiliary in configuration file. + *

* @see org.apache.jcs.auxiliary.AuxiliaryCacheAttributes#setName(java.lang.String) */ public void setName( String s ) Modified: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AuxiliaryCacheAttributes.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AuxiliaryCacheAttributes.java?rev=420957&r1=420956&r2=420957&view=diff ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AuxiliaryCacheAttributes.java (original) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/AuxiliaryCacheAttributes.java Tue Jul 11 12:36:02 2006 @@ -53,7 +53,7 @@ public String getCacheName(); /** - * Name know by by configurator + * Name known by by configurator * * @param s * The new name value Modified: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java?rev=420957&r1=420956&r2=420957&view=diff ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java (original) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java Tue Jul 11 12:36:02 2006 @@ -15,7 +15,6 @@ import java.io.Serializable; import java.sql.Connection; import java.sql.Date; -import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,14 +24,8 @@ import java.util.List; import java.util.Set; -import org.apache.commons.dbcp.ConnectionFactory; -import org.apache.commons.dbcp.DriverManagerConnectionFactory; -import org.apache.commons.dbcp.PoolableConnectionFactory; -import org.apache.commons.dbcp.PoolingDriver; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.commons.pool.ObjectPool; -import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.jcs.auxiliary.disk.AbstractDiskCache; import org.apache.jcs.engine.CacheConstants; import org.apache.jcs.engine.behavior.ICacheElement; @@ -50,20 +43,20 @@ *

* *

- *                     drop TABLE JCS_STORE;
- *                    
- *                     CREATE TABLE JCS_STORE
- *                     (
- *                     CACHE_KEY                  VARCHAR(250)          NOT NULL,
- *                     REGION                     VARCHAR(250)          NOT NULL,
- *                     ELEMENT                    BLOB,
- *                     CREATE_TIME                DATE,
- *                     CREATE_TIME_SECONDS        BIGINT,
- *                     MAX_LIFE_SECONDS           BIGINT,
- *                     SYSTEM_EXPIRE_TIME_SECONDS BIGINT,
- *                     IS_ETERNAL                 CHAR(1),
- *                     PRIMARY KEY (CACHE_KEY, REGION)
- *                     );
+ *                       drop TABLE JCS_STORE;
+ *                      
+ *                       CREATE TABLE JCS_STORE
+ *                       (
+ *                       CACHE_KEY                  VARCHAR(250)          NOT NULL,
+ *                       REGION                     VARCHAR(250)          NOT NULL,
+ *                       ELEMENT                    BLOB,
+ *                       CREATE_TIME                DATE,
+ *                       CREATE_TIME_SECONDS        BIGINT,
+ *                       MAX_LIFE_SECONDS           BIGINT,
+ *                       SYSTEM_EXPIRE_TIME_SECONDS BIGINT,
+ *                       IS_ETERNAL                 CHAR(1),
+ *                       PRIMARY KEY (CACHE_KEY, REGION)
+ *                       );
  * 
* *

@@ -87,12 +80,6 @@ private JDBCDiskCacheAttributes jdbcDiskCacheAttributes; - private static final String DEFAULT_POOL_NAME = "jcs"; - - private String poolName = DEFAULT_POOL_NAME; - - private static final String DRIVER_NAME = "jdbc:apache:commons:dbcp:"; - private int updateCount = 0; private int getCount = 0; @@ -100,13 +87,23 @@ // if count % interval == 0 then log private static final int LOG_INTERVAL = 100; + private JDBCDiskCachePoolAccess poolAccess = null; + + private TableState tableState; + /** + * Constructs a JDBC Disk Cache for the provided cache attributes. The table + * state object is used to mark deletions. + *

* @param cattr + * @param tableState */ - public JDBCDiskCache( JDBCDiskCacheAttributes cattr ) + public JDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState ) { super( cattr ); + this.setTableState( tableState ); + setJdbcDiskCacheAttributes( cattr ); if ( log.isInfoEnabled() ) @@ -114,10 +111,21 @@ log.info( "jdbcDiskCacheAttributes = " + getJdbcDiskCacheAttributes() ); } - // WE SHOULD HAVE A DIFFERENT POOL FOR EACH DB NO REGION - // THE SAME TABLE CAN BE USED BY MULTIPLE REGIONS - // this.setPoolName( jdbcDiskCacheAttributes.getCacheName() ); + /** + * This initializes the pool access. + */ + initializePoolAccess( cattr ); + // Initialization finished successfully, so set alive to true. + alive = true; + } + + /** + * Registers the driver and creates a poolAccess class. + * @param cattr + */ + protected void initializePoolAccess( JDBCDiskCacheAttributes cattr ) + { try { try @@ -130,18 +138,17 @@ log.error( "Couldn't find class for driver [" + cattr.getDriverClassName() + "]", e ); } - setupDriver( cattr.getUrl() + cattr.getDatabase(), cattr.getUserName(), cattr.getPassword(), cattr - .getMaxActive() ); + poolAccess = new JDBCDiskCachePoolAccess( cattr.getName() ); + + poolAccess.setupDriver( cattr.getUrl() + cattr.getDatabase(), cattr.getUserName(), cattr.getPassword(), + cattr.getMaxActive() ); - logDriverStats(); + poolAccess.logDriverStats(); } catch ( Exception e ) { log.error( "Problem getting connection.", e ); } - - // Initialization finished successfully, so set alive to true. - alive = true; } /* @@ -160,7 +167,7 @@ Connection con; try { - con = DriverManager.getConnection( getPoolUrl() ); + con = poolAccess.getConnection(); } catch ( SQLException e ) { @@ -356,7 +363,7 @@ Connection con; try { - con = DriverManager.getConnection( getPoolUrl() ); + con = poolAccess.getConnection(); } catch ( SQLException e ) { @@ -449,7 +456,7 @@ String selectString = "select ELEMENT from " + getJdbcDiskCacheAttributes().getTableName() + " where REGION = ? and CACHE_KEY = ?"; - Connection con = DriverManager.getConnection( getPoolUrl() ); + Connection con = poolAccess.getConnection(); try { PreparedStatement psSelect = null; @@ -457,7 +464,7 @@ { psSelect = con.prepareStatement( selectString ); psSelect.setString( 1, this.getCacheName() ); - psSelect.setString( 2, (String) key ); + psSelect.setString( 2, key.toString() ); ResultSet rs = null; rs = psSelect.executeQuery(); @@ -548,7 +555,7 @@ sql = "delete from " + getJdbcDiskCacheAttributes().getTableName() + " where REGION = '" + this.getCacheName() + "' and CACHE_KEY = like '" + key + "%'"; } - Connection con = DriverManager.getConnection( getPoolUrl() ); + Connection con = poolAccess.getConnection(); Statement sStatement = null; try { @@ -597,7 +604,7 @@ { String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName() + " where REGION = '" + this.getCacheName() + "'"; - Connection con = DriverManager.getConnection( getPoolUrl() ); + Connection con = poolAccess.getConnection(); Statement sStatement = null; try { @@ -653,6 +660,8 @@ try { + getTableState().setState( TableState.DELETE_RUNNING ); + long now = System.currentTimeMillis() / 1000; // This is to slow when we push over a million records @@ -664,7 +673,7 @@ String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName() + " where REGION = '" + this.getCacheName() + "' and IS_ETERNAL = 'F' and " + now + " > SYSTEM_EXPIRE_TIME_SECONDS"; - Connection con = DriverManager.getConnection( getPoolUrl() ); + Connection con = poolAccess.getConnection(); Statement sStatement = null; try { @@ -696,9 +705,13 @@ } catch ( Exception e ) { - log.error( "Problem removing all.", e ); + log.error( "Problem removing expired elements from the table.", e ); reset(); } + finally + { + getTableState().setState( TableState.FREE ); + } return deleted; } @@ -717,7 +730,7 @@ { try { - shutdownDriver(); + poolAccess.shutdownDriver(); } catch ( Exception e ) { @@ -741,7 +754,7 @@ Connection con; try { - con = DriverManager.getConnection( getPoolUrl() ); + con = poolAccess.getConnection(); } catch ( SQLException e1 ) { @@ -844,168 +857,6 @@ return elementSerializer; } - /** - * @param connectURI - * @param userName - * @param password - * @param maxActive - * max connetions - * @throws Exception - */ - public void setupDriver( String connectURI, String userName, String password, int maxActive ) - throws Exception - { - // First, we'll need a ObjectPool that serves as the - // actual pool of connections. - // We'll use a GenericObjectPool instance, although - // any ObjectPool implementation will suffice. - ObjectPool connectionPool = new GenericObjectPool( null, maxActive ); - - // TODO make configurable - // By dfault the size is 8!!!!!!! - ( (GenericObjectPool) connectionPool ).setMaxIdle( -1 ); - - // Next, we'll create a ConnectionFactory that the - // pool will use to create Connections. - // We'll use the DriverManagerConnectionFactory, - // using the connect string passed in the command line - // arguments. - // Properties props = new Properties(); - // props.setProperty( "user", userName ); - // props.setProperty( "password", password ); - ConnectionFactory connectionFactory = new DriverManagerConnectionFactory( connectURI, userName, password ); - - // Now we'll create the PoolableConnectionFactory, which wraps - // the "real" Connections created by the ConnectionFactory with - // the classes that implement the pooling functionality. - // PoolableConnectionFactory poolableConnectionFactory = - new PoolableConnectionFactory( connectionFactory, connectionPool, null, null, false, true ); - - // Finally, we create the PoolingDriver itself... - Class.forName( "org.apache.commons.dbcp.PoolingDriver" ); - PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); - - // ...and register our pool with it. - driver.registerPool( this.getPoolName(), connectionPool ); - - // Now we can just use the connect string - // "jdbc:apache:commons:dbcp:jcs" - // to access our pool of Connections. - } - - /** - * @throws Exception - */ - public void logDriverStats() - throws Exception - { - PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); - ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() ); - - if ( connectionPool != null ) - { - if ( log.isDebugEnabled() ) - { - log.debug( connectionPool ); - } - - if ( log.isInfoEnabled() ) - { - log.info( "NumActive: " + getNumActiveInPool() ); - log.info( "NumIdle: " + getNumIdleInPool() ); - } - } - else - { - log.warn( "Could not find pool." ); - } - } - - /** - * How many are idle in the pool. - * @return - */ - public int getNumIdleInPool() - { - int numIdle = 0; - try - { - PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); - ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() ); - - if ( log.isDebugEnabled() ) - { - log.debug( connectionPool ); - } - numIdle = connectionPool.getNumIdle(); - } - catch ( Exception e ) - { - log.error( e ); - } - return numIdle; - } - - /** - * How many are active in the pool. - * @return - */ - public int getNumActiveInPool() - { - int numActive = 0; - try - { - PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); - ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() ); - - if ( log.isDebugEnabled() ) - { - log.debug( connectionPool ); - } - numActive = connectionPool.getNumActive(); - } - catch ( Exception e ) - { - log.error( e ); - } - return numActive; - } - - /** - * @throws Exception - */ - public void shutdownDriver() - throws Exception - { - PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); - driver.closePool( this.getPoolName() ); - } - - /** - * @return Returns the poolUrl. - */ - public String getPoolUrl() - { - return DRIVER_NAME + this.getPoolName(); - } - - /** - * @param poolName - * The poolName to set. - */ - public void setPoolName( String poolName ) - { - this.poolName = poolName; - } - - /** - * @return Returns the poolName. - */ - public String getPoolName() - { - return poolName; - } - /** safely increment */ private synchronized void incrementUpdateCount() { @@ -1065,12 +916,12 @@ se = new StatElement(); se.setName( "Active DB Connections" ); - se.setData( "" + getNumActiveInPool() ); + se.setData( "" + poolAccess.getNumActiveInPool() ); elems.add( se ); se = new StatElement(); se.setName( "Idle DB Connections" ); - se.setData( "" + getNumIdleInPool() ); + se.setData( "" + poolAccess.getNumIdleInPool() ); elems.add( se ); se = new StatElement(); @@ -1104,6 +955,22 @@ name = this.getJdbcDiskCacheAttributes().getTableName(); } return name; + } + + /** + * @param tableState The tableState to set. + */ + public void setTableState( TableState tableState ) + { + this.tableState = tableState; + } + + /** + * @return Returns the tableState. + */ + public TableState getTableState() + { + return tableState; } /** Modified: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java?rev=420957&r1=420956&r2=420957&view=diff ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java (original) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java Tue Jul 11 12:36:02 2006 @@ -40,11 +40,13 @@ private boolean testBeforeInsert = true; - private static final int DEFAULT_MAX_ACTIVE = 10; + /** This is the default limit on the maximum number of active connections. */ + public static final int DEFAULT_MAX_ACTIVE = 10; private int maxActive = DEFAULT_MAX_ACTIVE; - private static final int DEFAULT_SHRINKER_INTERVAL_SECONDS = 300; + /** This is the default setting for the cleanup routine. */ + public static final int DEFAULT_SHRINKER_INTERVAL_SECONDS = 300; private int shrinkerIntervalSeconds = DEFAULT_SHRINKER_INTERVAL_SECONDS; @@ -231,17 +233,16 @@ { StringBuffer buf = new StringBuffer(); buf.append( "\nJDBCCacheAttributes" ); - buf.append( "\nUserName [" + getUserName() + "]" ); - buf.append( "\nUrl [" + getUrl() + "]" ); - buf.append( "\nDatabase [" + getDatabase() + "]" ); - buf.append( "\nDriverClassName [" + getDriverClassName() + "]" ); - buf.append( "\nTableName [" + getTableName() + "]" ); - buf.append( "\nTestBeforeInsert [" + isTestBeforeInsert() + "]" ); - buf.append( "\nMaxActive [" + getMaxActive() + "]" ); - buf.append( "\nAllowRemoveAll [" + isAllowRemoveAll() + "]" ); - buf.append( "\nShrinkerIntervalSeconds [" + getShrinkerIntervalSeconds() + "]" ); - buf.append( "\nUseDiskShrinker [" + isUseDiskShrinker() + "]" ); + buf.append( "\n UserName [" + getUserName() + "]" ); + buf.append( "\n Url [" + getUrl() + "]" ); + buf.append( "\n Database [" + getDatabase() + "]" ); + buf.append( "\n DriverClassName [" + getDriverClassName() + "]" ); + buf.append( "\n TableName [" + getTableName() + "]" ); + buf.append( "\n TestBeforeInsert [" + isTestBeforeInsert() + "]" ); + buf.append( "\n MaxActive [" + getMaxActive() + "]" ); + buf.append( "\n AllowRemoveAll [" + isAllowRemoveAll() + "]" ); + buf.append( "\n ShrinkerIntervalSeconds [" + getShrinkerIntervalSeconds() + "]" ); + buf.append( "\n UseDiskShrinker [" + isUseDiskShrinker() + "]" ); return buf.toString(); } - } Modified: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java?rev=420957&r1=420956&r2=420957&view=diff ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java (original) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java Tue Jul 11 12:36:02 2006 @@ -11,17 +11,9 @@ * governing permissions and limitations under the License. */ -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Map; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jcs.auxiliary.AuxiliaryCache; -import org.apache.jcs.auxiliary.AuxiliaryCacheManager; - -import EDU.oswego.cs.dl.util.concurrent.ClockDaemon; -import EDU.oswego.cs.dl.util.concurrent.ThreadFactory; /** * This manages instances of the jdbc disk cache. It maintains one for each @@ -29,31 +21,15 @@ * by region. */ public class JDBCDiskCacheManager - implements AuxiliaryCacheManager + extends JDBCDiskCacheManagerAbstractTemplate { private static final long serialVersionUID = -8258856770927857896L; private static final Log log = LogFactory.getLog( JDBCDiskCacheManager.class ); - private static int clients; - - private static Hashtable caches = new Hashtable(); - private static JDBCDiskCacheManager instance; - private JDBCDiskCacheAttributes defaultCattr; - - /** - * The background disk shrinker, one for all regions. - */ - private ClockDaemon shrinkerDaemon; - - /** - * A map of table name to shrinker threads. This allows each table to have a - * different setting. It assumes that there is only one jdbc disk cache - * auxiliary defined per table. - */ - private Map shrinkerThreadMap = new Hashtable(); + private JDBCDiskCacheAttributes defaultJDBCDiskCacheAttributes; /** * Constructor for the HSQLCacheManager object @@ -66,7 +42,7 @@ { log.info( "Creating JDBCDiskCacheManager with " + cattr ); } - defaultCattr = cattr; + defaultJDBCDiskCacheAttributes = cattr; } /** @@ -74,9 +50,9 @@ *

* @return The defaultCattr value */ - public JDBCDiskCacheAttributes getDefaultCattr() + public JDBCDiskCacheAttributes getDefaultJDBCDiskCacheAttributes() { - return defaultCattr; + return defaultJDBCDiskCacheAttributes; } /** @@ -94,7 +70,6 @@ instance = new JDBCDiskCacheManager( cattr ); } } - clients++; return instance; } @@ -107,131 +82,21 @@ */ public AuxiliaryCache getCache( String cacheName ) { - JDBCDiskCacheAttributes cattr = (JDBCDiskCacheAttributes) defaultCattr.copy(); + JDBCDiskCacheAttributes cattr = (JDBCDiskCacheAttributes) defaultJDBCDiskCacheAttributes.copy(); cattr.setCacheName( cacheName ); return getCache( cattr ); } /** - * Gets the cache attribute of the HSQLCacheManager object + * Creates a JDBCDiskCache using the supplied attributes. *

* @param cattr - * @return The cache value + * @return */ - public AuxiliaryCache getCache( JDBCDiskCacheAttributes cattr ) + protected AuxiliaryCache createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState ) { - AuxiliaryCache raf = null; - - log.debug( "cacheName = " + cattr.getCacheName() ); - - synchronized ( caches ) - { - raf = (AuxiliaryCache) caches.get( cattr.getCacheName() ); - - if ( raf == null ) - { - raf = new JDBCDiskCache( cattr ); - caches.put( cattr.getCacheName(), raf ); - } - } - - if ( log.isDebugEnabled() ) - { - log.debug( "JDBC cache = " + raf ); - } - - // add cache to shrinker. - if ( cattr.isUseDiskShrinker() ) - { - if ( shrinkerDaemon == null ) - { - shrinkerDaemon = new ClockDaemon(); - shrinkerDaemon.setThreadFactory( new MyThreadFactory() ); - } - - ShrinkerThread shrinkerThread = (ShrinkerThread) shrinkerThreadMap.get( cattr.getTableName() ); - if ( shrinkerThread == null ) - { - shrinkerThread = new ShrinkerThread(); - shrinkerThreadMap.put( cattr.getTableName(), shrinkerThread ); - - long intervalMillis = Math.max( 999, cattr.getShrinkerIntervalSeconds() * 1000 ); - if ( log.isInfoEnabled() ) - { - log.info( "Setting the shrinker to run every [" + intervalMillis + "] ms. for table [" - + cattr.getTableName() + "]" ); - } - shrinkerDaemon.executePeriodically( intervalMillis, shrinkerThread, false ); - } - shrinkerThread.addDiskCacheToShrinkList( (JDBCDiskCache) raf ); - } - + AuxiliaryCache raf; + raf = new JDBCDiskCache( cattr, tableState ); return raf; - } - - /** - * @param name - */ - public void freeCache( String name ) - { - JDBCDiskCache raf = (JDBCDiskCache) caches.get( name ); - if ( raf != null ) - { - raf.dispose(); - } - } - - /** - * Gets the cacheType attribute of the HSQLCacheManager object - *

- * @return The cacheType value - */ - public int getCacheType() - { - return DISK_CACHE; - } - - /** Disposes of all regions. */ - public void release() - { - // Wait until called by the last client - if ( --clients != 0 ) - { - return; - } - synchronized ( caches ) - { - Enumeration allCaches = caches.elements(); - - while ( allCaches.hasMoreElements() ) - { - JDBCDiskCache raf = (JDBCDiskCache) allCaches.nextElement(); - if ( raf != null ) - { - raf.dispose(); - } - } - } - } - - /** - * Allows us to set the daemon status on the clockdaemon - *

- * @author aaronsm - */ - class MyThreadFactory - implements ThreadFactory - { - /* - * (non-Javadoc) - * @see EDU.oswego.cs.dl.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) - */ - public Thread newThread( Runnable runner ) - { - Thread t = new Thread( runner ); - t.setDaemon( true ); - t.setPriority( Thread.MIN_PRIORITY ); - return t; - } } } Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,220 @@ +package org.apache.jcs.auxiliary.disk.jdbc; + +/* + * Copyright 2001-2004 The Apache Software Foundation. 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. + */ + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jcs.auxiliary.AuxiliaryCache; +import org.apache.jcs.auxiliary.AuxiliaryCacheManager; + +import EDU.oswego.cs.dl.util.concurrent.ClockDaemon; +import EDU.oswego.cs.dl.util.concurrent.ThreadFactory; + +/** + * This class serves as an abstract template for JDBCDiskCache Manager. The + * MySQL JDBC Disk Cache needs many of the same features as the generic maanger. + *

+ * @author Aaron Smuts + */ +public abstract class JDBCDiskCacheManagerAbstractTemplate + implements AuxiliaryCacheManager +{ + private static final Log log = LogFactory.getLog( JDBCDiskCacheManagerAbstractTemplate.class ); + + /** + * Incremented on getIntance, decremented on release. + */ + protected static int clients; + + /** + * A map of JDBCDiskCache objects to region names. + */ + protected static Hashtable caches = new Hashtable(); + + /** + * A map of TableState objects to table names. Each cache has a table state + * object, which is used to determin if any long processes such as deletes + * or optimizations are running. + */ + protected static Hashtable tableStates = new Hashtable(); + + /** + * The background disk shrinker, one for all regions. + */ + private ClockDaemon shrinkerDaemon; + + /** + * A map of table name to shrinker threads. This allows each table to have a + * different setting. It assumes that there is only one jdbc disk cache + * auxiliary defined per table. + */ + private Map shrinkerThreadMap = new Hashtable(); + + /** + * Children must implement this method. + *

+ * @param cattr + * @param tableState An object used by multiple processes to indicate state. + * @return AuxiliaryCache -- a JDBCDiskCache + */ + protected abstract AuxiliaryCache createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState ); + + /** + * Creates a JDBCDiskCache for the region if one doesn't exist, else it + * returns the precreated instance. It also adds the region to the shrinker + * thread if needed. + *

+ * @param cattr + * @return The cache value + */ + public AuxiliaryCache getCache( JDBCDiskCacheAttributes cattr ) + { + AuxiliaryCache diskCache = null; + + log.debug( "cacheName = " + cattr.getCacheName() ); + + synchronized ( caches ) + { + diskCache = (AuxiliaryCache) caches.get( cattr.getCacheName() ); + + if ( diskCache == null ) + { + TableState tableState = (TableState)tableStates.get( cattr.getTableName() ); + + if ( tableState == null ) + { + tableState = new TableState( cattr.getTableName() ); + } + + diskCache = createJDBCDiskCache( cattr, tableState ); + + caches.put( cattr.getCacheName(), diskCache ); + } + } + + if ( log.isDebugEnabled() ) + { + log.debug( "JDBC cache = " + diskCache ); + } + + // create a shrinker if we need it. + createShrinkerWhenNeeded( cattr, diskCache ); + + return diskCache; + } + + /** + * If UseDiskShrinker is true then we will create a shrinker daemon if + * necessary. + *

+ * @param cattr + * @param raf + */ + protected void createShrinkerWhenNeeded( JDBCDiskCacheAttributes cattr, AuxiliaryCache raf ) + { + // add cache to shrinker. + if ( cattr.isUseDiskShrinker() ) + { + if ( shrinkerDaemon == null ) + { + shrinkerDaemon = new ClockDaemon(); + shrinkerDaemon.setThreadFactory( new MyThreadFactory() ); + } + + ShrinkerThread shrinkerThread = (ShrinkerThread) shrinkerThreadMap.get( cattr.getTableName() ); + if ( shrinkerThread == null ) + { + shrinkerThread = new ShrinkerThread(); + shrinkerThreadMap.put( cattr.getTableName(), shrinkerThread ); + + long intervalMillis = Math.max( 999, cattr.getShrinkerIntervalSeconds() * 1000 ); + if ( log.isInfoEnabled() ) + { + log.info( "Setting the shrinker to run every [" + intervalMillis + "] ms. for table [" + + cattr.getTableName() + "]" ); + } + shrinkerDaemon.executePeriodically( intervalMillis, shrinkerThread, false ); + } + shrinkerThread.addDiskCacheToShrinkList( (JDBCDiskCache) raf ); + } + } + + /** + * @param name + */ + public void freeCache( String name ) + { + JDBCDiskCache raf = (JDBCDiskCache) caches.get( name ); + if ( raf != null ) + { + raf.dispose(); + } + } + + /** + * Gets the cacheType attribute of the HSQLCacheManager object + *

+ * @return The cacheType value + */ + public int getCacheType() + { + return DISK_CACHE; + } + + /** Disposes of all regions. */ + public void release() + { + // Wait until called by the last client + if ( --clients != 0 ) + { + return; + } + synchronized ( caches ) + { + Enumeration allCaches = caches.elements(); + + while ( allCaches.hasMoreElements() ) + { + JDBCDiskCache raf = (JDBCDiskCache) allCaches.nextElement(); + if ( raf != null ) + { + raf.dispose(); + } + } + } + } + + /** + * Allows us to set the daemon status on the clockdaemon + *

+ * @author aaronsm + */ + class MyThreadFactory + implements ThreadFactory + { + /* + * (non-Javadoc) + * @see EDU.oswego.cs.dl.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) + */ + public Thread newThread( Runnable runner ) + { + Thread t = new Thread( runner ); + t.setDaemon( true ); + t.setPriority( Thread.MIN_PRIORITY ); + return t; + } + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,250 @@ +package org.apache.jcs.auxiliary.disk.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.apache.commons.dbcp.ConnectionFactory; +import org.apache.commons.dbcp.DriverManagerConnectionFactory; +import org.apache.commons.dbcp.PoolableConnectionFactory; +import org.apache.commons.dbcp.PoolingDriver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.ObjectPool; +import org.apache.commons.pool.impl.GenericObjectPool; + +/** + * This class provides access to the connection pool. It ensures that the + * various resources that need to access the tables will be able to use the same + * pool. + *

+ * @author Aaron Smuts + */ +public class JDBCDiskCachePoolAccess +{ + private final static Log log = LogFactory.getLog( JDBCDiskCachePoolAccess.class ); + + /** The defualt Pool Name to which the connetion pool will be keyed. */ + public static final String DEFAULT_POOL_NAME = "jcs"; + + private String poolName = DEFAULT_POOL_NAME; + + private static final String DRIVER_NAME = "jdbc:apache:commons:dbcp:"; + + // WE SHOULD HAVE A DIFFERENT POOL FOR EACH DB NO REGION + // THE SAME TABLE CAN BE USED BY MULTIPLE REGIONS + // this.setPoolName( jdbcDiskCacheAttributes.getCacheName() ); + + /** + * Configures the pool name to use for the pool access. + *

+ * This pool name should be unique to the database. It is used as part of + * the URL each time we lookup a conection from the driver manager. + *

+ * @param poolName + * @param driverName + */ + public JDBCDiskCachePoolAccess( String poolName ) + { + // we can default to jcs if there is only one database in use. + if ( poolName != null ) + { + setPoolName( poolName ); + } + else + { + if ( log.isInfoEnabled() ) + { + log.info( "The pool name supplied was null. Using default instead." ); + } + } + } + + + /** + * Gets a connection from the pool. + *

+ * @return Connection + * @throws SQLException + */ + public Connection getConnection() + throws SQLException + { + Connection con; + try + { + con = DriverManager.getConnection( getPoolUrl() ); + } + catch ( SQLException e ) + { + log.error( "Problem getting conenction.", e ); + throw e; + } + + return con; + } + + /** + * How many are idle in the pool. + *

+ * @return + */ + public int getNumIdleInPool() + { + int numIdle = 0; + try + { + PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); + ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() ); + + if ( log.isDebugEnabled() ) + { + log.debug( connectionPool ); + } + numIdle = connectionPool.getNumIdle(); + } + catch ( Exception e ) + { + log.error( e ); + } + return numIdle; + } + + /** + * How many are active in the pool. + *

+ * @return + */ + public int getNumActiveInPool() + { + int numActive = 0; + try + { + PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); + ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() ); + + if ( log.isDebugEnabled() ) + { + log.debug( connectionPool ); + } + numActive = connectionPool.getNumActive(); + } + catch ( Exception e ) + { + log.error( e ); + } + return numActive; + } + + /** + * @throws Exception + */ + public void shutdownDriver() + throws Exception + { + PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); + driver.closePool( this.getPoolName() ); + } + + /** + * @return Returns the poolUrl. + */ + public String getPoolUrl() + { + return DRIVER_NAME + this.getPoolName(); + } + + /** + * @param poolName + * The poolName to set. + */ + public void setPoolName( String poolName ) + { + this.poolName = poolName; + } + + /** + * @return Returns the poolName. + */ + public String getPoolName() + { + return poolName; + } + + /** + * @param connectURI + * @param userName + * @param password + * @param maxActive + * max connetions + * @throws Exception + */ + public void setupDriver( String connectURI, String userName, String password, int maxActive ) + throws Exception + { + // First, we'll need a ObjectPool that serves as the + // actual pool of connections. + // We'll use a GenericObjectPool instance, although + // any ObjectPool implementation will suffice. + ObjectPool connectionPool = new GenericObjectPool( null, maxActive ); + + // TODO make configurable + // By dfault the size is 8!!!!!!! + ( (GenericObjectPool) connectionPool ).setMaxIdle( -1 ); + + // Next, we'll create a ConnectionFactory that the + // pool will use to create Connections. + // We'll use the DriverManagerConnectionFactory, + // using the connect string passed in the command line + // arguments. + // Properties props = new Properties(); + // props.setProperty( "user", userName ); + // props.setProperty( "password", password ); + ConnectionFactory connectionFactory = new DriverManagerConnectionFactory( connectURI, userName, password ); + + // Now we'll create the PoolableConnectionFactory, which wraps + // the "real" Connections created by the ConnectionFactory with + // the classes that implement the pooling functionality. + // PoolableConnectionFactory poolableConnectionFactory = + new PoolableConnectionFactory( connectionFactory, connectionPool, null, null, false, true ); + + // Finally, we create the PoolingDriver itself... + Class.forName( "org.apache.commons.dbcp.PoolingDriver" ); + PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); + + // ...and register our pool with it. + driver.registerPool( this.getPoolName(), connectionPool ); + + // Now we can just use the connect string + // "jdbc:apache:commons:dbcp:jcs" + // to access our pool of Connections. + } + + /** + * @throws Exception + */ + public void logDriverStats() + throws Exception + { + PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME ); + ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() ); + + if ( connectionPool != null ) + { + if ( log.isDebugEnabled() ) + { + log.debug( connectionPool ); + } + + if ( log.isInfoEnabled() ) + { + log.info( "NumActive: " + getNumActiveInPool() ); + log.info( "NumIdle: " + getNumIdleInPool() ); + } + } + else + { + log.warn( "Could not find pool." ); + } + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,86 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql; + +import java.io.Serializable; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCache; +import org.apache.jcs.auxiliary.disk.jdbc.TableState; +import org.apache.jcs.engine.behavior.ICacheElement; + +/** + * The MySQLDiskCache extends the core JDBCDiskCache. + *

+ * Although the generic JDBC Disk Cache can be used for MySQL, the MySQL JDBC + * Disk Cache has additional features, such as table optimization that are + * particular to MySQL. + *

+ * @author Aaron Smuts + */ +public class MySQLDiskCache + extends JDBCDiskCache +{ + private static final long serialVersionUID = -7169488308515823491L; + + private final static Log log = LogFactory.getLog( MySQLDiskCache.class ); + + MySQLDiskCacheAttributes mySQLDiskCacheAttributes; + + /** + * Delegates to the super and makes use of the MySQL specific parameters + * used for scheduled optimization. + *

+ * @param attributes + * @param tableState + */ + public MySQLDiskCache( MySQLDiskCacheAttributes attributes, TableState tableState ) + { + super( attributes, tableState ); + + mySQLDiskCacheAttributes = attributes; + + if ( log.isDebugEnabled() ) + { + log.debug( "MySQLDiskCacheAttributes = " + attributes ); + } + } + + /** + * This delegates to the generic JDBC disk cache. If we are currently + * optimizing, then this method will balk and return null. + *

+ * @param key + * Key to locate value for. + * @return An object matching key, or null. + */ + public ICacheElement doGet( Serializable key ) + { + if ( this.getTableState().getState() == TableState.OPTIMIZATION_RUNNING ) + { + if ( this.mySQLDiskCacheAttributes.isBalkDuringOptimization() ) + { + return null; + } + } + return super.doGet( key ); + } + + /** + * This delegates to the generic JDBC disk cache. If we are currently + * optimizing, then this method will balk and do nothing. A + *

+ * @param element + */ + public void doUpdate( ICacheElement element ) + { + if ( this.getTableState().getState() == TableState.OPTIMIZATION_RUNNING ) + { + if ( this.mySQLDiskCacheAttributes.isBalkDuringOptimization() ) + { + return; + } + } + super.doUpdate( element ); + } + +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,84 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql; + +import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes; + +/** + * This has additional attributes that are particular to the MySQL disk cache. + *

+ * @author Aaron Smuts + */ +public class MySQLDiskCacheAttributes + extends JDBCDiskCacheAttributes +{ + private static final long serialVersionUID = -6535808344813320061L; + + /** + * For now this is a simpel comma delimited list of HH:MM times to optimize + * the table. If none is supplied, then no optimizations will be performed. + *

+ * In the future we can add a chron like scheduling system. This is to meet + * a pressing current need. + *

+ * 03:01,15:00 will cause the optimizer to run at 3 am and at 3 pm. + */ + private String optimizationSchedule = null; + + /** + * If true, we will balk, that is return null during optimization rather than block. + */ + public static final boolean DEFAULT_BALK_DURING_OPTIMIZATION = true; + + /** + * If true, we will balk, that is return null during optimization rather than block. + *

+ * Balking + */ + private boolean balkDuringOptimization = DEFAULT_BALK_DURING_OPTIMIZATION; + + /** + * @param optimizationSchedule The optimizationSchedule to set. + */ + public void setOptimizationSchedule( String optimizationSchedule ) + { + this.optimizationSchedule = optimizationSchedule; + } + + /** + * @return Returns the optimizationSchedule. + */ + public String getOptimizationSchedule() + { + return optimizationSchedule; + } + + /** + * @param balkDuringOptimization The balkDuringOptimization to set. + */ + public void setBalkDuringOptimization( boolean balkDuringOptimization ) + { + this.balkDuringOptimization = balkDuringOptimization; + } + + /** + * Should we return null while optimizing the table. + *

+ * @return Returns the balkDuringOptimization. + */ + public boolean isBalkDuringOptimization() + { + return balkDuringOptimization; + } + + /** + * For debugging. + */ + public String toString() + { + StringBuffer buf = new StringBuffer(); + buf.append( "\nMySQLDiskCacheAttributes" ); + buf.append( "\n OptimizationSchedule [" + getOptimizationSchedule() + "]" ); + buf.append( "\n BalkDuringOptimization [" + isBalkDuringOptimization() + "]" ); + buf.append( super.toString() ); + return buf.toString(); + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,53 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql; + +/* + * Copyright 2001-2004 The Apache Software Foundation. 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. + */ + +import org.apache.jcs.auxiliary.AuxiliaryCache; +import org.apache.jcs.auxiliary.AuxiliaryCacheAttributes; +import org.apache.jcs.auxiliary.AuxiliaryCacheFactory; +import org.apache.jcs.engine.behavior.ICompositeCacheManager; + +/** + * This factory should create mysql disk caches. + *

+ * @author Aaron Smuts + */ +public class MySQLDiskCacheFactory + implements AuxiliaryCacheFactory +{ + private String name = "JDBCDiskCacheFactory"; + + /** + * This factory method should create an instance of the mysqlcache. + */ + public AuxiliaryCache createCache( AuxiliaryCacheAttributes rawAttr, ICompositeCacheManager arg1 ) + { + MySQLDiskCacheManager mgr = MySQLDiskCacheManager.getInstance( (MySQLDiskCacheAttributes) rawAttr ); + return mgr.getCache( (MySQLDiskCacheAttributes) rawAttr ); + } + + /** + * The name of the factory. + */ + public void setName( String nameArg ) + { + name = nameArg; + } + + /** + * Returns the display name + */ + public String getName() + { + return name; + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,249 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql; + +/* + * Copyright 2001-2004 The Apache Software Foundation. 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. + */ + +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jcs.auxiliary.AuxiliaryCache; +import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes; +import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheManagerAbstractTemplate; +import org.apache.jcs.auxiliary.disk.jdbc.TableState; +import org.apache.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleFormatException; +import org.apache.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleParser; + +/** + * This manages instances of the MySQL jdbc disk cache. It maintains one for + * each region. One for all regions would work, but this gives us more detailed + * stats by region. + *

+ * Although the generic JDBC Disk Cache Manager can be used for MySQL, the MySQL + * JDBC Disk Cache has additional features, such as table optimization that are + * particular to MySQL. + */ +public class MySQLDiskCacheManager + extends JDBCDiskCacheManagerAbstractTemplate +{ + private static final long serialVersionUID = -8258856770927857896L; + + private static final Log log = LogFactory.getLog( MySQLDiskCacheManager.class ); + + private static MySQLDiskCacheManager instance; + + private MySQLDiskCacheAttributes defaultJDBCDiskCacheAttributes; + + // ms in a day + private static final int DAILY_INTERVAL = 60 * 60 * 24 * 1000; + + // for schedule optimizations + private Timer daemon = null; + + /** + * Constructor for the HSQLCacheManager object + *

+ * @param cattr + */ + private MySQLDiskCacheManager( MySQLDiskCacheAttributes cattr ) + { + if ( log.isInfoEnabled() ) + { + log.info( "Creating MySQLDiskCacheManager with " + cattr ); + } + defaultJDBCDiskCacheAttributes = cattr; + } + + /** + * Gets the defaultCattr attribute of the HSQLCacheManager object + *

+ * @return The defaultCattr value + */ + public MySQLDiskCacheAttributes getDefaultJDBCDiskCacheAttributes() + { + return defaultJDBCDiskCacheAttributes; + } + + /** + * Gets the instance attribute of the HSQLCacheManager class + *

+ * @param cattr + * @return The instance value + */ + public static MySQLDiskCacheManager getInstance( MySQLDiskCacheAttributes cattr ) + { + synchronized ( MySQLDiskCacheManager.class ) + { + if ( instance == null ) + { + instance = new MySQLDiskCacheManager( cattr ); + } + } + clients++; + return instance; + } + + /** + * Gets the cache attribute of the HSQLCacheManager object + *

+ * @param cacheName + * @return The cache value + */ + public AuxiliaryCache getCache( String cacheName ) + { + MySQLDiskCacheAttributes cattr = (MySQLDiskCacheAttributes) defaultJDBCDiskCacheAttributes.copy(); + cattr.setCacheName( cacheName ); + return getCache( cattr ); + } + + /** + * Creates a JDBCDiskCache using the supplied attributes. + *

+ * @param cattr + * @return + */ + protected AuxiliaryCache createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState ) + { + AuxiliaryCache raf = new MySQLDiskCache( (MySQLDiskCacheAttributes) cattr, tableState ); + + scheduleOptimizations( (MySQLDiskCacheAttributes) cattr, tableState ); + + return raf; + } + + /** + * For each time in the optimization schedule, this calls schedule + * Optimizaiton. + *

+ * @param attributes + * @param tableState + */ + protected void scheduleOptimizations( MySQLDiskCacheAttributes attributes, TableState tableState ) + { + if ( attributes != null ) + { + if ( attributes.getOptimizationSchedule() != null ) + { + if ( log.isInfoEnabled() ) + { + log.info( "Will try to configure optimization for table [" + attributes.getTableName() + + "] on schdule [" + attributes.getOptimizationSchedule() + "]" ); + } + + MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState ); + + // loop through the dates. + try + { + Date[] dates = ScheduleParser.createDatesForSchedule( attributes.getOptimizationSchedule() ); + if ( dates != null ) + { + for ( int i = 0; i < dates.length; i++ ) + { + this.scheduleOptimization( dates[i], optimizer ); + } + } + } + catch ( ScheduleFormatException e ) + { + log.warn( "Problem creating optimization schedule for table [" + attributes.getTableName() + "]" ); + } + } + else + { + if ( log.isInfoEnabled() ) + { + log.info( "Optimization is not configured for table [" + attributes.getTableName() + "]" ); + } + } + } + } + + /** + * This takes in a single time and schedules the optimizer to be called at + * that time every day. + *

+ * @param startTime -- + * HH:MM:SS format + * @param optimizer + */ + protected void scheduleOptimization( Date startTime, MySQLTableOptimizer optimizer ) + { + if ( log.isInfoEnabled() ) + { + log.info( "startTime [" + startTime + "] for optimizer " + optimizer ); + } + + // create clock daemon if necessary + if ( daemon == null ) + { + // true for daemon status + daemon = new Timer( true ); + } + + // get the runnable from the factory + TimerTask runnable = new OptimizerTask( optimizer ); + + // have the daemon execut our runnable + // false to not execute immediately. + daemon.scheduleAtFixedRate( runnable, startTime, DAILY_INTERVAL ); + + if ( log.isInfoEnabled() ) + { + log.info( "Scheduled optimization to begin at [" + startTime + "]" ); + } + } + + /** + * This calls the optimizers' optimize table method. This is used by the + * timer. + *

+ * @author Aaron Smuts + */ + private class OptimizerTask + extends TimerTask + { + private MySQLTableOptimizer optimizer = null; + + /** + * Get a handle on the optimizer. + *

+ * @param optimizer + */ + public OptimizerTask( MySQLTableOptimizer optimizer ) + { + this.optimizer = optimizer; + } + + /** + * This calls optimize on the optimizer. + *

+ * @see java.lang.Runnable#run() + */ + public void run() + { + if ( optimizer != null ) + { + boolean success = optimizer.optimizeTable(); + if ( log.isInfoEnabled() ) + { + log.info( "Optimization success status [" + success + "]" ); + } + } + else + { + log.warn( "OptimizerRunner: The optimizer is null. Could not optimize table." ); + } + } + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,332 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes; +import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCachePoolAccess; +import org.apache.jcs.auxiliary.disk.jdbc.TableState; + +/** + * The MySQL Table Optimizer can optimize MySQL tables. It knows how to optimize + * for MySQL datbases in particular and how to repari the table if it is + * corrupted in the process. + *

+ * We will probably be able to abstract out a generic optimizer interface from + * this class in the future. + *

+ * @author Aaron Smuts + */ +public class MySQLTableOptimizer +{ + private final static Log log = LogFactory.getLog( MySQLTableOptimizer.class ); + + private JDBCDiskCachePoolAccess poolAccess = null; + + private String tableName = null; + + private TableState tableState; + + /** + * This constructs an optimizer with the disk cacn properties. + *

+ * @param attributes + * @param tableState + * We mark the table status as optimizing when this is happening. + */ + public MySQLTableOptimizer( MySQLDiskCacheAttributes attributes, TableState tableState ) + { + setTableName( attributes.getTableName() ); + + this.tableState = tableState; + /** + * This initializes the pool access. + */ + initializePoolAccess( attributes ); + } + + /** + * Register the driver and create a pool. + *

+ * @param cattr + */ + protected void initializePoolAccess( JDBCDiskCacheAttributes cattr ) + { + try + { + try + { + // org.gjt.mm.mysql.Driver + Class.forName( cattr.getDriverClassName() ); + } + catch ( ClassNotFoundException e ) + { + log.error( "Couldn't find class for driver [" + cattr.getDriverClassName() + "]", e ); + } + + poolAccess = new JDBCDiskCachePoolAccess( cattr.getName() ); + + poolAccess.setupDriver( cattr.getUrl() + cattr.getDatabase(), cattr.getUserName(), cattr.getPassword(), + cattr.getMaxActive() ); + } + catch ( Exception e ) + { + log.error( "Problem getting connection.", e ); + } + } + + /** + * A scheduler will call this method. When it is called the table state is + * marked as optimizing. TODO we need to verify that no deletions are + * running before we call optimize. We should wait if a deletion is in + * progress. + *

+ * This restores when there is an optimization error. The error output looks + * like this: + * + *

+     *           mysql> optimize table JCS_STORE_FLIGHT_OPTION_ITINERARY;
+     *               +---------------------------------------------+----------+----------+---------------------+
+     *               | Table                                       | Op       | Msg_type | Msg_text            |
+     *               +---------------------------------------------+----------+----------+---------------------+
+     *               | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | optimize | error    | 2 when fixing table |
+     *               | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | optimize | status   | Operation failed    |
+     *               +---------------------------------------------+----------+----------+---------------------+
+     *               2 rows in set (51.78 sec)
+     * 
+ * + * A successful repair response looks like this: + * + *
+     *        mysql> REPAIR TABLE JCS_STORE_FLIGHT_OPTION_ITINERARY;
+     *            +---------------------------------------------+--------+----------+----------------------------------------------+
+     *            | Table                                       | Op     | Msg_type | Msg_text                                     |
+     *            +---------------------------------------------+--------+----------+----------------------------------------------+
+     *            | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | repair | error    | 2 when fixing table                          |
+     *            | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | repair | warning  | Number of rows changed from 131276 to 260461 |
+     *            | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | repair | status   | OK                                           |
+     *            +---------------------------------------------+--------+----------+----------------------------------------------+
+     *            3 rows in set (3 min 5.94 sec)
+     * 
+ * + * A successful optimization looks like this: + * + *
+     *       mysql> optimize table JCS_STORE_DEFAULT;
+     *           +-----------------------------+----------+----------+----------+
+     *           | Table                       | Op       | Msg_type | Msg_text |
+     *           +-----------------------------+----------+----------+----------+
+     *           | jcs_cache.JCS_STORE_DEFAULT | optimize | status   | OK       |
+     *           +-----------------------------+----------+----------+----------+
+     *           1 row in set (1.10 sec)
+     * 
+ * + * @return + */ + public boolean optimizeTable() + { + long start = System.currentTimeMillis(); + boolean success = false; + + if ( tableState.getState() == TableState.OPTIMIZATION_RUNNING ) + { + log + .warn( "Skipping optimization. Optimize was called, but the table state indicates that an optimization is currently running." ); + return false; + } + + try + { + tableState.setState( TableState.OPTIMIZATION_RUNNING ); + if ( log.isInfoEnabled() ) + { + log.debug( "Optimizing table [" + this.getTableName() + "]" ); + } + + Connection con; + try + { + con = poolAccess.getConnection(); + } + catch ( SQLException e ) + { + log.error( "Problem getting connection.", e ); + return false; + } + + try + { + // TEST + Statement sStatement = null; + try + { + sStatement = con.createStatement(); + + ResultSet rs = sStatement.executeQuery( "optimize table " + this.getTableName() ); + + // first row is error, then status + // if there is only one row in the result set, everything + // should be fine. + // This may be mysql version specific. + if ( rs.next() ) + { + String status = rs.getString( "Msg_type" ); + String message = rs.getString( "Msg_text" ); + + if ( log.isInfoEnabled() ) + { + log.info( "Message Type: " + status ); + log.info( "Message: " + message ); + } + + if ( "error".equals( status ) ) + { + log.warn( "Optimization was in erorr. Will attempt to repair the table. Message: " + + message ); + + // try to repair the table. + success = repairTable( sStatement ); + } + } + + // log the table status + String statusString = getTableStatus( sStatement ); + if ( log.isInfoEnabled() ) + { + log.info( "Table status after optimizing table [" + this.getTableName() + "]\n" + statusString ); + } + } + catch ( SQLException e ) + { + log.error( "Problem optimizing table [" + this.getTableName() + "]", e ); + return false; + } + finally + { + try + { + sStatement.close(); + } + catch ( SQLException e ) + { + log.error( "Problem closing statement.", e ); + } + } + } + finally + { + try + { + con.close(); + } + catch ( SQLException e ) + { + log.error( "Problem closing connection.", e ); + } + } + } + finally + { + tableState.setState( TableState.FREE ); + + long end = System.currentTimeMillis(); + if ( log.isInfoEnabled() ) + { + log.info( "Optimization of table [" + this.getTableName() + "] took " + ( end - start ) + " ms." ); + } + } + + return success; + } + + /** + * This calls show table status and returns the result as a String. + *

+ * @param sStatement + * @return String + * @throws SQLException + */ + protected String getTableStatus( Statement sStatement ) + throws SQLException + { + ResultSet statusResultSet = sStatement.executeQuery( "show table status" ); + StringBuffer statusString = new StringBuffer(); + int numColumns = statusResultSet.getMetaData().getColumnCount(); + while ( statusResultSet.next() ) + { + statusString.append( "\n" ); + for ( int i = 1; i <= numColumns; i++ ) + { + statusString.append( statusResultSet.getMetaData().getColumnLabel( i ) + " [" + + statusResultSet.getString( i ) + "] | " ); + } + } + return statusString.toString(); + } + + /** + * This is called if the optimizatio is in error. + *

+ * It looks for "OK" in response. If it find "OK" as a message in any result + * set row, it returns true. Otherwise we assume that the repair failed. + *

+ * @param sStatement + * @return true if successful + * @throws SQLException + */ + protected boolean repairTable( Statement sStatement ) + throws SQLException + { + boolean success = false; + + // if( message != null && message.indexOf( ) ) + ResultSet repairResult = sStatement.executeQuery( "repair table " + this.getTableName() ); + StringBuffer repairString = new StringBuffer(); + int numColumns = repairResult.getMetaData().getColumnCount(); + while ( repairResult.next() ) + { + for ( int i = 1; i <= numColumns; i++ ) + { + repairString.append( repairResult.getMetaData().getColumnLabel( i ) + " [" + repairResult.getString( i ) + + "] | " ); + } + + String message = repairResult.getString( "Msg_text" ); + if ( "OK".equals( message ) ) + { + success = true; + } + } + if ( log.isInfoEnabled() ) + { + log.info( repairString ); + } + + if ( !success ) + { + log.warn( "Failed to repair the table. " + repairString ); + } + return success; + } + + /** + * @param tableName + * The tableName to set. + */ + public void setTableName( String tableName ) + { + this.tableName = tableName; + } + + /** + * @return Returns the tableName. + */ + public String getTableName() + { + return tableName; + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,20 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql.util; + +/** + * This is thrown internally by the schedule parser. + *

+ * @author Aaron Smuts + */ +public class ScheduleFormatException + extends Exception +{ + private static final long serialVersionUID = 1L; + + /** + * @param message + */ + public ScheduleFormatException( String message ) + { + super( message ); + } +} Added: jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java URL: http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java?rev=420957&view=auto ============================================================================== --- jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java (added) +++ jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java Tue Jul 11 12:36:02 2006 @@ -0,0 +1,92 @@ +package org.apache.jcs.auxiliary.disk.jdbc.mysql.util; + +import java.util.Calendar; +import java.util.Date; +import java.util.StringTokenizer; + +/** + * Parses the very simple schedule format. + *

+ * @author Aaron Smuts + */ +public class ScheduleParser +{ + /** + * For each date time that is separated by a comma in the + * OptimizationSchedule, create a date and add it to an array of dates. + *

+ * @param schedule + * @return Date[] + * @throws ScheduleFormatException + */ + public static Date[] createDatesForSchedule( String schedule ) + throws ScheduleFormatException + { + if ( schedule == null ) + { + throw new ScheduleFormatException( "Cannot create schedules for a null String." ); + } + + StringTokenizer toker = new StringTokenizer( schedule, "," ); + Date[] dates = new Date[toker.countTokens()]; + int cnt = 0; + while ( toker.hasMoreTokens() ) + { + String time = toker.nextToken(); + dates[cnt] = getDateForSchedule( time ); + cnt++; + } + return dates; + } + + /** + * For a single string it creates a date that is the next time this hh:mm:ss + * combo will be seen. + *

+ * @param startTime + * @return + * @throws ScheduleFormatException + */ + public static Date getDateForSchedule( String startTime ) + throws ScheduleFormatException + { + if ( startTime == null ) + { + throw new ScheduleFormatException( "Cannot create date for a null String." ); + } + + int firstColon = startTime.indexOf( ":" ); + int lastColon = startTime.lastIndexOf( ":" ); + int len = startTime.length(); + if ( firstColon == -1 || lastColon == -1 || firstColon == lastColon || lastColon == len ) + { + String message = "StartTime [" + startTime + "] is deformed. Unable to schedule optimizaiton."; + throw new ScheduleFormatException( message ); + } + + Calendar cal = Calendar.getInstance(); + try + { + int hour = Integer.parseInt( startTime.substring( 0, firstColon ) ); + cal.set( Calendar.HOUR_OF_DAY, hour ); + int minute = Integer.parseInt( startTime.substring( firstColon + 1, lastColon ) ); + cal.set( Calendar.MINUTE, minute ); + int second = Integer.parseInt( startTime.substring( lastColon + 1, len ) ); + cal.set( Calendar.SECOND, second ); + } + catch ( NumberFormatException e ) + { + String message = "Problem parsing start time [" + startTime + "]. It should be in HH:MM:SS format."; + throw new ScheduleFormatException( message ); + } + + // if the date is less than now, add a day. + Date now = new Date(); + if ( cal.getTime().before( now ) ) + { + cal.add( Calendar.DAY_OF_MONTH, 1 ); + } + + return cal.getTime(); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: jcs-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: jcs-dev-help@jakarta.apache.org