%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.jcs.utils.access.JCSWorker |
|
|
1 | package org.apache.jcs.utils.access; |
|
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.Serializable; |
|
23 | import java.util.HashMap; |
|
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.JCS; |
|
29 | import org.apache.jcs.access.exception.CacheException; |
|
30 | ||
31 | /** |
|
32 | * Utility class to encapsulate doing a piece of work, and caching the results |
|
33 | * in JCS. Simply construct this class with the region name for the Cache and |
|
34 | * keep a static reference to it instead of the JCS itself. Then make a new |
|
35 | * org.apache.jcs.utils.access.AbstractJCSWorkerHelper and implement Object |
|
36 | * doWork() and do the work in there, returning the object to be cached. Then |
|
37 | * call .getResult() with the key and the AbstractJCSWorkerHelper to get the |
|
38 | * result of the work. If the object isn't allready in the Cache, |
|
39 | * AbstractJCSWorkerHelper.doWork() will get called, and the result will be put |
|
40 | * into the cache. If the object is allready in cache, the cached result will be |
|
41 | * returned instead. |
|
42 | * <p> |
|
43 | * As an added bonus, multiple JCSWorkers with the same region, and key won't do |
|
44 | * the work multiple times: The first JCSWorker to get started will do the work, |
|
45 | * and all subsequent workers with the same region, group, and key will wait on |
|
46 | * the first one and use his resulting work instead of doing the work |
|
47 | * themselves. |
|
48 | * <p> |
|
49 | * This is ideal when the work being done is a query to the database where the |
|
50 | * results may take time to be retrieved. |
|
51 | * <p> |
|
52 | * For example: |
|
53 | * |
|
54 | * <pre> |
|
55 | * public static JCSWorker cachingWorker = new JCSWorker("example region"); |
|
56 | * public Object getSomething(Serializable aKey){ |
|
57 | * JCSWorkerHelper helper = new AbstractJCSWorkerHelper(){ |
|
58 | * public Object doWork(){ |
|
59 | * // Do some (DB?) work here which results in a list |
|
60 | * // This only happens if the cache dosn't have a item in this region for aKey |
|
61 | * // Note this is especially useful with Hibernate, which will cache indiviual |
|
62 | * // Objects, but not entire query result sets. |
|
63 | * List results = query.list(); |
|
64 | * // Whatever we return here get's cached with aKey, and future calls to |
|
65 | * // getResult() on a CachedWorker with the same region and key will return that instead. |
|
66 | * return results; |
|
67 | * }; |
|
68 | * List result = worker.getResult(aKey, helper); |
|
69 | * } |
|
70 | * </pre> |
|
71 | * |
|
72 | * This is essentially the same as doing: |
|
73 | * |
|
74 | * <pre> |
|
75 | * JCS jcs = JCS.getInstance( "exampleregion" ); |
|
76 | * List results = (List) jcs.get( aKey ); |
|
77 | * if ( results != null ) |
|
78 | * { |
|
79 | * //do the work here |
|
80 | * results = query.list(); |
|
81 | * jcs.put( aKey, results ); |
|
82 | * } |
|
83 | * </pre> |
|
84 | * |
|
85 | * <p> |
|
86 | * But has the added benifit of the work-load sharing; under normal |
|
87 | * circumstances if multiple threads all tried to do the same query at the same |
|
88 | * time, the same query would happen multiple times on the database, and the |
|
89 | * resulting object would get put into JCS multiple times. |
|
90 | * <p> |
|
91 | * @author Travis Savo |
|
92 | */ |
|
93 | 1 | public class JCSWorker |
94 | { |
|
95 | 15 | private static final Log logger = LogFactory.getLog( JCSWorker.class ); |
96 | ||
97 | private JCS cache; |
|
98 | ||
99 | /** |
|
100 | * Map to hold who's doing work presently. |
|
101 | */ |
|
102 | 8 | private static volatile Map map = new HashMap(); |
103 | ||
104 | /** |
|
105 | * Region for the JCS cache. |
|
106 | */ |
|
107 | private String region; |
|
108 | ||
109 | /** |
|
110 | * Constructor which takes a region for the JCS cache. |
|
111 | * @param aRegion |
|
112 | * The Region to use for the JCS cache. |
|
113 | */ |
|
114 | 1 | public JCSWorker( final String aRegion ) |
115 | 7 | { |
116 | 8 | region = aRegion; |
117 | try |
|
118 | { |
|
119 | 8 | cache = JCS.getInstance( aRegion ); |
120 | } |
|
121 | 0 | catch ( CacheException e ) |
122 | { |
|
123 | 0 | throw new RuntimeException( e.getMessage() ); |
124 | 7 | } |
125 | 8 | } |
126 | ||
127 | /** |
|
128 | * Getter for the region of the JCS Cache. |
|
129 | * @return The JCS region in which the result will be cached. |
|
130 | */ |
|
131 | public String getRegion() |
|
132 | { |
|
133 | 48 | return region; |
134 | } |
|
135 | ||
136 | /** |
|
137 | * Gets the cached result for this region/key OR does the work and caches |
|
138 | * the result, returning the result. If the result has not been cached yet, |
|
139 | * this calls doWork() on the JCSWorkerHelper to do the work and cache the |
|
140 | * result. This is also an opertunity to do any post processing of the |
|
141 | * result in your CachedWorker implementation. |
|
142 | * @param aKey |
|
143 | * The key to get/put with on the Cache. |
|
144 | * @param aWorker |
|
145 | * The JCSWorkerHelper implementing Object doWork(). This gets |
|
146 | * called if the cache get misses, and the result is put into |
|
147 | * cache. |
|
148 | * @return The result of doing the work, or the cached result. |
|
149 | * @throws Exception |
|
150 | * Throws an exception if anything goes wrong while doing the |
|
151 | * work. |
|
152 | */ |
|
153 | public Object getResult( Serializable aKey, JCSWorkerHelper aWorker ) |
|
154 | throws Exception |
|
155 | { |
|
156 | 16 | return run( aKey, null, aWorker ); |
157 | } |
|
158 | ||
159 | /** |
|
160 | * Gets the cached result for this region/key OR does the work and caches |
|
161 | * the result, returning the result. If the result has not been cached yet, |
|
162 | * this calls doWork() on the JCSWorkerHelper to do the work and cache the |
|
163 | * result. This is also an opertunity to do any post processing of the |
|
164 | * result in your CachedWorker implementation. |
|
165 | * @param aKey |
|
166 | * The key to get/put with on the Cache. |
|
167 | * @param aGroup |
|
168 | * The cache group to put the result in. |
|
169 | * @param aWorker |
|
170 | * The JCSWorkerHelper implementing Object doWork(). This gets |
|
171 | * called if the cache get misses, and the result is put into |
|
172 | * cache. |
|
173 | * @return The result of doing the work, or the cached result. |
|
174 | * @throws Exception |
|
175 | * Throws an exception if anything goes wrong while doing the |
|
176 | * work. |
|
177 | */ |
|
178 | public Object getResult( Serializable aKey, String aGroup, JCSWorkerHelper aWorker ) |
|
179 | throws Exception |
|
180 | { |
|
181 | 0 | return run( aKey, aGroup, aWorker ); |
182 | } |
|
183 | ||
184 | /** |
|
185 | * Try and get the object from the cache, and if it's not there, do the work |
|
186 | * and cache it. This also ensures that only one CachedWorker is doing the |
|
187 | * work and subsequent calls to a CachedWorker with identical |
|
188 | * region/key/group will wait on the results of this call. It will call the |
|
189 | * JCSWorkerHelper.doWork() if the cache misses, and will put the result. |
|
190 | * @param aKey |
|
191 | * @param aGroup |
|
192 | * @param aHelper |
|
193 | * @return Either the result of doing the work, or the cached result. |
|
194 | * @throws Exception |
|
195 | * If something goes wrong while doing the work, throw an |
|
196 | * exception. |
|
197 | */ |
|
198 | private Object run( Serializable aKey, String aGroup, JCSWorkerHelper aHelper ) |
|
199 | throws Exception |
|
200 | { |
|
201 | 16 | Object result = null; |
202 | // long start = 0; |
|
203 | // long dbTime = 0; |
|
204 | 16 | JCSWorkerHelper helper = null; |
205 | ||
206 | 18 | synchronized ( map ) |
207 | { |
|
208 | // Check to see if we allready have a thread doing this work. |
|
209 | 16 | helper = (JCSWorkerHelper) map.get( getRegion() + aKey ); |
210 | 16 | if ( helper == null ) |
211 | { |
|
212 | // If not, add ourselves as the Worker so |
|
213 | // calls in another thread will use this worker's result |
|
214 | 16 | map.put( getRegion() + aKey, aHelper ); |
215 | } |
|
216 | 14 | } |
217 | 16 | if ( helper != null ) |
218 | { |
|
219 | 0 | synchronized ( helper ) |
220 | { |
|
221 | 0 | if ( logger.isDebugEnabled() ) |
222 | { |
|
223 | 0 | logger.debug( "Found a worker allready doing this work (" + getRegion() + ":" + aKey + ")." ); |
224 | } |
|
225 | 0 | if ( !helper.isFinished() ) |
226 | { |
|
227 | 0 | helper.wait(); |
228 | } |
|
229 | 0 | if ( logger.isDebugEnabled() ) |
230 | { |
|
231 | 0 | logger.debug( "Another thread finished our work for us. Using thoes results instead. (" |
232 | + getRegion() + ":" + aKey + ")." ); |
|
233 | } |
|
234 | 0 | } |
235 | } |
|
236 | // Do the work |
|
237 | try |
|
238 | { |
|
239 | 16 | if ( logger.isDebugEnabled() ) |
240 | { |
|
241 | 0 | logger.debug( getRegion() + " is doing the work." ); |
242 | } |
|
243 | 16 | result = null; |
244 | ||
245 | // Try to get the item from the cache |
|
246 | 16 | if ( aGroup != null ) |
247 | { |
|
248 | 0 | result = cache.getFromGroup( aKey, aGroup ); |
249 | 0 | } |
250 | else |
|
251 | { |
|
252 | 16 | result = cache.get( aKey ); |
253 | } |
|
254 | // If the cache dosn't have it, do the work. |
|
255 | 16 | if ( result == null ) |
256 | { |
|
257 | 8 | result = aHelper.doWork(); |
258 | 8 | if ( logger.isDebugEnabled() ) |
259 | { |
|
260 | 0 | logger.debug( "Work Done, caching: key:" + aKey + ", group:" + aGroup + ", result:" + result + "." ); |
261 | } |
|
262 | // Stick the result of the work in the cache. |
|
263 | 8 | if ( aGroup != null ) |
264 | { |
|
265 | 0 | cache.putInGroup( aKey, aGroup, result ); |
266 | 0 | } |
267 | else |
|
268 | { |
|
269 | 8 | cache.put( aKey, result ); |
270 | } |
|
271 | } |
|
272 | // return the result |
|
273 | 18 | return result; |
274 | } |
|
275 | finally |
|
276 | { |
|
277 | 2 | if ( logger.isDebugEnabled() ) |
278 | { |
|
279 | 0 | logger.debug( getRegion() + ":" + aKey + " entered finally." ); |
280 | } |
|
281 | 18 | synchronized ( map ) |
282 | { |
|
283 | // Remove ourselves as the worker. |
|
284 | 16 | if ( helper == null ) |
285 | { |
|
286 | 16 | map.remove( getRegion() + aKey ); |
287 | } |
|
288 | 18 | synchronized ( this ) |
289 | { |
|
290 | 16 | aHelper.setFinished( true ); |
291 | // Wake everyone waiting on us |
|
292 | 16 | notifyAll(); |
293 | 14 | } |
294 | 14 | } |
295 | 14 | } |
296 | } |
|
297 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |