View Javadoc
1   package org.apache.commons.jcs3.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.sql.SQLException;
23  import java.util.Properties;
24  import java.util.concurrent.ConcurrentHashMap;
25  import java.util.concurrent.ConcurrentMap;
26  import java.util.concurrent.ScheduledExecutorService;
27  import java.util.concurrent.TimeUnit;
28  
29  import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheFactory;
30  import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes;
31  import org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.DataSourceFactory;
32  import org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.JndiDataSourceFactory;
33  import org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.SharedPoolDataSourceFactory;
34  import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager;
35  import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
36  import org.apache.commons.jcs3.engine.behavior.IRequireScheduler;
37  import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger;
38  import org.apache.commons.jcs3.log.Log;
39  import org.apache.commons.jcs3.log.LogManager;
40  import org.apache.commons.jcs3.utils.config.PropertySetter;
41  
42  /**
43   * This factory should create JDBC auxiliary caches.
44   */
45  public class JDBCDiskCacheFactory
46      extends AbstractAuxiliaryCacheFactory
47      implements IRequireScheduler
48  {
49      /** The logger */
50      private static final Log log = LogManager.getLog( JDBCDiskCacheFactory.class );
51  
52      /**
53       * A map of TableState objects to table names. Each cache has a table state object, which is
54       * used to determine if any long processes such as deletes or optimizations are running.
55       */
56      private ConcurrentMap<String, TableState> tableStates;
57  
58      /** The background scheduler, one for all regions. Injected by the configurator */
59      protected ScheduledExecutorService scheduler;
60  
61      /**
62       * A map of table name to shrinker threads. This allows each table to have a different setting.
63       * It assumes that there is only one jdbc disk cache auxiliary defined per table.
64       */
65      private ConcurrentMap<String, ShrinkerThread> shrinkerThreadMap;
66  
67      /** Pool name to DataSourceFactories */
68      private ConcurrentMap<String, DataSourceFactory> dsFactories;
69  
70      /** props prefix */
71      protected static final String POOL_CONFIGURATION_PREFIX = "jcs.jdbcconnectionpool.";
72  
73      /** .attributes */
74      protected static final String ATTRIBUTE_PREFIX = ".attributes";
75  
76      /**
77       * This factory method should create an instance of the jdbc cache.
78       * <p>
79       * @param rawAttr specific cache configuration attributes
80       * @param compositeCacheManager the global cache manager
81       * @param cacheEventLogger a specific logger for cache events
82       * @param elementSerializer a serializer for cache elements
83       * @return JDBCDiskCache the cache instance
84       * @throws SQLException if the cache instance could not be created
85       */
86      @Override
87      public <K, V> JDBCDiskCache<K, V> createCache( final AuxiliaryCacheAttributes rawAttr,
88              final ICompositeCacheManager compositeCacheManager,
89              final ICacheEventLogger cacheEventLogger, final IElementSerializer elementSerializer )
90              throws SQLException
91      {
92          final JDBCDiskCacheAttributes cattr = (JDBCDiskCacheAttributes) rawAttr;
93          final TableState tableState = getTableState( cattr.getTableName() );
94          final DataSourceFactory dsFactory = getDataSourceFactory(cattr, compositeCacheManager.getConfigurationProperties());
95  
96          final JDBCDiskCache<K, V> cache = new JDBCDiskCache<>(cattr, dsFactory, tableState);
97          cache.setCacheEventLogger( cacheEventLogger );
98          cache.setElementSerializer( elementSerializer );
99  
100         // create a shrinker if we need it.
101         createShrinkerWhenNeeded( cattr, cache );
102 
103         return cache;
104     }
105 
106     /**
107      * Initialize this factory
108      */
109     @Override
110     public void initialize()
111     {
112         super.initialize();
113         this.tableStates = new ConcurrentHashMap<>();
114         this.shrinkerThreadMap = new ConcurrentHashMap<>();
115         this.dsFactories = new ConcurrentHashMap<>();
116     }
117 
118     /**
119      * Dispose of this factory, clean up shared resources
120      */
121     @Override
122     public void dispose()
123     {
124         this.tableStates.clear();
125 
126         for (final DataSourceFactory dsFactory : this.dsFactories.values())
127         {
128         	try
129         	{
130 				dsFactory.close();
131 			}
132         	catch (final SQLException e)
133         	{
134         		log.error("Could not close data source factory {0}", dsFactory.getName(), e);
135 			}
136         }
137 
138         this.dsFactories.clear();
139         this.shrinkerThreadMap.clear();
140         super.dispose();
141     }
142 
143     /**
144      * Get a table state for a given table name
145      *
146      * @param tableName
147      * @return a cached instance of the table state
148      */
149     protected TableState getTableState(final String tableName)
150     {
151         return tableStates.computeIfAbsent(tableName, TableState::new);
152     }
153 
154     /**
155 	 * @see org.apache.commons.jcs3.engine.behavior.IRequireScheduler#setScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
156 	 */
157 	@Override
158 	public void setScheduledExecutorService(final ScheduledExecutorService scheduledExecutor)
159 	{
160 		this.scheduler = scheduledExecutor;
161 	}
162 
163 	/**
164      * Get the scheduler service
165      *
166      * @return the scheduler
167      */
168     protected ScheduledExecutorService getScheduledExecutorService()
169     {
170         return scheduler;
171     }
172 
173     /**
174      * If UseDiskShrinker is true then we will create a shrinker daemon if necessary.
175      * <p>
176      * @param cattr
177      * @param raf
178      */
179     protected void createShrinkerWhenNeeded( final JDBCDiskCacheAttributes cattr, final JDBCDiskCache<?, ?> raf )
180     {
181         // add cache to shrinker.
182         if ( cattr.isUseDiskShrinker() )
183         {
184             final ScheduledExecutorService shrinkerService = getScheduledExecutorService();
185             final ShrinkerThread shrinkerThread = shrinkerThreadMap.computeIfAbsent(cattr.getTableName(), key -> {
186                 final ShrinkerThread newShrinkerThread = new ShrinkerThread();
187 
188                 final long intervalMillis = Math.max( 999, cattr.getShrinkerIntervalSeconds() * 1000 );
189                 log.info( "Setting the shrinker to run every [{0}] ms. for table [{1}]",
190                         intervalMillis, key );
191                 shrinkerService.scheduleAtFixedRate(newShrinkerThread, 0, intervalMillis, TimeUnit.MILLISECONDS);
192 
193                 return newShrinkerThread;
194             });
195 
196             shrinkerThread.addDiskCacheToShrinkList( raf );
197         }
198     }
199 
200     /**
201      * manages the DataSourceFactories.
202      * <p>
203      * @param cattr the cache configuration
204      * @param configProps the configuration properties object
205      * @return a DataSourceFactory
206      * @throws SQLException if a database access error occurs
207      */
208     protected DataSourceFactory getDataSourceFactory( final JDBCDiskCacheAttributes cattr,
209                                                       final Properties configProps ) throws SQLException
210     {
211     	String poolName = null;
212 
213     	if (cattr.getConnectionPoolName() == null)
214     	{
215     		poolName = cattr.getCacheName() + "." + JDBCDiskCacheAttributes.DEFAULT_POOL_NAME;
216         }
217         else
218         {
219             poolName = cattr.getConnectionPoolName();
220         }
221 
222 
223     	return this.dsFactories.computeIfAbsent(poolName, key -> {
224     	    final DataSourceFactory newDsFactory;
225             JDBCDiskCacheAttributes dsConfig;
226 
227             if (cattr.getConnectionPoolName() == null)
228             {
229                 dsConfig = cattr;
230             }
231             else
232             {
233                 dsConfig = new JDBCDiskCacheAttributes();
234                 final String dsConfigAttributePrefix = POOL_CONFIGURATION_PREFIX + key + ATTRIBUTE_PREFIX;
235                 PropertySetter.setProperties( dsConfig,
236                         configProps,
237                         dsConfigAttributePrefix + "." );
238 
239                 dsConfig.setConnectionPoolName(key);
240             }
241 
242             if ( dsConfig.getJndiPath() != null )
243             {
244                 newDsFactory = new JndiDataSourceFactory();
245             }
246             else
247             {
248                 newDsFactory = new SharedPoolDataSourceFactory();
249             }
250 
251             try
252             {
253                 newDsFactory.initialize(dsConfig);
254             }
255             catch (final SQLException e)
256             {
257                 throw new RuntimeException(e);
258             }
259     	    return newDsFactory;
260     	});
261     }
262 }