001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.util;
007    
008    import java.util.*;
009    import java.text.*;
010    
011    /** 
012    Useful to store thread-local number format instances.  This class is
013    intended for servlets/molly pages. Instead of static get/set methods,
014    this class must be instantiated and the instance methods used to get/set
015    the calendar object. This allows multiple instances of this class in the
016    webapp, with each instance being able to get/set a separate calendar. [If
017    the methods in this class were static, then only 1 calendar could be
018    get/set per thread].
019    <p>
020    <font color=red><b>Each thread must remember to individually create a
021    separate calendar instance and store it via the set method</b></font>. The
022    usage idiom is:
023    <blockquote>
024    <pre>
025    <font color=blue>
026    //WebApp has a map of ThreadLocalNumberFormat's and also a instance variable
027    //pointing to a default ThreadLocalNumberFormat
028    ThreadLocalNumberFormat mynf = WebApp.getThreadLocalNumberFormat("foo");
029    </font><font color=red>
030    if (mynf.isNull())  {
031      mynf.set(NumberFormat.getInstance());
032      }
033    </font><font color=blue>
034    NumberFormat nf = mynf.get();
035    </font>
036    </pre>
037    </blockquote>
038    Note, the lines in red are always needed anywhere/anytime this class is used.
039    (note, it just so happens that the {@link NumberFormat#getInstance} returns (at
040    least for now) a new object every time it is called, which is why it is
041    being used in the example above, else we would have to manually create a new
042    NumberFormat instance instead).
043    */
044    public final class ThreadLocalNumberFormat
045    {
046    public ThreadLocalNumberFormat()
047      { }
048    /*
049    Each get/set into the threadlocal must be seperately by each thread (the
050    initialValue() method is good for auto-assigning a new value but we
051    may need to assign a custom calendar value per thread, so we can't use
052    initialValue()
053    */
054    private final ThreadLocal tlnf = new ThreadLocal();
055    
056    public NumberFormat get()
057      {
058      return (NumberFormat) tlnf.get();
059      }
060    
061    public void set(NumberFormat nf)
062      {
063      tlnf.set(nf);
064      }
065    
066    public boolean isNull()
067      {
068      return tlnf.get() == null;
069      }
070    
071    public static void main (String args[]) throws Exception
072      { 
073      final ThreadLocalNumberFormat nf1 = new ThreadLocalNumberFormat();
074      
075      Thread t1 = new TestThread(false, nf1);
076      Thread t2 = new TestThread(false, nf1);
077      t1.start();
078      t2.start();
079      
080      for (int n = 0; n < 100; n++) {
081        new TestThread(true, nf1).start();
082        }
083      }
084    
085    static class TestThread extends Thread
086      {
087      boolean timing_only = false;
088      ThreadLocalNumberFormat nf;
089      TestThread(boolean timing_only, ThreadLocalNumberFormat nf) {
090        this.timing_only = timing_only;
091        this.nf = nf;
092        }
093        
094      public void run()
095        {
096        //warm-up
097        if (nf.isNull()) {
098          nf.set(NumberFormat.getInstance());
099          }
100    
101        Watch w = new NanoWatch();
102        w.start();
103        if (nf.isNull()) {
104          //all threads will have the same instance but thats ok
105          //we are testing the time taken to get a thread local
106          //variable, we don't have to create a new number format
107          //per thread for this example (but we DO for real-world
108          //since these are not thread safe formatters). We use
109          //a thread-specific formatter below (nf)
110          nf.set(NumberFormat.getInstance());
111          }
112        NumberFormat c = nf.get();
113        w.stop();
114        
115        NumberFormat nf = NumberFormat.getNumberInstance();
116        nf.setMinimumFractionDigits(2);
117        nf.setMaximumFractionDigits(2);
118        nf.setMinimumIntegerDigits(2);
119      
120        if (timing_only)
121          System.out.println("ThreadLocal get: [" + nf.format(w.getTime() / 1000000.00D) 
122            + " ms]");
123        else
124          System.out.println("[" + nf.format(w.getTime() / 1000000.00D) 
125            + " ms]" + Thread.currentThread() 
126            + "/NumberFormat-ID:[" + System.identityHashCode(c) 
127            + "] "+ c);
128        }
129      }
130    }