001package org.apache.commons.jcs.auxiliary.disk.jdbc; 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 org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import java.util.Collections; 026import java.util.HashSet; 027import java.util.Set; 028 029/** 030 * Calls delete expired on the disk caches. The shrinker is run by a clock daemon. The shrinker 031 * calls delete on each region. It pauses between calls. 032 * <p> 033 * @author Aaron Smuts 034 */ 035public class ShrinkerThread 036 implements Runnable 037{ 038 /** The logger. */ 039 private static final Log log = LogFactory.getLog( ShrinkerThread.class ); 040 041 /** A set of JDBCDiskCache objects to call deleteExpired on. */ 042 private final Set<JDBCDiskCache<?, ?>> shrinkSet = 043 Collections.synchronizedSet( new HashSet<JDBCDiskCache<?, ?>>() ); 044 045 /** Default time period to use. */ 046 private static final long DEFAULT_PAUSE_BETWEEN_REGION_CALLS_MILLIS = 5000; 047 048 /** 049 * How long should we wait between calls to deleteExpired when we are iterating through the list 050 * of regions. Delete can lock the table. We want to give clients a chance to get some work 051 * done. 052 */ 053 private long pauseBetweenRegionCallsMillis = DEFAULT_PAUSE_BETWEEN_REGION_CALLS_MILLIS; 054 055 /** 056 * Does nothing special. 057 */ 058 protected ShrinkerThread() 059 { 060 super(); 061 } 062 063 /** 064 * Adds a JDBC disk cache to the set of disk cache to shrink. 065 * <p> 066 * @param diskCache 067 */ 068 public void addDiskCacheToShrinkList( JDBCDiskCache<?, ?> diskCache ) 069 { 070 // the set will prevent dupes. 071 // we could also just add these to a hashmap by region name 072 // but that might cause a problem if you wanted to use two different 073 // jbdc disk caches for the same region. 074 shrinkSet.add( diskCache ); 075 } 076 077 /** 078 * Calls deleteExpired on each item in the set. It pauses between each call. 079 */ 080 @Override 081 public void run() 082 { 083 try 084 { 085 deleteExpiredFromAllRegisteredRegions(); 086 } 087 catch ( Throwable e ) 088 { 089 log.error( "Caught an expcetion while trying to delete expired items.", e ); 090 } 091 } 092 093 /** 094 * Deletes the expired items from all the registered regions. 095 */ 096 private void deleteExpiredFromAllRegisteredRegions() 097 { 098 if ( log.isInfoEnabled() ) 099 { 100 log.info( "Running JDBC disk cache shrinker. Number of regions [" + shrinkSet.size() + "]" ); 101 } 102 103 Object[] caches = null; 104 105 synchronized ( shrinkSet ) 106 { 107 caches = this.shrinkSet.toArray(); 108 } 109 110 if ( caches != null ) 111 { 112 for ( int i = 0; i < caches.length; i++ ) 113 { 114 JDBCDiskCache<?, ?> cache = (JDBCDiskCache<?, ?>) caches[i]; 115 116 long start = System.currentTimeMillis(); 117 int deleted = cache.deleteExpired(); 118 long end = System.currentTimeMillis(); 119 120 if ( log.isInfoEnabled() ) 121 { 122 log.info( "Deleted [" + deleted + "] expired for region [" + cache.getCacheName() + "] for table [" 123 + cache.getTableName() + "] in " + ( end - start ) + " ms." ); 124 } 125 126 // don't pause after the last call to delete expired. 127 if ( i < caches.length - 1 ) 128 { 129 if ( log.isInfoEnabled() ) 130 { 131 log.info( "Pausing for [" + this.getPauseBetweenRegionCallsMillis() 132 + "] ms. before shrinking the next region." ); 133 } 134 135 try 136 { 137 Thread.sleep( this.getPauseBetweenRegionCallsMillis() ); 138 } 139 catch ( InterruptedException e ) 140 { 141 log.warn( "Interrupted while waiting to delete expired for the next region." ); 142 } 143 } 144 } 145 } 146 } 147 148 /** 149 * How long should we wait between calls to deleteExpired when we are iterating through the list 150 * of regions. 151 * <p> 152 * @param pauseBetweenRegionCallsMillis The pauseBetweenRegionCallsMillis to set. 153 */ 154 public void setPauseBetweenRegionCallsMillis( long pauseBetweenRegionCallsMillis ) 155 { 156 this.pauseBetweenRegionCallsMillis = pauseBetweenRegionCallsMillis; 157 } 158 159 /** 160 * How long should we wait between calls to deleteExpired when we are iterating through the list 161 * of regions. 162 * <p> 163 * @return Returns the pauseBetweenRegionCallsMillis. 164 */ 165 public long getPauseBetweenRegionCallsMillis() 166 { 167 return pauseBetweenRegionCallsMillis; 168 } 169}