1 package org.apache.jcs.auxiliary.disk.jdbc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.Enumeration;
23 import java.util.Hashtable;
24 import java.util.Map;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.jcs.auxiliary.AuxiliaryCache;
29 import org.apache.jcs.auxiliary.AuxiliaryCacheManager;
30
31 import EDU.oswego.cs.dl.util.concurrent.ClockDaemon;
32 import EDU.oswego.cs.dl.util.concurrent.ThreadFactory;
33
34 /***
35 * This class serves as an abstract template for JDBCDiskCache Manager. The MySQL JDBC Disk Cache
36 * needs many of the same features as the generic maanger.
37 * <p>
38 * @author Aaron Smuts
39 */
40 public abstract class JDBCDiskCacheManagerAbstractTemplate
41 implements AuxiliaryCacheManager
42 {
43 /*** The logger. */
44 private static final Log log = LogFactory.getLog( JDBCDiskCacheManagerAbstractTemplate.class );
45
46 /***
47 * Incremented on getIntance, decremented on release.
48 */
49 protected static int clients;
50
51 /***
52 * A map of JDBCDiskCache objects to region names.
53 */
54 protected static Hashtable caches = new Hashtable();
55
56 /***
57 * A map of TableState objects to table names. Each cache has a table state object, which is
58 * used to determin if any long processes such as deletes or optimizations are running.
59 */
60 protected static Hashtable tableStates = new Hashtable();
61
62 /***
63 * The background disk shrinker, one for all regions.
64 */
65 private ClockDaemon shrinkerDaemon;
66
67 /***
68 * A map of table name to shrinker threads. This allows each table to have a different setting.
69 * It assumes that there is only one jdbc disk cache auxiliary defined per table.
70 */
71 private Map shrinkerThreadMap = new Hashtable();
72
73 /***
74 * Children must implement this method.
75 * <p>
76 * @param cattr
77 * @param tableState An object used by multiple processes to indicate state.
78 * @return AuxiliaryCache -- a JDBCDiskCache
79 */
80 protected abstract AuxiliaryCache createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState );
81
82 /***
83 * Creates a JDBCDiskCache for the region if one doesn't exist, else it returns the precreated
84 * instance. It also adds the region to the shrinker thread if needed.
85 * <p>
86 * @param cattr
87 * @return The cache value
88 */
89 public AuxiliaryCache getCache( JDBCDiskCacheAttributes cattr )
90 {
91 AuxiliaryCache diskCache = null;
92
93 log.debug( "cacheName = " + cattr.getCacheName() );
94
95 synchronized ( caches )
96 {
97 diskCache = (AuxiliaryCache) caches.get( cattr.getCacheName() );
98
99 if ( diskCache == null )
100 {
101 TableState tableState = (TableState) tableStates.get( cattr.getTableName() );
102
103 if ( tableState == null )
104 {
105 tableState = new TableState( cattr.getTableName() );
106 }
107
108 diskCache = createJDBCDiskCache( cattr, tableState );
109
110 caches.put( cattr.getCacheName(), diskCache );
111 }
112 }
113
114 if ( log.isDebugEnabled() )
115 {
116 log.debug( "JDBC cache = " + diskCache );
117 }
118
119
120 createShrinkerWhenNeeded( cattr, diskCache );
121
122 return diskCache;
123 }
124
125 /***
126 * If UseDiskShrinker is true then we will create a shrinker daemon if necessary.
127 * <p>
128 * @param cattr
129 * @param raf
130 */
131 protected void createShrinkerWhenNeeded( JDBCDiskCacheAttributes cattr, AuxiliaryCache raf )
132 {
133
134 if ( cattr.isUseDiskShrinker() )
135 {
136 if ( shrinkerDaemon == null )
137 {
138 shrinkerDaemon = new ClockDaemon();
139 shrinkerDaemon.setThreadFactory( new MyThreadFactory() );
140 }
141
142 ShrinkerThread shrinkerThread = (ShrinkerThread) shrinkerThreadMap.get( cattr.getTableName() );
143 if ( shrinkerThread == null )
144 {
145 shrinkerThread = new ShrinkerThread();
146 shrinkerThreadMap.put( cattr.getTableName(), shrinkerThread );
147
148 long intervalMillis = Math.max( 999, cattr.getShrinkerIntervalSeconds() * 1000 );
149 if ( log.isInfoEnabled() )
150 {
151 log.info( "Setting the shrinker to run every [" + intervalMillis + "] ms. for table ["
152 + cattr.getTableName() + "]" );
153 }
154 shrinkerDaemon.executePeriodically( intervalMillis, shrinkerThread, false );
155 }
156 shrinkerThread.addDiskCacheToShrinkList( (JDBCDiskCache) raf );
157 }
158 }
159
160 /***
161 * @param name
162 */
163 public void freeCache( String name )
164 {
165 JDBCDiskCache raf = (JDBCDiskCache) caches.get( name );
166 if ( raf != null )
167 {
168 raf.dispose();
169 }
170 }
171
172 /***
173 * Gets the cacheType attribute of the HSQLCacheManager object
174 * <p>
175 * @return The cacheType value
176 */
177 public int getCacheType()
178 {
179 return DISK_CACHE;
180 }
181
182 /*** Disposes of all regions. */
183 public void release()
184 {
185
186 if ( --clients != 0 )
187 {
188 return;
189 }
190 synchronized ( caches )
191 {
192 Enumeration allCaches = caches.elements();
193
194 while ( allCaches.hasMoreElements() )
195 {
196 JDBCDiskCache raf = (JDBCDiskCache) allCaches.nextElement();
197 if ( raf != null )
198 {
199 raf.dispose();
200 }
201 }
202 }
203 }
204
205 /***
206 * Allows us to set the daemon status on the clockdaemon
207 * <p>
208 * @author aaronsm
209 */
210 class MyThreadFactory
211 implements ThreadFactory
212 {
213 /***
214 * Set the priority to min and daemon to true.
215 * <p>
216 * @param runner
217 * @return the daemon thread.
218 */
219 public Thread newThread( Runnable runner )
220 {
221 Thread t = new Thread( runner );
222 t.setDaemon( true );
223 t.setPriority( Thread.MIN_PRIORITY );
224 return t;
225 }
226 }
227 }