001    package org.apache.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    
022    import java.io.Serializable;
023    import java.util.Date;
024    import java.util.Timer;
025    import java.util.TimerTask;
026    
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
030    import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheManagerAbstractTemplate;
031    import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCachePoolAccess;
032    import org.apache.jcs.auxiliary.disk.jdbc.TableState;
033    import org.apache.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleFormatException;
034    import org.apache.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleParser;
035    import org.apache.jcs.engine.behavior.ICompositeCacheManager;
036    import org.apache.jcs.engine.behavior.IElementSerializer;
037    import org.apache.jcs.engine.logging.behavior.ICacheEventLogger;
038    
039    /**
040     * This manages instances of the MySQL jdbc disk cache. It maintains one for each region. One for
041     * all regions would work, but this gives us more detailed stats by region.
042     * <p>
043     * Although the generic JDBC Disk Cache Manager can be used for MySQL, the MySQL JDBC Disk Cache has
044     * additional features, such as table optimization that are particular to MySQL.
045     */
046    public class MySQLDiskCacheManager
047        extends JDBCDiskCacheManagerAbstractTemplate
048    {
049        /** Don't change. */
050        private static final long serialVersionUID = -8258856770927857896L;
051    
052        /** The logger */
053        protected static final Log log = LogFactory.getLog( MySQLDiskCacheManager.class );
054    
055        /** The singleton instance. */
056        private static MySQLDiskCacheManager instance;
057    
058        /** User configurable attributes. */
059        private final MySQLDiskCacheAttributes defaultJDBCDiskCacheAttributes;
060    
061        /** ms in a day */
062        private static final int DAILY_INTERVAL = 60 * 60 * 24 * 1000;
063    
064        /** for schedule optimizations */
065        private Timer daemon = null;
066    
067        /** The cache manager instance */
068        private ICompositeCacheManager compositeCacheManager;
069    
070        /**
071         * Constructor for the HSQLCacheManager object
072         * <p>
073         * @param cattr
074         * @param compositeCacheManager
075         * @param cacheEventLogger
076         * @param elementSerializer
077         */
078        private MySQLDiskCacheManager( MySQLDiskCacheAttributes cattr, ICompositeCacheManager compositeCacheManager,
079                                       ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
080        {
081            if ( log.isInfoEnabled() )
082            {
083                log.info( "Creating MySQLDiskCacheManager with " + cattr );
084            }
085            defaultJDBCDiskCacheAttributes = cattr;
086            setElementSerializer( elementSerializer );
087            setCacheEventLogger( cacheEventLogger );
088            setCompositeCacheManager( compositeCacheManager );
089        }
090    
091        /**
092         * Gets the defaultCattr attribute of the HSQLCacheManager object
093         * <p>
094         * @return The defaultCattr value
095         */
096        public MySQLDiskCacheAttributes getDefaultJDBCDiskCacheAttributes()
097        {
098            return defaultJDBCDiskCacheAttributes;
099        }
100    
101        /**
102         * Gets the instance attribute of the HSQLCacheManager class
103         * <p>
104         * @param cattr
105         * @param compositeCacheManager
106         * @param cacheEventLogger
107         * @param elementSerializer
108         * @return The instance value
109         */
110        public static MySQLDiskCacheManager getInstance( MySQLDiskCacheAttributes cattr, ICompositeCacheManager compositeCacheManager,
111                                                         ICacheEventLogger cacheEventLogger,
112                                                         IElementSerializer elementSerializer )
113        {
114            synchronized ( MySQLDiskCacheManager.class )
115            {
116                if ( instance == null )
117                {
118                    instance = new MySQLDiskCacheManager( cattr, compositeCacheManager, cacheEventLogger, elementSerializer );
119                }
120            }
121            clients++;
122            return instance;
123        }
124    
125        /**
126         * Gets the cache attribute of the HSQLCacheManager object
127         * <p>
128         * @param cacheName
129         * @return The cache value
130         */
131        @SuppressWarnings("unchecked")
132        public <K extends Serializable, V extends Serializable> MySQLDiskCache<K, V> getCache( String cacheName )
133        {
134            MySQLDiskCacheAttributes cattr = (MySQLDiskCacheAttributes) defaultJDBCDiskCacheAttributes.copy();
135            cattr.setCacheName( cacheName );
136            return (MySQLDiskCache<K, V>) getCache( cattr );
137        }
138    
139        /**
140         * Creates a JDBCDiskCache using the supplied attributes.
141         * <p>
142         * @param cattr
143         * @param tableState
144         * @return AuxiliaryCache
145         */
146        @Override
147        protected <K extends Serializable, V extends Serializable> MySQLDiskCache<K, V> createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState )
148        {
149            MySQLDiskCache<K, V> diskCache = new MySQLDiskCache<K, V>( (MySQLDiskCacheAttributes) cattr, tableState, getCompositeCacheManager() );
150    
151            scheduleOptimizations( (MySQLDiskCacheAttributes) cattr, tableState, diskCache.getPoolAccess() );
152    
153            return diskCache;
154        }
155    
156        /**
157         * @param compositeCacheManager the compositeCacheManager to set
158         */
159        protected void setCompositeCacheManager( ICompositeCacheManager compositeCacheManager )
160        {
161            this.compositeCacheManager = compositeCacheManager;
162        }
163    
164        /**
165         * @return the compositeCacheManager
166         */
167        protected ICompositeCacheManager getCompositeCacheManager()
168        {
169            return compositeCacheManager;
170        }
171        /**
172         * For each time in the optimization schedule, this calls schedule Optimizaiton.
173         * <p>
174         * @param attributes configuration propeties.
175         * @param tableState for noting optimization in progress, etc.
176         * @param poolAccess access to the pool
177         */
178        protected void scheduleOptimizations( MySQLDiskCacheAttributes attributes, TableState tableState, JDBCDiskCachePoolAccess poolAccess  )
179        {
180            if ( attributes != null )
181            {
182                if ( attributes.getOptimizationSchedule() != null )
183                {
184                    if ( log.isInfoEnabled() )
185                    {
186                        log.info( "Will try to configure optimization for table [" + attributes.getTableName()
187                            + "] on schdule [" + attributes.getOptimizationSchedule() + "]" );
188                    }
189    
190                    MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState, poolAccess );
191    
192                    // loop through the dates.
193                    try
194                    {
195                        Date[] dates = ScheduleParser.createDatesForSchedule( attributes.getOptimizationSchedule() );
196                        if ( dates != null )
197                        {
198                            for ( int i = 0; i < dates.length; i++ )
199                            {
200                                this.scheduleOptimization( dates[i], optimizer );
201                            }
202                        }
203                    }
204                    catch ( ScheduleFormatException e )
205                    {
206                        log.warn( "Problem creating optimization schedule for table [" + attributes.getTableName() + "]" );
207                    }
208                }
209                else
210                {
211                    if ( log.isInfoEnabled() )
212                    {
213                        log.info( "Optimization is not configured for table [" + attributes.getTableName() + "]" );
214                    }
215                }
216            }
217        }
218    
219        /**
220         * This takes in a single time and schedules the optimizer to be called at that time every day.
221         * <p>
222         * @param startTime -- HH:MM:SS format
223         * @param optimizer
224         */
225        protected void scheduleOptimization( Date startTime, MySQLTableOptimizer optimizer )
226        {
227            if ( log.isInfoEnabled() )
228            {
229                log.info( "startTime [" + startTime + "] for optimizer " + optimizer );
230            }
231    
232            // create clock daemon if necessary
233            if ( daemon == null )
234            {
235                // true for daemon status
236                daemon = new Timer( true );
237            }
238    
239            // get the runnable from the factory
240            TimerTask runnable = new OptimizerTask( optimizer );
241    
242            // have the daemon execute our runnable
243            // false to not execute immediately.
244            daemon.scheduleAtFixedRate( runnable, startTime, DAILY_INTERVAL );
245    
246            if ( log.isInfoEnabled() )
247            {
248                log.info( "Scheduled optimization to begin at [" + startTime + "]" );
249            }
250        }
251    
252        /**
253         * This calls the optimizers' optimize table method. This is used by the timer.
254         * <p>
255         * @author Aaron Smuts
256         */
257        private static class OptimizerTask
258            extends TimerTask
259        {
260            /** Handles optimization */
261            private MySQLTableOptimizer optimizer = null;
262    
263            /**
264             * Get a handle on the optimizer.
265             * <p>
266             * @param optimizer
267             */
268            public OptimizerTask( MySQLTableOptimizer optimizer )
269            {
270                this.optimizer = optimizer;
271            }
272    
273            /**
274             * This calls optimize on the optimizer.
275             * <p>
276             * @see java.lang.Runnable#run()
277             */
278            @Override
279            public void run()
280            {
281                if ( optimizer != null )
282                {
283                    boolean success = optimizer.optimizeTable();
284                    if ( log.isInfoEnabled() )
285                    {
286                        log.info( "Optimization success status [" + success + "]" );
287                    }
288                }
289                else
290                {
291                    log.warn( "OptimizerRunner: The optimizer is null.  Could not optimize table." );
292                }
293            }
294        }
295    }