001package org.apache.commons.jcs3.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.jcs3.auxiliary.AuxiliaryCacheAttributes; 030import org.apache.commons.jcs3.auxiliary.disk.jdbc.JDBCDiskCacheFactory; 031import org.apache.commons.jcs3.auxiliary.disk.jdbc.TableState; 032import org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.DataSourceFactory; 033import org.apache.commons.jcs3.auxiliary.disk.jdbc.mysql.util.ScheduleParser; 034import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager; 035import org.apache.commons.jcs3.engine.behavior.IElementSerializer; 036import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger; 037import org.apache.commons.jcs3.log.Log; 038import org.apache.commons.jcs3.log.LogManager; 039 040/** 041 * This factory should create mysql disk caches. 042 */ 043public class MySQLDiskCacheFactory 044 extends JDBCDiskCacheFactory 045{ 046 /** The logger */ 047 private static final Log log = LogManager.getLog( MySQLDiskCacheFactory.class ); 048 049 /** 050 * This factory method should create an instance of the mysqlcache. 051 * <p> 052 * @param rawAttr specific cache configuration attributes 053 * @param compositeCacheManager the global cache manager 054 * @param cacheEventLogger a specific logger for cache events 055 * @param elementSerializer a serializer for cache elements 056 * @return MySQLDiskCache the cache instance 057 * @throws SQLException if the cache instance could not be created 058 */ 059 @Override 060 public <K, V> MySQLDiskCache<K, V> createCache( final AuxiliaryCacheAttributes rawAttr, 061 final ICompositeCacheManager compositeCacheManager, 062 final ICacheEventLogger cacheEventLogger, final IElementSerializer elementSerializer ) 063 throws SQLException 064 { 065 final MySQLDiskCacheAttributes cattr = (MySQLDiskCacheAttributes) rawAttr; 066 final TableState tableState = getTableState( cattr.getTableName() ); 067 final DataSourceFactory dsFactory = getDataSourceFactory(cattr, compositeCacheManager.getConfigurationProperties()); 068 069 final MySQLDiskCache<K, V> cache = new MySQLDiskCache<>( cattr, dsFactory, tableState); 070 cache.setCacheEventLogger( cacheEventLogger ); 071 cache.setElementSerializer( elementSerializer ); 072 073 // create a shrinker if we need it. 074 createShrinkerWhenNeeded( cattr, cache ); 075 scheduleOptimizations( cattr, tableState, cache.getDataSource() ); 076 077 return cache; 078 079 } 080 081 /** 082 * For each time in the optimization schedule, this calls schedule Optimization. 083 * <p> 084 * @param attributes configuration properties. 085 * @param tableState for noting optimization in progress, etc. 086 * @param ds the DataSource 087 */ 088 protected void scheduleOptimizations( final MySQLDiskCacheAttributes attributes, final TableState tableState, final DataSource ds ) 089 { 090 if ( attributes != null ) 091 { 092 if ( attributes.getOptimizationSchedule() != null ) 093 { 094 log.info( "Will try to configure optimization for table [{0}] on schedule [{1}]", 095 attributes::getTableName, attributes::getOptimizationSchedule); 096 097 final MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState, ds ); 098 099 // loop through the dates. 100 try 101 { 102 // canĀ“t be null, otherwise ScheduleParser.createDatesForSchedule will throw ParseException 103 final Date[] dates = ScheduleParser.createDatesForSchedule( attributes.getOptimizationSchedule() ); 104 for (final Date date : dates) { 105 this.scheduleOptimization( date, optimizer ); 106 } 107 } 108 catch ( final ParseException e ) 109 { 110 log.warn( "Problem creating optimization schedule for table [{0}]", 111 attributes.getTableName(), e ); 112 } 113 } 114 else 115 { 116 log.info( "Optimization is not configured for table [{0}]", 117 attributes.getTableName()); 118 } 119 } 120 } 121 122 /** 123 * This takes in a single time and schedules the optimizer to be called at that time every day. 124 * <p> 125 * @param startTime -- HH:MM:SS format 126 * @param optimizer 127 */ 128 protected void scheduleOptimization( final Date startTime, final MySQLTableOptimizer optimizer ) 129 { 130 log.info( "startTime [{0}] for optimizer {1}", startTime, optimizer ); 131 132 final Date now = new Date(); 133 final long initialDelay = startTime.getTime() - now.getTime(); 134 135 // have the daemon execute the optimization 136 getScheduledExecutorService().scheduleAtFixedRate(() -> optimizeTable(optimizer), 137 initialDelay, 86400L, TimeUnit.SECONDS ); 138 } 139 140 /** 141 * This calls the optimizers' optimize table method. This is used by the timer. 142 */ 143 private static void optimizeTable(final MySQLTableOptimizer optimizer) 144 { 145 if ( optimizer != null ) 146 { 147 final boolean success = optimizer.optimizeTable(); 148 log.info( "Optimization success status [{0}]", success ); 149 } 150 else 151 { 152 log.warn( "OptimizerRunner: The optimizer is null. Could not optimize table." ); 153 } 154 } 155}