View Javadoc

1   //
2   //Copyright (c) 2003, Caltha - Gajda, Krzewski, Mach, Potempski Sp.J.
3   //All rights reserved.
4   //
5   //Redistribution and use in source and binary forms, with or without modification, 
6   //are permitted provided that the following conditions are met:
7   //
8   //* Redistributions of source code must retain the above copyright notice, 
9   //this list of conditions and the following disclaimer.
10  //* Redistributions in binary form must reproduce the above copyright notice, 
11  //this list of conditions and the following disclaimer in the documentation 
12  //and/or other materials provided with the distribution.
13  //* Neither the name of the Caltha - Gajda, Krzewski, Mach, Potempski Sp.J. 
14  //nor the names of its contributors may be used to endorse or promote products 
15  //derived from this software without specific prior written permission.
16  //
17  //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
18  //AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
19  //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  //IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
21  //INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
22  //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23  //OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
24  //WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
25  //ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
26  //POSSIBILITY OF SUCH DAMAGE.
27  //
28  
29  package org.objectledge.cache;
30  
31  import java.io.PrintWriter;
32  import java.lang.reflect.Modifier;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  import java.util.SortedMap;
41  import java.util.TreeMap;
42  
43  import org.jcontainer.dna.Configuration;
44  import org.jcontainer.dna.ConfigurationException;
45  import org.jcontainer.dna.Logger;
46  import org.objectledge.ComponentInitializationError;
47  import org.objectledge.cache.spi.CacheFactorySPI;
48  import org.objectledge.cache.spi.ConfigurableMap;
49  import org.objectledge.cache.spi.ConfigurableValueFactory;
50  import org.objectledge.cache.spi.DistributedMap;
51  import org.objectledge.cache.spi.FactoryMap;
52  import org.objectledge.cache.spi.ForgetfullMap;
53  import org.objectledge.cache.spi.LRUMap;
54  import org.objectledge.cache.spi.LayeredMap;
55  import org.objectledge.cache.spi.SoftMap;
56  import org.objectledge.cache.spi.StatisticsMap;
57  import org.objectledge.cache.spi.TimeoutMap;
58  import org.objectledge.context.Context;
59  import org.objectledge.database.persistence.Persistence;
60  import org.objectledge.notification.Notification;
61  import org.objectledge.threads.Task;
62  import org.objectledge.threads.ThreadPool;
63  
64  /**
65   * DefaultCacheFactory component.
66   * 
67   * <p>An instance declaration is a list of map layer declarations.
68   * A map layer declaration is composed of a map type name (both
69   * standard and custom types may be used) and a pair of parentheses. Optional
70   * configuration string may be placed betweene the parens. The meaning of the
71   * string is depenant upon the implementation class. If there is a non-empty
72   * configuration string, the map implementation is required to support {@link
73   * ConfigurableMap} interface. Layers declared first are the deeper than those
74   * declared later. The implemenation of map types used on layers deeper than
75   * layer one are required to support {@link LayeredMap} interface. The layer
76   * number <i>n</i> becomes the delegate of the layer <i>n+1</i>.</p>
77   * 
78   * @author <a href="mailto:rafal@caltha.pl">Rafal Krzewski</a>
79   * @version $Id: DefaultCacheFactory.java,v 1.4 2006/02/08 18:19:56 zwierzem Exp $
80   */
81  public class DefaultCacheFactory
82      implements CacheFactorySPI, CacheFactory
83  {
84      // constants ////////////////////////////////////////////////////////////
85      /*** Type constant for HashMap. */
86      public static final String HASH_MAP_TYPE = "HashMap";
87  
88      /*** Type constant for TimeoutMap. */
89      public static final String TIMEOUT_MAP_TYPE = "TimeoutMap";
90  
91      /*** Type constant for LRUMap. */
92      public static final String LRU_MAP_TYPE = "LRUMap";
93  
94      /*** Type constant for SoftMap. */
95      public static final String SOFT_MAP_TYPE = "SoftMap";
96  
97      /*** Type constant for FactoryMap. */
98      public static final String FACTORY_MAP_TYPE = "FactoryMap";
99  
100     /*** Type constant for DistributedMap. */
101     public static final String DISTRIBUTED_MAP_TYPE = "DistributedMap";
102 
103     /*** Type constant for StatisticsMap. */
104     public static final String STATISTICS_MAP_TYPE = "StatisticsMap";
105 
106     /*** Type constant for ForgetFullMap. */
107     public static final String FORGETFULL_MAP_TYPE = "ForgetfullMap";
108     
109     /*** The default implementation HashMap implementation. */
110     public static final String HASH_MAP_CLASS_DEFALUT =
111         "java.util.HashMap";
112 
113     /*** The default implementation TimeoutMap implementation. */
114     public static final String TIMEOUT_MAP_CLASS_DEFALUT =
115         "org.objectledge.cache.impl.TimeoutMapImpl";
116 
117     /*** The default implementation LRUMap implementation. */
118     public static final String LRU_MAP_CLASS_DEFALUT =
119         "org.objectledge.cache.impl.LRUMapImpl";
120     
121     /*** The default implementation SoftMap implementation. */
122     public static final String SOFT_MAP_CLASS_DEFALUT =
123         "org.objectledge.cache.impl.SoftMapImpl";
124 
125     /*** The default implementation DistributedMap implementation. */
126     public static final String DISTRIBUTED_MAP_CLASS_DEFALUT =
127         "org.objectledge.cache.impl.DistributedMapImpl";
128 
129     /*** The default implementation FactoryMap implementation. */
130     public static final String FACTORY_MAP_CLASS_DEFALUT =
131         "org.objectledge.cache.impl.FactoryMapImpl";
132 
133     /*** The default implementation StatisticsMap implementation. */
134     public static final String STATISTICS_MAP_CLASS_DEFALUT =
135         "org.objectledge.cache.impl.StatisticsMapImpl";
136 
137     /*** The default implementation StatisticsMap implementation. */
138     public static final String FORGETFULL_MAP_CLASS_DEFALUT =
139         "org.objectledge.cache.impl.ForgetfullMapImpl";
140     // member objects ////////////////////////////////////////////////////////
141     
142     /*** The registered StatisticsMaps */
143     private List<StatisticsMap> statistics = new ArrayList<StatisticsMap>();
144 
145     /*** Mapping of *_TYPE constants into configured implementation classes. */
146     private Map<String,Class> implClasses = new HashMap<String,Class>();
147 
148     /*** instance prepared configurations map. */
149     private Map<String,Configuration> instanceConfigurations = new HashMap<String,Configuration>();
150     
151     /*** Factory configurations */
152     private Map<String,Configuration> factoryConfigurations = new HashMap<String,Configuration>();
153 
154     /*** Configured map instances. */
155     private Map<String,Map> instances = new HashMap<String,Map>();
156 
157     /*** DelayedUpdate queue (target update time -&gt; object)*/
158     private SortedMap<Long,Set<DelayedUpdate>> queue = new TreeMap<Long,Set<DelayedUpdate>>();
159 
160     /*** DelayedUpdate queue helper map (object -&gt; target update time)*/
161     private Map<DelayedUpdate,Long> queueHelper = new HashMap<DelayedUpdate,Long>();
162 
163     /*** The configuration */
164     private Configuration config;
165     
166     /*** The logging facility. */
167     private Logger logger;
168     
169     /*** The thread pool */
170     private ThreadPool threadPool;
171     
172     /*** The notification */
173     private Notification notification;
174     
175     /*** The persistence */
176     private Persistence persistence;
177 
178     // initialization ////////////////////////////////////////////////////////
179 
180     /***
181      * Component constructor.
182      * 
183      * @param config the configuration.
184      * @param logger the logger.
185      * @param threadPool the thread pool.
186      * @param notification the notification.
187      * @param persistence the persistence.
188      * @throws ConfigurationException thrown if configuration is invalid.
189      * @throws ClassNotFoundException thrown if one of the class not found.
190      */
191     public DefaultCacheFactory(Configuration config, Logger logger, 
192                     ThreadPool threadPool, Notification notification,
193                     Persistence persistence)
194         throws ConfigurationException, ClassNotFoundException
195     {
196         this.config = config;
197         this.logger = logger;
198         this.threadPool = threadPool;
199         this.notification = notification;
200         this.persistence = persistence;
201         
202         Map<String, String> classMap = new HashMap<String, String>();
203         classMap.put(HASH_MAP_TYPE, HASH_MAP_CLASS_DEFALUT);
204         classMap.put(TIMEOUT_MAP_TYPE, TIMEOUT_MAP_CLASS_DEFALUT);
205         classMap.put(LRU_MAP_TYPE, LRU_MAP_CLASS_DEFALUT);
206         classMap.put(SOFT_MAP_TYPE, SOFT_MAP_CLASS_DEFALUT);
207         classMap.put(DISTRIBUTED_MAP_TYPE, DISTRIBUTED_MAP_CLASS_DEFALUT);
208         classMap.put(FACTORY_MAP_TYPE, FACTORY_MAP_CLASS_DEFALUT);
209         classMap.put(STATISTICS_MAP_TYPE, STATISTICS_MAP_CLASS_DEFALUT);
210         classMap.put(FORGETFULL_MAP_TYPE, FORGETFULL_MAP_CLASS_DEFALUT);
211         
212         Map<String, Class> ifaceMap = new HashMap<String, Class>();
213         ifaceMap.put(HASH_MAP_TYPE, Map.class);
214         ifaceMap.put(TIMEOUT_MAP_TYPE, TimeoutMap.class);
215         ifaceMap.put(LRU_MAP_TYPE, LRUMap.class);
216         ifaceMap.put(SOFT_MAP_TYPE, SoftMap.class);
217         ifaceMap.put(DISTRIBUTED_MAP_TYPE, DistributedMap.class);
218         ifaceMap.put(FACTORY_MAP_TYPE, FactoryMap.class);
219         ifaceMap.put(STATISTICS_MAP_TYPE, StatisticsMap.class);
220         ifaceMap.put(FORGETFULL_MAP_TYPE, ForgetfullMap.class);
221         
222         Configuration[] custom = config.getChildren("implementation");
223         for(int i=0; i<custom.length; i++)
224         {
225             String type = custom[i].getAttribute("type");
226             String clazz = custom[i].getAttribute("class");
227             classMap.put(type,clazz);
228         }
229         Iterator it = classMap.keySet().iterator();
230         while(it.hasNext())
231         {
232             String type = (String)it.next();
233             String clazz = (String)classMap.get(type);
234             Class iface = (Class)ifaceMap.get(type);
235             if(iface == null)
236             {
237                 iface = Map.class;
238             }
239             initImpl(type, clazz, iface);    
240         }
241         
242         Configuration[] aliases = config.getChildren("alias");
243         for(int i = 0; i < aliases.length; i++)
244         {
245             String name = aliases[i].getAttribute("name");
246             instanceConfigurations.put(name,aliases[i]);
247         }
248         Configuration[] factories = config.getChildren("factory");
249         for(int i = 0; i < factories.length; i++)
250         {
251             String name = factories[i].getAttribute("name");
252             factoryConfigurations.put(name,factories[i]);
253         }
254         Configuration[] instanceNodes = config.getChildren("instance");
255         for(int i =0; i < instanceNodes.length;i++)
256         {
257             String name = instanceNodes[i].getAttribute("name");
258             Configuration instanceConfig = instanceNodes[i];
259             String alias = instanceNodes[i].getAttribute("alias",null);
260             if(alias != null)
261             {
262                 instanceConfig = (Configuration)instanceConfigurations.get(alias);
263                 if(instanceConfig == null)
264                 {
265                     throw new ComponentInitializationError(
266                         "cannot find conifured alias: '"+alias+"'");
267                 }
268             }
269             Map map = buildInstance(name, instanceConfig);
270             instances.put(name, map);
271         }
272         threadPool.runDaemon(new DelayedUpdateTask());
273     }                          
274 
275     // CacheFactorySPI interface ////////////////////////////////////////////////
276 
277     /***
278      * {@inheritDoc}
279      */
280     public Map getMap(String type)
281     {
282         Class cl = (Class)implClasses.get(type);
283         if(cl == null)
284         {
285             throw new IllegalArgumentException("unknown map type "+type);
286         }
287         try
288         {
289             return (Map)cl.newInstance();
290         }
291         ///CLOVER:OFF
292         catch(VirtualMachineError t)
293         {
294             throw t;
295         }
296         catch(ThreadDeath t)
297         {
298             throw t;
299         }
300         catch(Throwable t)
301         {
302             throw new RuntimeException("failed to instantaite map of type "+type, t);
303         }
304         ///CLOVER:ON
305     }
306 
307     /***
308      * {@inheritDoc}
309      */
310     public synchronized Map getInstance(String name)
311     {
312         Map map = (Map)instances.get(name);
313         if(map == null)
314         {
315             throw new IllegalArgumentException("configuration "+name+
316                                                 " not defined in config file");
317         }
318         return map;
319     }
320 
321     /***
322      * {@inheritDoc}
323      */
324     public synchronized Map getInstance(String name, String configAlias)
325         throws ConfigurationException
326     {
327         Map map = (Map)instances.get(name);
328         if(map == null)
329         {
330             Configuration instanceConfiguration = 
331                 (Configuration)instanceConfigurations.get(configAlias);
332             if(instanceConfiguration == null)
333             {
334                 throw new IllegalArgumentException("configuration "+configAlias+
335                                                    " not defined in config file");
336             }
337             map = buildInstance(name, instanceConfiguration);
338             instances.put(name, map);
339         }
340         return map;
341     }
342 
343     /***
344      * {@inheritDoc}
345      */
346     public ValueFactory getValueFactory(String factory, String map)
347     {
348         Configuration factoryConfig = (Configuration)factoryConfigurations.get(factory);
349         String fclass = factoryConfig.getAttribute("class",null);
350         Object obj;
351         if(fclass == null)
352         {
353             throw new IllegalArgumentException("factory "+factory+" not defined in config file");
354         }
355         try
356         {
357             obj = Class.forName(fclass).newInstance();
358         }
359         catch(ClassNotFoundException e)
360         {
361             throw new IllegalArgumentException(fclass+" not found");
362         }
363         catch(Exception e)
364         {
365             throw new IllegalArgumentException(fclass+" not found");
366         }
367         
368         if(!(obj instanceof ConfigurableValueFactory))
369         {
370             throw new IllegalArgumentException(fclass+" does not implement " +
371                                                "ConfigurableValueFactory interface");
372         } 
373         ((ConfigurableValueFactory)obj).configure(this, map, factoryConfig);
374         return (ValueFactory)obj;
375     }
376 
377     /***
378      * {@inheritDoc}
379      */
380     public Map getHashMap()
381     {
382         Map map = getMap(HASH_MAP_TYPE);
383         return map;
384     }
385 
386     /***
387      * {@inheritDoc}
388      */
389     public Map getTimeoutMap(long timeoutMillis)
390     {
391         TimeoutMap map = (TimeoutMap)getMap(TIMEOUT_MAP_TYPE);
392         map.setTimeout(timeoutMillis);
393         return map;
394     }
395     
396     /***
397      * {@inheritDoc}
398      */
399     public Map getLRUMap(int capacity)
400     {
401         LRUMap map = (LRUMap)getMap(LRU_MAP_TYPE);
402         map.setCapacity(capacity);
403         return map;
404     }
405     
406     /***
407      * {@inheritDoc}
408      */
409     public Map getSoftMap(int protect)
410     {
411         SoftMap map = (SoftMap)getMap(SOFT_MAP_TYPE);
412         map.setProtect(protect);
413         return map;
414     }
415     
416     /***
417      * {@inheritDoc}
418      */
419     public org.objectledge.cache.DistributedMap getDistributedMap(String name, Map delegate)
420     {
421         DistributedMap map = (DistributedMap)getMap(DISTRIBUTED_MAP_TYPE);
422         map.setDelegate(delegate);
423         map.attach(notification, name);
424         return map;
425     }
426 
427     /***
428      * {@inheritDoc}
429      */
430     public Map getFactoryMap(ValueFactory factory, Map delegate)
431     {
432         FactoryMap map = (FactoryMap)getMap(FACTORY_MAP_TYPE);
433         map.setDelegate(delegate);
434         map.setFactory(factory);
435         return map;
436     }
437     
438     /***
439      * {@inheritDoc}
440      */
441     public Map getStatisticsMap(String name, Map delegate)
442     {
443         StatisticsMap map = (StatisticsMap)getMap(STATISTICS_MAP_TYPE);
444         map.setDelegate(delegate);
445         map.setName(name);
446         addStatisticsMap(map);
447         return map;
448     }
449 
450     /***
451      * {@inheritDoc}
452      */
453     public ValueFactory getPersitenceValueFactory(Class valueClass)
454     {
455         PersistenceValueFactory factory = new PersistenceValueFactory();
456         factory.init(valueClass, persistence);
457         return factory;
458     }
459 
460     /***
461      * {@inheritDoc}
462      */
463     public void register(DelayedUpdate object)
464     {
465         synchronized(queue)
466         {
467             // queue helper provides a reverse mapping, so we don't need have
468             // to perform liner search
469             Long target = (Long)queueHelper.get(object);
470             Set<DelayedUpdate> set;
471             if(target != null)
472             {
473                 set = queue.get(target);
474                 set.remove(object);
475                 if(set.isEmpty())
476                 {
477                     queue.remove(target);
478                 }
479             }
480             target = new Long(System.currentTimeMillis()+object.getUpdateLatency());
481             set = queue.get(target);
482             if(set == null)
483             {
484                 set = new HashSet<DelayedUpdate>();
485                 queue.put(target, set);
486                 queueHelper.put(object, target);
487             }
488             set.add(object);
489             queue.notify();
490         }
491     }
492 
493     /***
494      * Prints the report on the specified PrintWriter.
495      *
496      * @param out the PrintWriter to print report into.
497      */
498     public void getStatus(PrintWriter out)
499     {
500         synchronized(statistics)
501         {
502             for(int i=0; i<statistics.size(); i++)
503             {
504                 out.print(((StatisticsMap)statistics.get(i)).getStatistics());
505             }
506         }
507     }
508 
509     /***
510      * {@inheritDoc}
511      */
512     public void addStatisticsMap(StatisticsMap map)
513     {
514         synchronized(statistics)
515         {
516             statistics.add(map);
517         }
518     }
519     
520     /***
521      * {@inheritDoc}
522      */
523     public Notification getNotification()
524     {
525         return notification;
526     }
527 
528     /***
529      * {@inheritDoc}
530      */
531     public Persistence getPersistence()
532     {
533         return persistence;
534     }
535     // implementation ////////////////////////////////////////////////////////
536 
537     /***
538      * Initializes an entry in the {@link #implClasses} map.
539      *
540      * @param type the map type.
541      * @param className the implementation class name.
542      * @param iface the interface the implementation class must support.
543      * @throws ClassNotFoundException if class couldn't be found.
544      */
545     private void initImpl(String type, String className, Class iface)
546         throws ClassNotFoundException
547     {
548         Class cl = Class.forName(className);
549         String reason = null;
550         int mod = cl.getModifiers();
551         if(Modifier.isInterface(mod) || Modifier.isAbstract(mod))
552         {
553             reason =  "it is an abstract or interface class";
554         }
555         try
556         {
557             cl.getConstructor( new Class[] {} );
558         }
559         catch(NoSuchMethodException e)
560         {
561             reason = " it has no public no-arg constructor";
562         }                      
563         if(!iface.isAssignableFrom(cl))
564         {
565             reason = " it does not implement "+iface.getName()+" interface";
566         }
567         if(reason != null)
568         {
569             throw new ComponentInitializationError(className+" can't be used as "+type+
570                                          " implementation because "+reason);
571         }
572         implClasses.put(type, cl);
573     }
574 
575     /***
576      * Creates a new configurable map instance.
577      *
578      * @param name instance's name.
579      * @param conf inststance's configuration elements.
580      */ 
581     private Map buildInstance(String name, Configuration conf)
582         throws ConfigurationException
583     {
584         Map previous = null;
585         Map current = null;
586         Configuration[] mapConfigs = conf.getChildren("config"); 
587         for(int i=0; i < mapConfigs.length; i++)
588         {
589             String num = (i == 0) ? "st" :
590                 ( (i == 1) ? "nd" : 
591                   ( (i == 2) ? "rd" : "th" ) );
592             num = (i+1)+num;
593                 
594             String s = mapConfigs[i].getValue().trim();
595             int op = s.indexOf('(');
596             int cp = (op > 0) ? s.indexOf(')',op) : -1;
597             if(op < 1 || cp < 0 || cp < s.length() - 1)
598             {
599                 throw new IllegalArgumentException(num+" entry in "+name+" configuration "+
600                                              "is malformed: MapType([config]) expected");
601             }
602             String type = s.substring(0,op);
603             String mapConfig = s.substring(op+1,cp);
604             current = getMap(type);
605             if(i > 0)
606             {
607                 if(current instanceof LayeredMap)
608                 {
609                     ((LayeredMap)current).setDelegate(previous);
610                 }
611                 else
612                 {
613                     throw new IllegalArgumentException(type+" was specified as the "+
614                         num+" entry in "+name+" configuration, but it's "+
615                         "implementation does not support LayeredMap interface");
616                 }
617             }
618             if(current instanceof ConfigurableMap)
619             {
620                 ((ConfigurableMap)current).configure(this,name,mapConfig);
621             }
622             else
623             {
624                 if(mapConfig.length() != 0)
625                 {
626                     throw new ComponentInitializationError("configuration was specified " +
627                         "for the "+num+ " entry in "+name+" configuration, but "+
628                         type+" implementation does not support ConfigurableMap interface");
629                 }
630             }
631             previous = current;
632         }
633         return current;
634     }
635 
636     /***
637      * The task that performs delayed updates.
638      */
639     private class DelayedUpdateTask
640         extends Task
641     {
642         public String getName()
643         {
644             return "Delayed update sheduler";
645         }
646         
647         public void process(Context context)
648         {
649             synchronized(queue)
650             {
651                 long now;
652                 loop: while(!Thread.interrupted())
653                 {
654                     // queue is empty - wait indefinetely
655                     if(queue.size() == 0)
656                     {
657                         try
658                         {
659                             queue.wait();
660                         }
661                         catch(InterruptedException e)
662                         {
663                             break loop;
664                         }
665                     }
666                     // there is something in the queue
667                     now = System.currentTimeMillis();
668                     Long first = (Long)queue.firstKey();
669                     while(!queue.isEmpty() && first.longValue() <= now)
670                     {
671                         // the first element of the que has reached or passed
672                         // it's target time
673                         Set set = (Set)queue.remove(first);
674                         Iterator i = set.iterator();
675                         while(i.hasNext())
676                         {
677                             DelayedUpdate obj = (DelayedUpdate)i.next();
678                             queueHelper.remove(obj);
679                             try
680                             {
681                                 obj.update();
682                             }
683                             ///CLOVER:OFF
684                             catch(VirtualMachineError e)
685                             {
686                                 throw e;
687                             }
688                             catch(ThreadDeath e)
689                             {
690                                 throw e;
691                             }
692                             catch(Throwable e)
693                             {
694                                 logger.error("exception in delayed update thread", e);
695                             }
696                             ///CLOVER:ON
697                         }
698                         if(!queue.isEmpty())
699                         {
700                             first = (Long)queue.firstKey();
701                         }
702                     }
703                     // wait for the first element's target time
704                     if(!queue.isEmpty())
705                     {
706                         try
707                         {
708                             queue.wait(first.longValue() - now);
709                         }
710                         catch(InterruptedException e)
711                         {
712                             break loop;
713                         }
714                     }
715                 }
716             }
717         }
718         
719         public void terminate(Thread t)
720         {
721             t.interrupt();
722         }
723     }
724 }