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 calendar instances. This class is intended
013 for servlets/molly pages. Instead of static get/set methods, this class
014 must be instantiated and the instance methods used to get/set the calendar
015 object. This allows multiple instances of this class in the webapp, with
016 each instance being able to get/set a seperate calendar. [If the methods in
017 this 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
020 calendar instance and store it via the set method</b></font>. The usage idiom
021 is:
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
027 ThreadLocalCalendar mycal = WebApp.getThreadLocalCalendar("foo");
028 </font><font color=red>
029 if (mycal.isNull()) {
030 mycal.set(Calendar.getInstance());
031 }
032 </font><font color=blue>
033 Calendar cal = mycal.get();
034 </font>
035 </pre>
036 </blockquote>
037 Note, 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
039 least for now) a new object every time it is called, else which is why it is
040 being used in the example above, else we would have to manually create a new
041 Calendar instance instead).
042
043 */
044 public final class ThreadLocalCalendar
045 {
046 public ThreadLocalCalendar()
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 tlcal = new ThreadLocal();
055
056 public Calendar get()
057 {
058 return (Calendar) tlcal.get();
059 }
060
061 public void set(Calendar cal)
062 {
063 tlcal.set(cal);
064 }
065
066 public boolean isNull()
067 {
068 return tlcal.get() == null;
069 }
070
071 public 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
085 static 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 }