View Javadoc

1   package org.apache.jcs.auxiliary.disk.jdbc;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.Serializable;
24  import java.util.Hashtable;
25  import java.util.Map;
26  import java.util.concurrent.Executors;
27  import java.util.concurrent.ScheduledExecutorService;
28  import java.util.concurrent.ThreadFactory;
29  import java.util.concurrent.TimeUnit;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.jcs.auxiliary.AuxiliaryCache;
34  import org.apache.jcs.auxiliary.disk.AbstractDiskCacheManager;
35  
36  /**
37   * This class serves as an abstract template for JDBCDiskCache Manager. The MySQL JDBC Disk Cache
38   * needs many of the same features as the generic manager.
39   * <p>
40   * @author Aaron Smuts
41   */
42  public abstract class JDBCDiskCacheManagerAbstractTemplate
43      extends AbstractDiskCacheManager
44  {
45      /** Don't change. */
46      private static final long serialVersionUID = 218557927622128905L;
47  
48      /** The logger. */
49      private static final Log log = LogFactory.getLog( JDBCDiskCacheManagerAbstractTemplate.class );
50  
51      /** Incremented on getIntance, decremented on release. */
52      protected static int clients;
53  
54      /** A map of JDBCDiskCache objects to region names. */
55      protected static Hashtable<String, JDBCDiskCache<? extends Serializable, ? extends Serializable>> caches =
56          new Hashtable<String, JDBCDiskCache<? extends Serializable, ? extends Serializable>>();
57  
58      /**
59       * A map of TableState objects to table names. Each cache has a table state object, which is
60       * used to determine if any long processes such as deletes or optimizations are running.
61       */
62      protected static Hashtable<String, TableState> tableStates = new Hashtable<String, TableState>();
63  
64      /** The background disk shrinker, one for all regions. */
65      private ScheduledExecutorService 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 final Map<String, ShrinkerThread> shrinkerThreadMap = new Hashtable<String, ShrinkerThread>();
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 <K extends Serializable, V extends Serializable> JDBCDiskCache<K, V> createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState );
81  
82      /**
83       * Creates a JDBCDiskCache for the region if one doesn't exist, else it returns the pre-created
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 <K extends Serializable, V extends Serializable> JDBCDiskCache<K, V> getCache( JDBCDiskCacheAttributes cattr )
90      {
91          JDBCDiskCache<K, V> diskCache = null;
92  
93          log.debug( "cacheName = " + cattr.getCacheName() );
94  
95          synchronized ( caches )
96          {
97              @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
98              JDBCDiskCache<K, V> jdbcDiskCache = (JDBCDiskCache<K, V>) caches.get( cattr.getCacheName() );
99              diskCache = jdbcDiskCache;
100 
101             if ( diskCache == null )
102             {
103                 TableState tableState = tableStates.get( cattr.getTableName() );
104 
105                 if ( tableState == null )
106                 {
107                     tableState = new TableState( cattr.getTableName() );
108                 }
109 
110                 diskCache = createJDBCDiskCache( cattr, tableState );
111                 diskCache.setCacheEventLogger( getCacheEventLogger() );
112                 diskCache.setElementSerializer( getElementSerializer() );
113                 caches.put( cattr.getCacheName(), diskCache );
114             }
115         }
116 
117         if ( log.isDebugEnabled() )
118         {
119             log.debug( "JDBC cache = " + diskCache );
120         }
121 
122         // create a shrinker if we need it.
123         createShrinkerWhenNeeded( cattr, diskCache );
124 
125         return diskCache;
126     }
127 
128     /**
129      * If UseDiskShrinker is true then we will create a shrinker daemon if necessary.
130      * <p>
131      * @param cattr
132      * @param raf
133      */
134     protected void createShrinkerWhenNeeded( JDBCDiskCacheAttributes cattr, AuxiliaryCache<?, ?> raf )
135     {
136         // add cache to shrinker.
137         if ( cattr.isUseDiskShrinker() )
138         {
139             if ( shrinkerDaemon == null )
140             {
141                 shrinkerDaemon = Executors.newScheduledThreadPool(2, new MyThreadFactory());
142             }
143 
144             ShrinkerThread shrinkerThread = shrinkerThreadMap.get( cattr.getTableName() );
145             if ( shrinkerThread == null )
146             {
147                 shrinkerThread = new ShrinkerThread();
148                 shrinkerThreadMap.put( cattr.getTableName(), shrinkerThread );
149 
150                 long intervalMillis = Math.max( 999, cattr.getShrinkerIntervalSeconds() * 1000 );
151                 if ( log.isInfoEnabled() )
152                 {
153                     log.info( "Setting the shrinker to run every [" + intervalMillis + "] ms. for table ["
154                         + cattr.getTableName() + "]" );
155                 }
156                 shrinkerDaemon.scheduleAtFixedRate(shrinkerThread, 0, intervalMillis, TimeUnit.MILLISECONDS);
157             }
158             shrinkerThread.addDiskCacheToShrinkList( (JDBCDiskCache<?, ?>) raf );
159         }
160     }
161 
162     /**
163      * @param name
164      */
165     public void freeCache( String name )
166     {
167         JDBCDiskCache<?, ?> raf = caches.get( name );
168         if ( raf != null )
169         {
170             try
171             {
172                 raf.dispose();
173             }
174             catch ( IOException e )
175             {
176                 log.error( "Problem disposing of disk.", e );
177             }
178         }
179     }
180 
181     /** Disposes of all regions. */
182     public void release()
183     {
184         // Wait until called by the last client
185         if ( --clients != 0 )
186         {
187             return;
188         }
189         synchronized ( caches )
190         {
191             for (JDBCDiskCache<?, ?> raf : caches.values())
192             {
193                 if ( raf != null )
194                 {
195                     try
196                     {
197                         raf.dispose();
198                     }
199                     catch ( IOException e )
200                     {
201                         log.error( "Problem disposing of disk.", e );
202                     }
203                 }
204             }
205         }
206     }
207 
208     /**
209      * Allows us to set the daemon status on the clock-daemon
210      */
211     protected static class MyThreadFactory
212         implements ThreadFactory
213     {
214         /**
215          * Set the priority to min and daemon to true.
216          * <p>
217          * @param runner
218          * @return the daemon thread.
219          */
220         public Thread newThread( Runnable runner )
221         {
222             Thread t = new Thread( runner );
223             String oldName = t.getName();
224             t.setName( "JCS-JDBCDiskCacheManager-" + oldName );
225             t.setDaemon( true );
226             t.setPriority( Thread.MIN_PRIORITY );
227             return t;
228         }
229     }
230 }