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 }