001package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.sql.SQLException;
023import java.text.ParseException;
024import java.util.Date;
025import java.util.concurrent.TimeUnit;
026
027import javax.sql.DataSource;
028
029import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
030import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory;
031import org.apache.commons.jcs.auxiliary.disk.jdbc.TableState;
032import org.apache.commons.jcs.auxiliary.disk.jdbc.dsfactory.DataSourceFactory;
033import org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleParser;
034import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
035import org.apache.commons.jcs.engine.behavior.IElementSerializer;
036import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039
040/**
041 * This factory should create mysql disk caches.
042 * <p>
043 * @author Aaron Smuts
044 */
045public class MySQLDiskCacheFactory
046    extends JDBCDiskCacheFactory
047{
048    /** The logger */
049    private static final Log log = LogFactory.getLog( MySQLDiskCacheFactory.class );
050
051    /**
052     * This factory method should create an instance of the mysqlcache.
053     * <p>
054     * @param rawAttr specific cache configuration attributes
055     * @param compositeCacheManager the global cache manager
056     * @param cacheEventLogger a specific logger for cache events
057     * @param elementSerializer a serializer for cache elements
058     * @return MySQLDiskCache the cache instance
059     * @throws SQLException if the cache instance could not be created
060     */
061    @Override
062    public <K, V> MySQLDiskCache<K, V> createCache( AuxiliaryCacheAttributes rawAttr,
063            ICompositeCacheManager compositeCacheManager,
064            ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
065            throws SQLException
066    {
067        MySQLDiskCacheAttributes cattr = (MySQLDiskCacheAttributes) rawAttr;
068        TableState tableState = getTableState( cattr.getTableName() );
069        DataSourceFactory dsFactory = getDataSourceFactory(cattr, compositeCacheManager.getConfigurationProperties());
070
071        MySQLDiskCache<K, V> cache = new MySQLDiskCache<K, V>( cattr, dsFactory, tableState, compositeCacheManager );
072        cache.setCacheEventLogger( cacheEventLogger );
073        cache.setElementSerializer( elementSerializer );
074
075        // create a shrinker if we need it.
076        createShrinkerWhenNeeded( cattr, cache );
077        scheduleOptimizations( cattr, tableState, cache.getDataSource() );
078
079        return cache;
080
081    }
082
083    /**
084     * For each time in the optimization schedule, this calls schedule Optimization.
085     * <p>
086     * @param attributes configuration properties.
087     * @param tableState for noting optimization in progress, etc.
088     * @param ds the DataSource
089     */
090    protected void scheduleOptimizations( MySQLDiskCacheAttributes attributes, TableState tableState, DataSource ds  )
091    {
092        if ( attributes != null )
093        {
094            if ( attributes.getOptimizationSchedule() != null )
095            {
096                if ( log.isInfoEnabled() )
097                {
098                    log.info( "Will try to configure optimization for table [" + attributes.getTableName()
099                        + "] on schedule [" + attributes.getOptimizationSchedule() + "]" );
100                }
101
102                MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState, ds );
103
104                // loop through the dates.
105                try
106                {
107                    Date[] dates = ScheduleParser.createDatesForSchedule( attributes.getOptimizationSchedule() );
108                    if ( dates != null )
109                    {
110                        for ( int i = 0; i < dates.length; i++ )
111                        {
112                            this.scheduleOptimization( dates[i], optimizer );
113                        }
114                    }
115                }
116                catch ( ParseException e )
117                {
118                    log.warn( "Problem creating optimization schedule for table [" + attributes.getTableName() + "]", e );
119                }
120            }
121            else
122            {
123                if ( log.isInfoEnabled() )
124                {
125                    log.info( "Optimization is not configured for table [" + attributes.getTableName() + "]" );
126                }
127            }
128        }
129    }
130
131    /**
132     * This takes in a single time and schedules the optimizer to be called at that time every day.
133     * <p>
134     * @param startTime -- HH:MM:SS format
135     * @param optimizer
136     */
137    protected void scheduleOptimization( Date startTime, MySQLTableOptimizer optimizer )
138    {
139        if ( log.isInfoEnabled() )
140        {
141            log.info( "startTime [" + startTime + "] for optimizer " + optimizer );
142        }
143
144        // get the runnable from the factory
145        OptimizerTask runnable = new OptimizerTask( optimizer );
146        Date now = new Date();
147        long initialDelay = startTime.getTime() - now.getTime();
148
149        // have the daemon execute our runnable
150        getScheduledExecutorService().scheduleAtFixedRate(runnable, initialDelay, 86400000L, TimeUnit.MILLISECONDS );
151    }
152
153    /**
154     * This calls the optimizers' optimize table method. This is used by the timer.
155     * <p>
156     * @author Aaron Smuts
157     */
158    private static class OptimizerTask
159        implements Runnable
160    {
161        /** Handles optimization */
162        private MySQLTableOptimizer optimizer = null;
163
164        /**
165         * Get a handle on the optimizer.
166         * <p>
167         * @param optimizer
168         */
169        public OptimizerTask( MySQLTableOptimizer optimizer )
170        {
171            this.optimizer = optimizer;
172        }
173
174        /**
175         * This calls optimize on the optimizer.
176         * <p>
177         * @see java.lang.Runnable#run()
178         */
179        @Override
180        public void run()
181        {
182            if ( optimizer != null )
183            {
184                boolean success = optimizer.optimizeTable();
185                if ( log.isInfoEnabled() )
186                {
187                    log.info( "Optimization success status [" + success + "]" );
188                }
189            }
190            else
191            {
192                log.warn( "OptimizerRunner: The optimizer is null.  Could not optimize table." );
193            }
194        }
195    }
196}