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
006package fc.util;
007
008import java.util.*;
009import java.text.*;
010
011/** 
012Useful to store thread-local calendar instances.  This class is intended
013for servlets/molly pages. Instead of static get/set methods, this class
014must be instantiated and the instance methods used to get/set the calendar
015object. This allows multiple instances of this class in the webapp, with
016each instance being able to get/set a seperate calendar. [If the methods in
017this class were static, then only 1 calendar could be get/set per thread].
018<p>
019<font color=red><b>Each thread must remember to individually create a seperate
020calendar instance and store it via the set method</b></font>. The usage idiom
021is:
022<blockquote>
023<pre>
024<font color=blue>
025//WebApp has a map of ThreadLocalCalendars and also a instance variable
026//with a default ThreadLocalCalendar
027ThreadLocalCalendar mycal = WebApp.getThreadLocalCalendar("foo");
028</font><font color=red>
029if (mycal.isNull())  {
030  mycal.set(Calendar.getInstance());
031  }
032</font><font color=blue>
033Calendar cal = mycal.get();
034</font>
035</pre>
036</blockquote>
037Note, the lines in red are always needed anywhere/anytime this class is used.
038(note, it just so happens that the {@link Calendar#getInstance} returns (at
039least for now) a new object every time it is called, else which is why it is
040being used in the example above, else we would have to manually create a new
041Calendar instance instead).
042
043*/
044public final class ThreadLocalCalendar
045{
046public ThreadLocalCalendar()
047  { }
048/*
049Each get/set into the threadlocal must be seperately by each thread (the
050initialValue() method is good for auto-assigning a new value but we
051may need to assign a custom calendar value per thread, so we can't use
052initialValue()
053*/
054private final ThreadLocal tlcal = new ThreadLocal();
055
056public Calendar get()
057  {
058  return (Calendar) tlcal.get();
059  }
060
061public void set(Calendar cal)
062  {
063  tlcal.set(cal);
064  }
065
066public boolean isNull()
067  {
068  return tlcal.get() == null;
069  }
070
071public static void main (String args[]) throws Exception
072  { 
073  final ThreadLocalCalendar cal1 = new ThreadLocalCalendar();
074  
075  Thread t1 = new TestThread(false, cal1);
076  Thread t2 = new TestThread(false, cal1);
077  t1.start();
078  t2.start();
079  
080  for (int n = 0; n < 100; n++) {
081    new TestThread(true, cal1).start();
082    }
083  }
084
085static class TestThread extends Thread
086  {
087  boolean timing_only = false;
088  ThreadLocalCalendar cal;
089  TestThread(boolean timing_only, ThreadLocalCalendar cal) {
090    this.timing_only = timing_only;
091    this.cal = cal;
092    }
093    
094  public void run()
095    {
096    //warm up
097    if (cal.isNull()) {
098      cal.set(Calendar.getInstance());
099      }
100
101    Watch w = new NanoWatch();
102    w.start();
103    if (cal.isNull()) {
104      cal.set(Calendar.getInstance());
105      }
106    Calendar c = cal.get();
107    w.stop();
108    
109    NumberFormat nf = NumberFormat.getNumberInstance();
110    nf.setMinimumFractionDigits(2);
111    nf.setMaximumFractionDigits(2);
112    nf.setMinimumIntegerDigits(2);
113  
114    if (timing_only)
115      System.out.println("[" + nf.format(w.getTime() / 1000000.00D) + " ms]");
116    else
117      System.out.println("[" + nf.format(w.getTime() / 1000000.00D) + " ms]" + Thread.currentThread() + "/Calendar-ID:[" + System.identityHashCode(c) + "] "+ c);
118    }
119  }
120}