1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24
25 import java.util.ConcurrentModificationException;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.concurrent.Delayed;
29 import java.util.concurrent.DelayQueue;
30 import java.util.concurrent.TimeUnit;
31
32 import java.io.IOException;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class Leases extends Thread {
54 private static final Log LOG = LogFactory.getLog(Leases.class.getName());
55 private final int leasePeriod;
56 private final int leaseCheckFrequency;
57 private volatile DelayQueue<Lease> leaseQueue = new DelayQueue<Lease>();
58 protected final Map<String, Lease> leases = new HashMap<String, Lease>();
59 private volatile boolean stopRequested = false;
60
61
62
63
64
65
66
67
68 public Leases(final int leasePeriod, final int leaseCheckFrequency) {
69 this.leasePeriod = leasePeriod;
70 this.leaseCheckFrequency = leaseCheckFrequency;
71 }
72
73
74
75
76 @Override
77 public void run() {
78 while (!stopRequested || (stopRequested && leaseQueue.size() > 0) ) {
79 Lease lease = null;
80 try {
81 lease = leaseQueue.poll(leaseCheckFrequency, TimeUnit.MILLISECONDS);
82 } catch (InterruptedException e) {
83 continue;
84 } catch (ConcurrentModificationException e) {
85 continue;
86 } catch (Throwable e) {
87 LOG.fatal("Unexpected exception killed leases thread", e);
88 break;
89 }
90 if (lease == null) {
91 continue;
92 }
93
94
95 if (lease.getListener() == null) {
96 LOG.error("lease listener is null for lease " + lease.getLeaseName());
97 } else {
98 lease.getListener().leaseExpired();
99 }
100 synchronized (leaseQueue) {
101 leases.remove(lease.getLeaseName());
102 }
103 }
104 close();
105 }
106
107
108
109
110
111
112
113
114 public void closeAfterLeasesExpire() {
115 this.stopRequested = true;
116 }
117
118
119
120
121
122 public void close() {
123 LOG.info(Thread.currentThread().getName() + " closing leases");
124 this.stopRequested = true;
125 synchronized (leaseQueue) {
126 leaseQueue.clear();
127 leases.clear();
128 leaseQueue.notifyAll();
129 }
130 LOG.info(Thread.currentThread().getName() + " closed leases");
131 }
132
133
134
135
136
137
138
139
140 public void createLease(String leaseName, final LeaseListener listener)
141 throws LeaseStillHeldException {
142 if (stopRequested) {
143 return;
144 }
145 Lease lease = new Lease(leaseName, listener,
146 System.currentTimeMillis() + leasePeriod);
147 synchronized (leaseQueue) {
148 if (leases.containsKey(leaseName)) {
149 throw new LeaseStillHeldException(leaseName);
150 }
151 leases.put(leaseName, lease);
152 leaseQueue.add(lease);
153 }
154 }
155
156
157
158
159
160 @SuppressWarnings("serial")
161 public static class LeaseStillHeldException extends IOException {
162 private final String leaseName;
163
164
165
166
167 public LeaseStillHeldException(final String name) {
168 this.leaseName = name;
169 }
170
171
172 public String getName() {
173 return this.leaseName;
174 }
175 }
176
177
178
179
180
181
182
183 public void renewLease(final String leaseName) throws LeaseException {
184 synchronized (leaseQueue) {
185 Lease lease = leases.get(leaseName);
186
187
188
189 if (lease == null || !leaseQueue.remove(lease)) {
190 throw new LeaseException("lease '" + leaseName +
191 "' does not exist or has already expired");
192 }
193 lease.setExpirationTime(System.currentTimeMillis() + leasePeriod);
194 leaseQueue.add(lease);
195 }
196 }
197
198
199
200
201
202
203
204 public void cancelLease(final String leaseName) throws LeaseException {
205 synchronized (leaseQueue) {
206 Lease lease = leases.remove(leaseName);
207 if (lease == null) {
208 throw new LeaseException("lease '" + leaseName + "' does not exist");
209 }
210 leaseQueue.remove(lease);
211 }
212 }
213
214
215 private static class Lease implements Delayed {
216 private final String leaseName;
217 private final LeaseListener listener;
218 private long expirationTime;
219
220 Lease(final String leaseName, LeaseListener listener, long expirationTime) {
221 this.leaseName = leaseName;
222 this.listener = listener;
223 this.expirationTime = expirationTime;
224 }
225
226
227 public String getLeaseName() {
228 return leaseName;
229 }
230
231
232 public LeaseListener getListener() {
233 return this.listener;
234 }
235
236 @Override
237 public boolean equals(Object obj) {
238 if (this == obj) {
239 return true;
240 }
241 if (obj == null) {
242 return false;
243 }
244 if (getClass() != obj.getClass()) {
245 return false;
246 }
247 return this.hashCode() == ((Lease) obj).hashCode();
248 }
249
250 @Override
251 public int hashCode() {
252 return this.leaseName.hashCode();
253 }
254
255 public long getDelay(TimeUnit unit) {
256 return unit.convert(this.expirationTime - System.currentTimeMillis(),
257 TimeUnit.MILLISECONDS);
258 }
259
260 public int compareTo(Delayed o) {
261 long delta = this.getDelay(TimeUnit.MILLISECONDS) -
262 o.getDelay(TimeUnit.MILLISECONDS);
263
264 return this.equals(o) ? 0 : (delta > 0 ? 1 : -1);
265 }
266
267
268 public void setExpirationTime(long expirationTime) {
269 this.expirationTime = expirationTime;
270 }
271
272
273
274
275
276 public long getExpirationTime() {
277 return this.expirationTime;
278 }
279
280 }
281 }