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.regionserver;
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 setDaemon(true);
72 }
73
74
75
76
77 @Override
78 public void run() {
79 while (!stopRequested || (stopRequested && leaseQueue.size() > 0) ) {
80 Lease lease = null;
81 try {
82 lease = leaseQueue.poll(leaseCheckFrequency, TimeUnit.MILLISECONDS);
83 } catch (InterruptedException e) {
84 continue;
85 } catch (ConcurrentModificationException e) {
86 continue;
87 } catch (Throwable e) {
88 LOG.fatal("Unexpected exception killed leases thread", e);
89 break;
90 }
91 if (lease == null) {
92 continue;
93 }
94
95
96 if (lease.getListener() == null) {
97 LOG.error("lease listener is null for lease " + lease.getLeaseName());
98 } else {
99 lease.getListener().leaseExpired();
100 }
101 synchronized (leaseQueue) {
102 leases.remove(lease.getLeaseName());
103 }
104 }
105 close();
106 }
107
108
109
110
111
112
113
114
115 public void closeAfterLeasesExpire() {
116 this.stopRequested = true;
117 }
118
119
120
121
122
123 public void close() {
124 LOG.info(Thread.currentThread().getName() + " closing leases");
125 this.stopRequested = true;
126 synchronized (leaseQueue) {
127 leaseQueue.clear();
128 leases.clear();
129 leaseQueue.notifyAll();
130 }
131 LOG.info(Thread.currentThread().getName() + " closed leases");
132 }
133
134
135
136
137
138
139
140
141 public void createLease(String leaseName, final LeaseListener listener)
142 throws LeaseStillHeldException {
143 if (stopRequested) {
144 return;
145 }
146 Lease lease = new Lease(leaseName, listener,
147 System.currentTimeMillis() + leasePeriod);
148 synchronized (leaseQueue) {
149 if (leases.containsKey(leaseName)) {
150 throw new LeaseStillHeldException(leaseName);
151 }
152 leases.put(leaseName, lease);
153 leaseQueue.add(lease);
154 }
155 }
156
157
158
159
160
161 @SuppressWarnings("serial")
162 public static class LeaseStillHeldException extends IOException {
163 private final String leaseName;
164
165
166
167
168 public LeaseStillHeldException(final String name) {
169 this.leaseName = name;
170 }
171
172
173 public String getName() {
174 return this.leaseName;
175 }
176 }
177
178
179
180
181
182
183
184 public void renewLease(final String leaseName) throws LeaseException {
185 synchronized (leaseQueue) {
186 Lease lease = leases.get(leaseName);
187
188
189
190 if (lease == null || !leaseQueue.remove(lease)) {
191 throw new LeaseException("lease '" + leaseName +
192 "' does not exist or has already expired");
193 }
194 lease.setExpirationTime(System.currentTimeMillis() + leasePeriod);
195 leaseQueue.add(lease);
196 }
197 }
198
199
200
201
202
203
204
205 public void cancelLease(final String leaseName) throws LeaseException {
206 synchronized (leaseQueue) {
207 Lease lease = leases.remove(leaseName);
208 if (lease == null) {
209 throw new LeaseException("lease '" + leaseName + "' does not exist");
210 }
211 leaseQueue.remove(lease);
212 }
213 }
214
215
216 private static class Lease implements Delayed {
217 private final String leaseName;
218 private final LeaseListener listener;
219 private long expirationTime;
220
221 Lease(final String leaseName, LeaseListener listener, long expirationTime) {
222 this.leaseName = leaseName;
223 this.listener = listener;
224 this.expirationTime = expirationTime;
225 }
226
227
228 public String getLeaseName() {
229 return leaseName;
230 }
231
232
233 public LeaseListener getListener() {
234 return this.listener;
235 }
236
237 @Override
238 public boolean equals(Object obj) {
239 if (this == obj) {
240 return true;
241 }
242 if (obj == null) {
243 return false;
244 }
245 if (getClass() != obj.getClass()) {
246 return false;
247 }
248 return this.hashCode() == ((Lease) obj).hashCode();
249 }
250
251 @Override
252 public int hashCode() {
253 return this.leaseName.hashCode();
254 }
255
256 public long getDelay(TimeUnit unit) {
257 return unit.convert(this.expirationTime - System.currentTimeMillis(),
258 TimeUnit.MILLISECONDS);
259 }
260
261 public int compareTo(Delayed o) {
262 long delta = this.getDelay(TimeUnit.MILLISECONDS) -
263 o.getDelay(TimeUnit.MILLISECONDS);
264
265 return this.equals(o) ? 0 : (delta > 0 ? 1 : -1);
266 }
267
268
269 public void setExpirationTime(long expirationTime) {
270 this.expirationTime = expirationTime;
271 }
272
273
274
275
276
277 public long getExpirationTime() {
278 return this.expirationTime;
279 }
280
281 }
282 }