View Javadoc

1   package org.apache.jcs.auxiliary.remote;
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.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             // We don't want to destroy the queue on a get failure.
159             // The RemoteCache will Zombie and queue.
160             // Since get does not use the queue, I dont want to killing the queue.
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             // we don't expect anything, it would have all gone to the zombie
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      * (non-Javadoc)
362      * @see org.apache.jcs.auxiliary.AuxiliaryCache#getStatistics()
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         // no data gathered here
395 
396         // get the stats from the cache queue too
397         // get as array, convert to list, add list to our outer list
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         // get the stats from the event queue too
407         // get as array, convert to list, add list to our outer list
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         // get an array and put them in the Stats object
429         IStatElement[] ses = (IStatElement[]) elems.toArray( new StatElement[elems.size()] );
430         stats.setStatElements( ses );
431 
432         return stats;
433     }
434 
435 }