1 package org.apache.commons.jcs3.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.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
44
45 public class JDBCDiskCacheFactory
46 extends AbstractAuxiliaryCacheFactory
47 implements IRequireScheduler
48 {
49
50 private static final Log log = LogManager.getLog( JDBCDiskCacheFactory.class );
51
52
53
54
55
56 private ConcurrentMap<String, TableState> tableStates;
57
58
59 protected ScheduledExecutorService scheduler;
60
61
62
63
64
65 private ConcurrentMap<String, ShrinkerThread> shrinkerThreadMap;
66
67
68 private ConcurrentMap<String, DataSourceFactory> dsFactories;
69
70
71 protected static final String POOL_CONFIGURATION_PREFIX = "jcs.jdbcconnectionpool.";
72
73
74 protected static final String ATTRIBUTE_PREFIX = ".attributes";
75
76
77
78
79
80
81
82
83
84
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
101 createShrinkerWhenNeeded( cattr, cache );
102
103 return cache;
104 }
105
106
107
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
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
145
146
147
148
149 protected TableState getTableState(final String tableName)
150 {
151 return tableStates.computeIfAbsent(tableName, TableState::new);
152 }
153
154
155
156
157 @Override
158 public void setScheduledExecutorService(final ScheduledExecutorService scheduledExecutor)
159 {
160 this.scheduler = scheduledExecutor;
161 }
162
163
164
165
166
167
168 protected ScheduledExecutorService getScheduledExecutorService()
169 {
170 return scheduler;
171 }
172
173
174
175
176
177
178
179 protected void createShrinkerWhenNeeded( final JDBCDiskCacheAttributes cattr, final JDBCDiskCache<?, ?> raf )
180 {
181
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
202
203
204
205
206
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 }