KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > mq > server > MessageCache


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.mq.server;
23
24 import java.lang.ref.Reference JavaDoc;
25 import java.lang.ref.ReferenceQueue JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import javax.jms.JMSException JavaDoc;
28 import javax.management.MBeanRegistration JavaDoc;
29 import javax.management.ObjectName JavaDoc;
30 import org.jboss.mq.DurableSubscriptionID;
31 import org.jboss.mq.SpyMessage;
32 import org.jboss.mq.pm.CacheStore;
33 import org.jboss.system.ServiceMBeanSupport;
34 import EDU.oswego.cs.dl.util.concurrent.SynchronizedLong;
35
36 /**
37  * This class implements a Message cache so that larger amounts of messages
38  * can be processed without running out of memory. When memory starts getting tight
39  * it starts moving messages out of memory and into a file so that they can be recovered
40  * later.
41  *
42  * The locks should be obtained in the following order:<br>
43  * mr, the relevent message we are working with<br>
44  * lruCache, when maintaining the usage order
45  *
46  * @author <a HREF="mailto:hiram.chirino@jboss.org">Hiram Chirino</a>
47  * @author <a HREF="mailto:David.Maplesden@orion.co.nz">David Maplesden</a>
48  * @author <a HREF="mailto:pra@tim.se">Peter Antman</a>
49  * @author <a HREF="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>
50  * @version $Revision: 45306 $
51  *
52  * @jmx.mbean name="jboss.mq:service=MessageCache"
53  * extends="org.jboss.system.ServiceMBean"
54  */

