1 package org.apache.jcs.auxiliary.remote;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Serializable;
24 import java.rmi.UnmarshalException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Set;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.jcs.auxiliary.AuxiliaryCache;
33 import org.apache.jcs.auxiliary.AuxiliaryCacheAttributes;
34 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
35 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService;
36 import org.apache.jcs.engine.CacheAdaptor;
37 import org.apache.jcs.engine.CacheConstants;
38 import org.apache.jcs.engine.CacheEventQueueFactory;
39 import org.apache.jcs.engine.behavior.ICacheElement;
40 import org.apache.jcs.engine.behavior.ICacheEventQueue;
41 import org.apache.jcs.engine.behavior.ICacheType;
42 import org.apache.jcs.engine.stats.StatElement;
43 import org.apache.jcs.engine.stats.Stats;
44 import org.apache.jcs.engine.stats.behavior.IStatElement;
45 import org.apache.jcs.engine.stats.behavior.IStats;
46
47 /***
48 * Used to queue up update requests to the underlying cache. These requests will be processed in
49 * their order of arrival via the cache event queue processor.
50 * <p>
51 * Typically errors will be handled down stream. We only need to kill the queue if an error makes it
52 * to this level from the queue. That can only happen if the queue is damaged, since the events are
53 * procesed asynchronously.
54 * <p>
55 * There is no reason to create a queue on startup if the remote is not healthy.
56 * <p>
57 * If the remote cache encounters an error it will zombie--create a blaking facade for the service.
58 * The Zombie will queue up items until the conenction is restored. An alternative way to accomplish
59 * the same thing would be to stop, not destroy the queue at this level. That way items would be
60 * added to the queue and then when the connection is restored, we could start the worker threads
61 * again. This is a better long term solution, but it requires some significnat changes to the
62 * complicated worker queues.
63 */
64 public class RemoteCacheNoWait
65 implements AuxiliaryCache
66 {
67 private static final long serialVersionUID = -3104089136003714717L;
68
69 private final static Log log = LogFactory.getLog( RemoteCacheNoWait.class );
70
71 private final IRemoteCacheClient cache;
72
73 private ICacheEventQueue cacheEventQueue;
74
75 private int getCount = 0;
76
77 private int removeCount = 0;
78
79 private int putCount = 0;
80
81 /***
82 * Constructs with the given remote cache, and fires up an event queue for aysnchronous
83 * processing.
84 * <p>
85 * @param cache
86 */
87 public RemoteCacheNoWait( IRemoteCacheClient cache )
88 {
89 this.cache = cache;
90 CacheEventQueueFactory fact = new CacheEventQueueFactory();
91 this.cacheEventQueue = fact.createCacheEventQueue( new CacheAdaptor( cache ), cache.getListenerId(), cache.getCacheName(),
92 cache.getAuxiliaryCacheAttributes().getEventQueuePoolName(), cache
93 .getAuxiliaryCacheAttributes().getEventQueueTypeFactoryCode() );
94
95 if ( cache.getStatus() == CacheConstants.STATUS_ERROR )
96 {
97 cacheEventQueue.destroy();
98 }
99 }
100
101 /***
102 * Adds a put event to the queue.
103 * <p>
104 * (non-Javadoc)
105 * @see org.apache.jcs.engine.behavior.ICache#update(org.apache.jcs.engine.behavior.ICacheElement)
106 */
107 public void update( ICacheElement ce )
108 throws IOException
109 {
110 putCount++;
111 try
112 {
113 cacheEventQueue.addPutEvent( ce );
114 }
115 catch ( IOException ex )
116 {
117 log.error( "Problem adding putEvent to queue.", ex );
118 cacheEventQueue.destroy();
119 throw ex;
120 }
121 }
122
123 /***
124 * Synchronously reads from the remote cache.
125 * <p>
126 * @param key
127 * @return
128 * @throws IOException
129 */
130 public ICacheElement get( Serializable key )
131 throws IOException
132 {
133 getCount++;
134 try
135 {
136 return cache.get( key );
137 }
138 catch ( UnmarshalException ue )
139 {
140 if ( log.isDebugEnabled() )
141 {
142 log.debug( "Retrying the get owing to UnmarshalException..." );
143 }
144 try
145 {
146 return cache.get( key );
147 }
148 catch ( IOException ex )
149 {
150 if ( log.isInfoEnabled() )
151 {
152 log.info( "Failed in retrying the get for the second time. " + ex.getMessage() );
153 }
154 }
155 }
156 catch ( IOException ex )
157 {
158
159
160
161 throw ex;
162 }
163 return null;
164 }
165
166 public Set getGroupKeys( String groupName )
167 throws IOException
168 {
169 return cache.getGroupKeys( groupName );
170 }
171
172 /***
173 * Adds a remove request to the remote cache.
174 * <p>
175 * @param key
176 * @return
177 * @throws IOException
178 */
179 public boolean remove( Serializable key )
180 throws IOException
181 {
182 removeCount++;
183 try
184 {
185 cacheEventQueue.addRemoveEvent( key );
186 }
187 catch ( IOException ex )
188 {
189 log.error( "Problem adding RemoveEvent to queue.", ex );
190 cacheEventQueue.destroy();
191 throw ex;
192 }
193 return false;
194 }
195
196 /***
197 * Adds a removeAll request to the remote cache.
198 * <p>
199 * @throws IOException
200 */
201 public void removeAll()
202 throws IOException
203 {
204 try
205 {
206 cacheEventQueue.addRemoveAllEvent();
207 }
208 catch ( IOException ex )
209 {
210 log.error( "Problem adding RemoveAllEvent to queue.", ex );
211 cacheEventQueue.destroy();
212 throw ex;
213 }
214 }
215
216 /*** Adds a dispose request to the remote cache. */
217 public void dispose()
218 {
219 try
220 {
221 cacheEventQueue.addDisposeEvent();
222 }
223 catch ( IOException ex )
224 {
225 log.error( "Problem adding DisposeEvent to queue.", ex );
226 cacheEventQueue.destroy();
227 }
228 }
229
230 /***
231 * No remote invocation.
232 * <p>
233 * @return The size value
234 */
235 public int getSize()
236 {
237 return cache.getSize();
238 }
239
240 /***
241 * No remote invokation.
242 * <p>
243 * @return The cacheType value
244 */
245 public int getCacheType()
246 {
247 return ICacheType.REMOTE_CACHE;
248 }
249
250 /***
251 * Returns the asyn cache status. An error status indicates either the remote connection is not
252 * available, or the asyn queue has been unexpectedly destroyed. No remote invocation.
253 * <p>
254 * @return The status value
255 */
256 public int getStatus()
257 {
258 return cacheEventQueue.isWorking() ? cache.getStatus() : CacheConstants.STATUS_ERROR;
259 }
260
261 /***
262 * Gets the cacheName attribute of the RemoteCacheNoWait object
263 * <p>
264 * @return The cacheName value
265 */
266 public String getCacheName()
267 {
268 return cache.getCacheName();
269 }
270
271 /***
272 * Replaces the remote cache service handle with the given handle and reset the event queue by
273 * starting up a new instance.
274 * <p>
275 * @param remote
276 */
277 public void fixCache( IRemoteCacheService remote )
278 {
279 cache.fixCache( remote );
280 resetEventQ();
281 return;
282 }
283
284 /***
285 * Resets the event q by first destroying the existing one and starting up new one.
286 * <p>
287 * There may be no good reason to kill the existing queue. We will sometimes need to set a new
288 * listener id, so we should create a new queue. We should let the old queue drain. If we were
289 * conencted to the failover, it would be best to finish sending items.
290 */
291 public void resetEventQ()
292 {
293 ICacheEventQueue previousQueue = cacheEventQueue;
294
295 CacheEventQueueFactory fact = new CacheEventQueueFactory();
296 this.cacheEventQueue = fact.createCacheEventQueue( new CacheAdaptor( cache ), cache.getListenerId(), cache.getCacheName(),
297 cache.getAuxiliaryCacheAttributes().getEventQueuePoolName(), cache
298 .getAuxiliaryCacheAttributes().getEventQueueTypeFactoryCode() );
299
300 if ( previousQueue.isWorking() )
301 {
302
303 if ( log.isInfoEnabled() )
304 {
305 log.info( "resetEventQ, previous queue has [" + previousQueue.size() + "] items queued up." );
306 }
307 previousQueue.destroy();
308 }
309 }
310
311 /***
312 * This is temporary. It allows the manager to get the lister.
313 * <p>
314 * @return
315 */
316 protected IRemoteCacheClient getRemoteCache()
317 {
318 return cache;
319 }
320
321 /***
322 * @return Returns the AuxiliaryCacheAttributes.
323 */
324 public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
325 {
326 return cache.getAuxiliaryCacheAttributes();
327 }
328
329 /***
330 * This is for testing only. It allows you to take a look at the event queue.
331 * <p>
332 * @return ICacheEventQueue
333 */
334 protected ICacheEventQueue getCacheEventQueue()
335 {
336 return this.cacheEventQueue;
337 }
338
339 /***
340 * Returns the stats and the cache.toString().
341 * <p>
342 * (non-Javadoc)
343 * @see java.lang.Object#toString()
344 */
345 public String toString()
346 {
347 return getStats() + "\n" + cache.toString();
348 }
349
350 /***
351 * Returns the statistics in String form.
352 * <p>
353 * @return String
354 */
355 public String getStats()
356 {
357 return getStatistics().toString();
358 }
359
360
361
362
363
364 public IStats getStatistics()
365 {
366 IStats stats = new Stats();
367 stats.setTypeName( "Remote Cache No Wait" );
368
369 ArrayList elems = new ArrayList();
370
371 IStatElement se = null;
372
373 se = new StatElement();
374 se.setName( "Status" );
375 int status = this.getStatus();
376 if ( status == CacheConstants.STATUS_ERROR )
377 {
378 se.setData( "ERROR" );
379 }
380 else if ( status == CacheConstants.STATUS_ALIVE )
381 {
382 se.setData( "ALIVE" );
383 }
384 else if ( status == CacheConstants.STATUS_DISPOSED )
385 {
386 se.setData( "DISPOSED" );
387 }
388 else
389 {
390 se.setData( "" + status );
391 }
392 elems.add( se );
393
394
395
396
397
398 IStats cStats = this.cache.getStatistics();
399 if ( cStats != null )
400 {
401 IStatElement[] cSEs = cStats.getStatElements();
402 List cL = Arrays.asList( cSEs );
403 elems.addAll( cL );
404 }
405
406
407
408 IStats eqStats = this.cacheEventQueue.getStatistics();
409 IStatElement[] eqSEs = eqStats.getStatElements();
410 List eqL = Arrays.asList( eqSEs );
411 elems.addAll( eqL );
412
413 se = new StatElement();
414 se.setName( "Get Count" );
415 se.setData( "" + this.getCount );
416 elems.add( se );
417
418 se = new StatElement();
419 se.setName( "Remove Count" );
420 se.setData( "" + this.removeCount );
421 elems.add( se );
422
423 se = new StatElement();
424 se.setName( "Put Count" );
425 se.setData( "" + this.putCount );
426 elems.add( se );
427
428
429 IStatElement[] ses = (IStatElement[]) elems.toArray( new StatElement[elems.size()] );
430 stats.setStatElements( ses );
431
432 return stats;
433 }
434
435 }