View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.id.uuid.clock;
19  
20  
21  /**
22   * <p>{@link Clock} provides a timing mechanism for returning the current time in
23   * 100-nano second intervals since 00:00:00.00, 15 October 1582.</p>
24   *
25   * <p>This Class consumes a single thread which will die off if not utilized before
26   * the expiration. Subsequent calls after a thread expires start a new thread.
27   * Compensates for jvm time resolution issues. This clock should be used in
28   * instances where the system resolution does not perform adequately - the
29   * clocking resolution on some windows virtual machines can range from 10 to 50
30   * milliseconds before the System.currentTimeMillis changes. In instances where
31   * 10,000 or more uuid's may be generated in a millisecond this Clock
32   * implementation may be required.</p>
33   *
34   * @author Commons-Id Team
35   * @version $Revision: 480488 $ $Date: 2006-11-29 08:57:26 +0000 (Wed, 29 Nov 2006) $
36   */
37  
38  public final class ThreadClockImpl extends Thread implements Clock {
39  
40      /** Default time to live of the Clock thread in milliseconds */
41      public static final long DEFAULT_THREAD_LIFE = 200;
42  
43      /** Life time of the clock thread in milliseconds */
44      private static long threadLife = DEFAULT_THREAD_LIFE;
45  
46      /** Singleton instance of the Clock thread*/
47      private static ThreadClockImpl worker = null;
48  
49      /** The current time in milliseconds held in this clock thread. */
50      private static long currentTimeMillis;
51  
52      /** Time when the clock thread should die */
53      private static long expires = threadLife;
54  
55      //---------------- Instance members ---------------------
56      /** The time in time milliseconds used in this instance */
57      private long lastTimeMs = 0;
58  
59      /** The counter for nanoseconds generated during this system interval(ms) */
60      private int generatedThisInterval;
61  
62      /** The system time interval to increment the clock */
63      private static short sysInterval = 1;
64      // See bug parade 4814012, 4500388
65      static {
66          if (System.getProperty("os.name").indexOf("Windows") != -1) {
67              sysInterval = 10;
68          }
69      }
70  
71      /**
72       * <p>Public constructor to instantiate a Clock instance.</p>
73       */
74      public ThreadClockImpl() {
75          if (worker == null) {
76              synchronized (ThreadClockImpl.class) {
77                  worker = new ThreadClockImpl(true);
78              }
79          }
80      }
81  
82      /**
83       * Private constructor for clock implementation. Utilizes a single thread to
84       * increment the clock every milli seconds this should be more
85       * accurate than System.currentTimeMillis() as described in
86       * the javaworld article:
87       * http://www.javaworld.com/javaworld/javaqa/2003-01/01-qa-0110-timing.html
88       *
89       * @param isWorkerThread boolean indicating this is the worker thread.
90       */
91      private ThreadClockImpl(boolean isWorkerThread) {
92          setDaemon(true);
93          setPriority(Thread.MAX_PRIORITY);
94          currentTimeMillis = System.currentTimeMillis();
95          generatedThisInterval = 0;
96          start();
97      }
98  
99      /**
100      * Returns the thread life in milliseconds. If the clock thread is not
101      * accessed within this time span the thread will die off.
102      *
103      * @return thread life time span in milliseconds
104      */
105     public static long getThreadLife() {
106         return ThreadClockImpl.threadLife;
107     }
108 
109     /**
110      * @param threadLifeLen milliseconds this thread should live for. Each
111      * call to getCurrentTime resets the expiration time value.
112      */
113     public static void setThreadLife(long threadLifeLen) {
114         ThreadClockImpl.threadLife = threadLifeLen;
115     }
116 
117     /**
118      * Threads run method that increments the clock and resets the generated
119      * nano seconds counter.
120      */
121     public void run() {
122         try {
123             while (--expires >= 0) {
124                 sleep(sysInterval);
125                 synchronized (ThreadClockImpl.class) {
126                     currentTimeMillis += sysInterval;
127                 }
128             }
129         } catch (InterruptedException e) {
130             System.out.println("Clock thread interrupted");
131         }
132     }
133 
134     /**
135      * Returns the current time as described in the clock resolution and
136      * timestamp sections of the uuid specification.
137      *
138      * @return the current time in 100-nano second intervals (simulated)
139      * @throws  OverClockedException an exception when the number of timestamps
140      *          generated exceeds the allowable timestamps for the system time
141      *          interval.
142      */
143     private synchronized long getTimeSynchronized()
144             throws OverClockedException {
145         long current = 0;
146         synchronized (ThreadClockImpl.class) {
147             current = currentTimeMillis;
148         }
149         if (current != lastTimeMs) {
150             generatedThisInterval = 0;
151             lastTimeMs = current;
152         }
153         if (generatedThisInterval + 1 >= (INTERVALS_PER_MILLI * sysInterval)) {
154             throw new OverClockedException();
155         }
156         return ((current + GREGORIAN_CHANGE_OFFSET) * INTERVALS_PER_MILLI)
157                 + generatedThisInterval++;
158       }
159 
160     /**
161      * Method returns the clocks current time in 100-nanosecond intervals
162      * since the Gregorian calander change. Calendar.GREGORIAN_OFFSET
163      *
164      * @return  Coordinated Universal Time (UTC) as a count of 100- nanosecond
165      *          intervals since 00:00:00.00, 15 October 1582.
166      * @throws  OverClockedException an exception when the number of timestamps
167      *          generated exceeds the allowable timestamps for the system time
168      *          interval.
169      */
170     public long getUUIDTime() throws OverClockedException {
171             if (!worker.isAlive()) {
172                 synchronized (SystemClockImpl.class) {
173                     currentTimeMillis = System.currentTimeMillis();
174                     worker.start();
175                 }
176                 generatedThisInterval = 0;
177             }
178         return getTimeSynchronized();
179     }
180 }