55 public class MessageCache extends ServiceMBeanSupport implements MessageCacheMBean, MBeanRegistration JavaDoc, Runnable JavaDoc
56 {
57    public static final long ONE_MEGABYTE = 1024L * 1000;
58    public static final long DEFAULT_HIGH_MEMORY_MARK = ONE_MEGABYTE * 50;
59    public static final long DEFAULT_MAX_MEMORY_MARK = ONE_MEGABYTE * 60;
60
61    // The cached messages are orded in a LRU linked list
62
private LRUCache lruCache = new LRUCache();
63
64    // Provides a Unique ID to MessageHanles
65
private SynchronizedLong messageCounter = new SynchronizedLong(0);
66    long cacheHits = 0;
67    long cacheMisses = 0;
68
69    protected CacheStore cacheStore;
70    ObjectName JavaDoc cacheStoreName;
71
72    private Thread JavaDoc referenceSoftner;
73
74    private long highMemoryMark = DEFAULT_HIGH_MEMORY_MARK;
75    private long maxMemoryMark = DEFAULT_MAX_MEMORY_MARK;
76
77    /** Whether to make soft references */
78    private boolean makeSoftReferences = true;
79
80    /** The last time we softened */
81    private long lastSoften = 0L;
82
83    /** soften no more than often */
84    private long softenNoMoreOftenThanMillis = 0L;
85
86    /** soften at least every */
87    private long softenAtLeastEveryMillis = 0L;
88
89    /** The length of time to wait before checking whether we should soften messages */
90    private long softenWaitMillis = 1000L;
91
92    /** The minimum number of hard messages */
93    private int minimumHard = 1;
94
95    /** The maximum number of hard messages */
96    private int maximumHard = 0;
97    
98    int softRefCacheSize = 0;
99    int totalCacheSize = 0;
100
101    // Used to get notified when message are being deleted by GC
102
ReferenceQueue JavaDoc referenceQueue = new ReferenceQueue JavaDoc();
103
104    // The historical number of softenings
105
long softenedSize = 0;
106
107    // Check the soft reference depth
108
boolean checkSoftReferenceDepth = false;
109
110    /**
111     * The <code>getInstance</code> method
112     *
113     * @return a <code>MessageCache</code> value
114     *
115     * @jmx.managed-attribute
116     */

117    public MessageCache getInstance()
118    {
119       return this;
120    }
121
122    /**
123     * Adds a message to the cache.
124     */

125    public MessageReference add(SpyMessage message, BasicQueue queue, int stored) throws javax.jms.JMSException JavaDoc
126    {
127       DurableSubscriptionID id = message.header.durableSubscriberID;
128       return addInternal(message, queue, stored, id);
129    }
130
131    /**
132     * Adds a message to the cache.
133     */

134    public MessageReference add(SpyMessage message, BasicQueue queue, int stored, DurableSubscriptionID id) throws javax.jms.JMSException JavaDoc
135    {
136       return addInternal(message, queue, stored, id);
137    }
138
139    /**
140     * Adds a message to the cache.
141     */

142    public MessageReference addInternal(SpyMessage message, BasicQueue queue, int stored, DurableSubscriptionID id) throws javax.jms.JMSException JavaDoc
143    {
144       // Create the message reference
145
MessageReference mh = new MessageReference();
146       mh.init(this, messageCounter.increment(), message, queue, id);
147       mh.setStored(stored);
148
149       // Add it to the cache
150
synchronized (mh)
151       {
152          synchronized (lruCache)
153          {
154             lruCache.addMostRecent(mh);
155             totalCacheSize++;
156          }
157       }
158       validateSoftReferenceDepth();
159
160       return mh;
161    }
162
163    /**
164     * removes a message from the cache
165     */

166    public void remove(MessageReference mr) throws JMSException JavaDoc
167    {
168       // Remove if not done already
169
removeInternal(mr, true, true);
170    }
171
172    /**
173     * removes a message from the cache without returning it to the pool
174     * used in two phase removes for joint cache/persistence
175     */

176    public void removeDelayed(MessageReference mr) throws JMSException JavaDoc
177    {
178       // Remove from the cache
179
removeInternal(mr, true, false);
180    }
181
182    /**
183     * removes a message from the cache but does not clear it,
184     * used in softening
185     */

186    void soften(MessageReference mr) throws JMSException JavaDoc
187    {
188       // Remove from the cache
189
removeInternal(mr, false, false);
190
191       if (makeSoftReferences)
192          softRefCacheSize++;
193    }
194
195    /**
196     * removes a message from the cache
197     */

198    protected void removeInternal(MessageReference mr, boolean clear, boolean reset) throws JMSException JavaDoc
199    {
200       synchronized (mr)
201       {
202          if (mr.stored != MessageReference.REMOVED)
203          {
204             synchronized (lruCache)
205             {
206                if (mr.hardReference != null) //If message is not hard, dont do lru stuff
207
lruCache.remove(mr);
208                if (clear)
209                   totalCacheSize--;
210             }
211             if (clear)
212                mr.clear();
213             //Will remove it from storage if stored
214
}
215
216          if (reset)
217             mr.reset();
218          //Return to the pool
219
}
220    }
221
222    /**
223     * The strategy is that we keep the most recently used messages as
224     * Hard references. Then we make the older ones soft references. Making
225     * something a soft reference stores it to disk so we need to avoid making
226     * soft references if we can avoid it. But once it is made a soft reference does
227     * not mean that it is removed from memory. Depending on how agressive the JVM's
228     * GC is, it may stay around long enough for it to be used by a client doing a read,
229     * saving us read from the file system. If memory gets tight the GC will remove
230     * the soft references. What we want to do is make sure there are at least some
231     * soft references available so that the GC can reclaim memory.
232     * @see Runnable#run()
233     */

234    public void run()
235    {
236       try
237       {
238          while (true)
239          {
240             // Get the next soft reference that was canned by the GC
241
Reference JavaDoc r = null;
242             if (checkSoftReferenceDepth)
243                r = referenceQueue.poll();
244             else
245                r = referenceQueue.remove(softenWaitMillis);
246             if (r != null)
247             {
248                softRefCacheSize--;
249                // the GC will free a set of messages together, so we poll them
250
// all before we validate the soft reference depth.
251
while ((r = referenceQueue.poll()) != null)
252                {
253                   softRefCacheSize--;
254                }
255                if (log.isTraceEnabled())
256                   log.trace("soft reference cache size is now: " + softRefCacheSize);
257
258                checkSoftReferenceDepth = true;
259             }
260
261             long now = System.currentTimeMillis();
262             
263             // Don't try to soften too often
264
if (softenNoMoreOftenThanMillis > 0 && (now - lastSoften < softenNoMoreOftenThanMillis))
265                checkSoftReferenceDepth = false;
266
267             // Is it a while since we last softened?
268
else if (softenAtLeastEveryMillis > 0 && (now - lastSoften > softenAtLeastEveryMillis))
269                checkSoftReferenceDepth = true;
270
271             // Should we check for softening
272
if (checkSoftReferenceDepth)
273             {
274                checkSoftReferenceDepth = validateSoftReferenceDepth();
275
276                // Did the softening complete?
277
if (checkSoftReferenceDepth == false)
278                   lastSoften = now;
279             }
280          }
281       }
282       catch (InterruptedException JavaDoc e)
283       {
284          // Signal to exit the thread.
285
}
286       catch (Throwable JavaDoc t)
287       {
288          log.error("Message Cache Thread Stopped: ", t);
289       }
290       log.debug("Thread exiting.");
291    }
292
293    /**
294     * This method is in charge of determining if it time to convert some
295     * hard references over to soft references.
296     */

297    boolean validateSoftReferenceDepth() throws JMSException JavaDoc
298    {
299       boolean trace = log.isTraceEnabled();
300       
301       // Loop until softening is not required or we find a message we can soften
302
while (getState() == ServiceMBeanSupport.STARTED)
303       {
304          MessageReference messageToSoften = null;
305
306          synchronized (lruCache)
307          {
308             // howmany to change over to soft refs
309
int softenCount = 0;
310             int hardCount = getHardRefCacheSize();
311             int softCount = getSoftRefCacheSize();
312
313             // Only soften down to a minimum
314
if (hardCount <= minimumHard)
315                return false;
316
317             long currentMem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
318             if (currentMem > highMemoryMark)
319             {
320                // we need to get more aggresive... how much?? lets get
321
// a mesurment from 0 to 1
322
float severity = ((float) (currentMem - highMemoryMark)) / (maxMemoryMark - highMemoryMark);
323                severity = Math.min(severity, 1.0F);
324                if (trace)
325                   log.trace("Memory usage serverity=" + severity);
326                int totalMessageInMem = hardCount + softCount;
327                int howManyShouldBeSoft = (int) ((totalMessageInMem) * severity);
328                softenCount = howManyShouldBeSoft - softCount;
329             }
330
331             // Are there too many messages in memory?
332
if (maximumHard > 0)
333             {
334                int removeCount = hardCount - maximumHard;
335                if (removeCount > 0 && removeCount > softenCount)
336                   softenCount = removeCount;
337             }
338             
339             // We can only do so much, somebody else is using all the memory?
340
if (softenCount > hardCount)
341             {
342                if (trace)
343                   log.trace("Soften count " + softenCount + " greater than hard references " + hardCount);
344                softenCount = hardCount;
345             }
346
347             // Ignore soften counts of 1 since this will happen too often even
348
// if the serverity is low since it will round up.
349
if (softenCount > 1 || (maximumHard > 0 && hardCount > maximumHard))
350             {
351                if (trace)
352                   log.trace("Need to soften " + softenCount + " messages");
353                Node node = lruCache.getLeastRecent();
354                messageToSoften = (MessageReference) node.data;
355             }
356          }
357
358          // No softening required
359
if (messageToSoften == null)
360             return false;
361
362          synchronized (messageToSoften)
363          {
364             // Soften unless it was removed
365
if (messageToSoften.messageCache != null && messageToSoften.stored != MessageReference.REMOVED)
366             {
367                messageToSoften.makeSoft();
368                if (messageToSoften.stored == MessageReference.STORED)
369                {
370                   softenedSize++;
371                   return true;
372                }
373                else if (messageToSoften.isPersistent())
374                {
375                   // Avoid going into a cpu loop if there are persistent
376
// messages just about to be persisted
377
return false;
378                }
379             }
380             else if (trace)
381                log.trace("not softening removed message " + messageToSoften);
382          }
383       }
384       return false;
385    }
386
387    /**
388     * This gets called when a MessageReference is de-referenced.
389     * We will pop it to the top of the RLU
390     */

391    void messageReferenceUsedEvent(MessageReference mh, boolean wasHard) throws JMSException JavaDoc
392    {
393       synchronized (mh)
394       {
395          synchronized (lruCache)
396          {
397             if (wasHard)
398                lruCache.makeMostRecent(mh);
399             else
400             {
401                lruCache.addMostRecent(mh);
402             }
403          }
404       }
405
406       if (wasHard == false)
407          checkSoftReferenceDepth = true;
408    }
409
410    //////////////////////////////////////////////////////////////////////////////////
411
// Perisitence methods used by the MessageReference.
412
//////////////////////////////////////////////////////////////////////////////////
413
SpyMessage loadFromStorage(MessageReference mh) throws JMSException JavaDoc
414    {
415       return cacheStore.loadFromStorage(mh);
416    }
417
418    void saveToStorage(MessageReference mh, SpyMessage message) throws JMSException JavaDoc
419    {
420       cacheStore.saveToStorage(mh, message);
421    }
422
423    void removeFromStorage(MessageReference mh) throws JMSException JavaDoc
424    {
425       cacheStore.removeFromStorage(mh);
426    }
427
428    //////////////////////////////////////////////////////////////////////////////////
429
//
430
// The following section deals the the JMX interface to manage the Cache
431
//
432
//////////////////////////////////////////////////////////////////////////////////
433

434    /**
435     * This gets called to start the cache service. Synch. by start
436     */

437    protected void startService() throws Exception JavaDoc
438    {
439       setupCacheStore();
440
441       referenceSoftner = new Thread JavaDoc(this, "JBossMQ Cache Reference Softner");
442       referenceSoftner.setDaemon(true);
443       referenceSoftner.start();
444    }
445
446    protected void setupCacheStore() throws Exception JavaDoc
447    {
448       cacheStore = (CacheStore) getServer().getAttribute(cacheStoreName, "Instance");
449    }
450
451    /**
452     * This gets called to stop the cache service.
453     */

454    protected void stopService()
455    {
456       synchronized (lruCache)
457       {
458          referenceSoftner.interrupt();
459          referenceSoftner = null;
460       }
461       cacheStore = null;
462    }
463
464    /**
465     * Gets the hardRefCacheSize
466     * @return Returns a int
467     *
468     * @jmx.managed-attribute
469     */

470    public int getHardRefCacheSize()
471    {
472       synchronized (lruCache)
473       {
474          return lruCache.size();
475       }
476    }
477
478    /**
479     * The <code>getSoftenedSize</code> method
480     *
481     * @return a <code>long</code> value
482     *
483     * @jmx.managed-attribute
484     */

485    public long getSoftenedSize()
486    {
487       return softenedSize;
488    }
489
490    /**
491     * Gets the softRefCacheSize
492     * @return Returns a int
493     *
494     * @jmx.managed-attribute
495     */

496    public int getSoftRefCacheSize()
497    {
498       return softRefCacheSize;
499    }
500
501    /**
502     * Gets the totalCacheSize
503     * @return Returns a int
504     *
505     * @jmx.managed-attribute
506     */

507    public int getTotalCacheSize()
508    {
509       return totalCacheSize;
510    }
511
512    /**
513     * Gets the cacheMisses
514     * @return Returns a int
515     *
516     * @jmx.managed-attribute
517     */

518    public long getCacheMisses()
519    {
520       return cacheMisses;
521    }
522
523    /**
524     * Gets the cacheHits
525     * @return Returns a long
526     *
527     * @jmx.managed-attribute
528     */

529    public long getCacheHits()
530    {
531       return cacheHits;
532    }
533
534    /**
535     * Gets whether to make soft references
536     *
537     * @jmx.managed-attribute
538     * @return true when making soft references
539     */

540    public boolean getMakeSoftReferences()
541    {
542       return makeSoftReferences;
543    }
544    
545    /**
546     * Sets whether to make soft references
547     *
548     * @jmx.managed-attribute
549     * @param true to make soft references
550     */

551    public void setMakeSoftReferences(boolean makeSoftReferences)
552    {
553       this.makeSoftReferences = makeSoftReferences;
554    }
555
556    /**
557     * Gets the minimum number of hard messages
558     *
559     * @jmx.managed-attribute
560     * @return the minimum number of hard messages
561     */

562    public int getMinimumHard()
563    {
564       return minimumHard;
565    }
566    
567    /**
568     * Sets the minimum number of hard messages
569     *
570     * @jmx.managed-attribute
571     * @param minimumHard the minimum number of hard messages
572     */

573    public void setMinimumHard(int minimumHard)
574    {
575       if (minimumHard < 1)
576          this.minimumHard = 1;
577       else
578          this.minimumHard = minimumHard;
579    }
580
581    /**
582     * Gets the maximum number of hard messages
583     *
584     * @jmx.managed-attribute
585     * @return the minimum number of hard messages
586     */

587    public int getMaximumHard()
588    {
589       return maximumHard;
590    }
591    
592    /**
593     * Sets the maximum number of hard messages
594     *
595     * @jmx.managed-attribute
596     * @param maximumHard the maximum number of hard messages
597     */

598    public void setMaximumHard(int maximumHard)
599    {
600       if (maximumHard < 0)
601          this.maximumHard = 0;
602       else
603          this.maximumHard = maximumHard;
604    }
605
606    /**
607     * Gets the length of time to wait before checking whether we should soften
608     *
609     * @jmx.managed-attribute
610     * @return the time to wait
611     */

612    public long getSoftenWaitMillis()
613    {
614       return softenWaitMillis;
615    }
616    
617    /**
618     * Sets the length of time to wait before checking whether we should soften
619     *
620     * @jmx.managed-attribute
621     * @param millis the time to wait in millis
622     */

623    public void setSoftenWaitMillis(long millis)
624    {
625       if (millis < 1000)
626          softenWaitMillis = 1000;
627       else
628          softenWaitMillis = millis;
629    }
630
631    /**
632     * Gets the minimum length between softening checks
633     *
634     * @jmx.managed-attribute
635     * @return the time to wait
636     */

637    public long getSoftenNoMoreOftenThanMillis()
638    {
639       return softenNoMoreOftenThanMillis;
640    }
641    
642    /**
643     * Sets the minimum length between softening checks
644     *
645     * @jmx.managed-attribute
646     * @param wait the time between checks
647     */

648    public void setSoftenNoMoreOftenThanMillis(long millis)
649    {
650       if (millis < 0)
651          softenNoMoreOftenThanMillis = 0;
652       else
653          softenNoMoreOftenThanMillis = millis;
654    }
655
656    /**
657     * Gets the maximum length between softening checks
658     *
659     * @jmx.managed-attribute
660     * @return the time
661     */

662    public long getSoftenAtLeastEveryMillis()
663    {
664       return softenAtLeastEveryMillis;
665    }
666    
667    /**
668     * Sets the minimum length between softening checks
669     *
670     * @jmx.managed-attribute
671     * @param wait the time between checks
672     */

673    public void setSoftenAtLeastEveryMillis(long millis)
674    {
675       if (millis < 0)
676          softenAtLeastEveryMillis = 0;
677       else
678          softenAtLeastEveryMillis = millis;
679    }
680
681    /**
682     * Gets the highMemoryMark
683     * @return Returns a long
684     *
685     * @jmx.managed-attribute
686     */

687    public long getHighMemoryMark()
688    {
689       return highMemoryMark / ONE_MEGABYTE;
690    }
691    /**
692     * Sets the highMemoryMark
693     * @param highMemoryMark The highMemoryMark to set
694     *
695     * @jmx.managed-attribute
696     */

697    public void setHighMemoryMark(long highMemoryMark)
698    {
699       if (highMemoryMark > 0)
700          this.highMemoryMark = highMemoryMark * ONE_MEGABYTE;
701       else
702          this.highMemoryMark = 0;
703    }
704
705    /**
706     * Gets the maxMemoryMark
707     * @return Returns a long
708     *
709     * @jmx.managed-attribute
710     */

711    public long getMaxMemoryMark()
712    {
713       return maxMemoryMark / ONE_MEGABYTE;
714    }
715
716    /**
717     * Sets the maxMemoryMark
718     * @param maxMemoryMark The maxMemoryMark to set
719     *
720     * @jmx.managed-attribute
721     */

722    public void setMaxMemoryMark(long maxMemoryMark)
723    {
724       if (maxMemoryMark > 0)
725          this.maxMemoryMark = maxMemoryMark * ONE_MEGABYTE;
726       else
727          this.maxMemoryMark = 0;
728    }
729
730    /**
731     * Gets the CurrentMemoryUsage
732     * @return Returns a long
733     *
734     * @jmx.managed-attribute
735     */

736    public long getCurrentMemoryUsage()
737    {
738       return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / ONE_MEGABYTE;
739    }
740
741    /**
742     * @see ServiceMBeanSupport#getName()
743     */

744    public String JavaDoc getName()
745    {
746       return "MessageCache";
747    }
748
749    /**
750     * @see MessageCacheMBean#setCacheStore(ObjectName)
751     *
752     * @jmx.managed-attribute
753     */

754    public void setCacheStore(ObjectName JavaDoc cacheStoreName)
755    {
756       this.cacheStoreName = cacheStoreName;
757    }
758
759    /**
760     * The <code>getCacheStore</code> method
761     *
762     * @return an <code>ObjectName</code> value
763     *
764     * @jmx.managed-attribute
765     */

766    public ObjectName JavaDoc getCacheStore()
767    {
768       return cacheStoreName;
769    }
770
771    /**
772     * This class implements a simple, efficient LRUCache. It is pretty much a
773     * cut down version of the code in org.jboss.pool.cache.LeastRecentlyUsedCache
774     */

775    class LRUCache
776    {
777       int currentSize = 0;
778       //maps objects to their nodes
779
HashMap JavaDoc map = new HashMap JavaDoc();
780       Node mostRecent = null;
781       Node leastRecent = null;
782       public void addMostRecent(Object JavaDoc o)
783       {
784          Node newNode = new Node();
785          newNode.data = o;
786          //insert into map
787
Object JavaDoc oldNode = map.put(o, newNode);
788          if (oldNode != null)
789          {
790             map.put(o, oldNode);
791             throw new RuntimeException JavaDoc("Can't add object '" + o + "' to LRUCache that is already in cache.");
792          }
793          //insert into linked list
794
if (mostRecent == null)
795          {
796             //first element
797
mostRecent = newNode;
798             leastRecent = newNode;
799          }
800          else
801          {
802             newNode.lessRecent = mostRecent;
803             mostRecent.moreRecent = newNode;
804             mostRecent = newNode;
805          }
806          ++currentSize;
807       }
808       // Not used anywhere!!
809
public void addLeastRecent(Object JavaDoc o)
810       {
811          Node newNode = new Node();
812          newNode.data = o;
813          //insert into map
814
Object JavaDoc oldNode = map.put(o, newNode);
815          if (oldNode != null)
816          {
817             map.put(o, oldNode);
818             throw new RuntimeException JavaDoc("Can't add object '" + o + "' to LRUCache that is already in cache.");
819          }
820          //insert into linked list
821
if (leastRecent == null)
822          {
823             //first element
824
mostRecent = newNode;
825             leastRecent = newNode;
826          }
827          else
828          {
829             newNode.moreRecent = leastRecent;
830             leastRecent.lessRecent = newNode;
831             leastRecent = newNode;
832          }
833          ++currentSize;
834       }
835       public void remove(Object JavaDoc o)
836       {
837          //remove from map
838
Node node = (Node) map.remove(o);
839          if (node == null)
840             throw new RuntimeException JavaDoc("Can't remove object '" + o + "' that is not in cache.");
841          //remove from linked list
842
Node more = node.moreRecent;
843          Node less = node.lessRecent;
844          if (more == null)
845          { //means node is mostRecent
846
mostRecent = less;
847             if (mostRecent != null)
848             {
849                mostRecent.moreRecent = null; //Mark it as beeing at the top of tree
850
}
851          }
852          else
853          {
854             more.lessRecent = less;
855          }
856          if (less == null)
857          { //means node is leastRecent
858
leastRecent = more;
859             if (leastRecent != null)
860             {
861                leastRecent.lessRecent = null; //Mark it last in tree
862
}
863          }
864          else
865          {
866             less.moreRecent = more;
867          }
868          --currentSize;
869       }
870       public void makeMostRecent(Object JavaDoc o)
871       {
872          //get node from map
873
Node node = (Node) map.get(o);
874          if (node == null)
875             throw new RuntimeException JavaDoc("Can't make most recent object '" + o + "' that is not in cache.");
876          //reposition in linked list, first remove
877
Node more = node.moreRecent;
878          Node less = node.lessRecent;
879          if (more == null) //means node is mostRecent
880
return;
881          else
882             more.lessRecent = less;
883          if (less == null) //means node is leastRecent
884
leastRecent = more;
885          else
886             less.moreRecent = more;
887          //now add back in at most recent position
888
node.lessRecent = mostRecent;
889          node.moreRecent = null; //We are at the top
890
mostRecent.moreRecent = node;
891          mostRecent = node;
892       }
893       public int size()
894       {
895          return currentSize;
896       }
897       public Node getMostRecent()
898       {
899          return mostRecent;
900       }
901       public Node getLeastRecent()
902       {
903          return leastRecent;
904       }
905    }
906
907    static class Node
908    {
909       Node moreRecent = null;
910       Node lessRecent = null;
911       Object JavaDoc data = null;
912    }
913 }
914
Popular Tags