1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.constraint;
19
20 import java.io.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.regex.Pattern;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.hbase.classification.InterfaceAudience;
35 import org.apache.hadoop.conf.Configuration;
36 import org.apache.hadoop.hbase.HTableDescriptor;
37 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.util.Pair;
40
41
42
43
44
45
46
47
48
49 @InterfaceAudience.Private
50 public final class Constraints {
51 private static final int DEFAULT_PRIORITY = -1;
52
53 private Constraints() {
54 }
55
56 private static final Log LOG = LogFactory.getLog(Constraints.class);
57 private static final String CONSTRAINT_HTD_KEY_PREFIX = "constraint $";
58 private static final Pattern CONSTRAINT_HTD_ATTR_KEY_PATTERN = Pattern
59 .compile(CONSTRAINT_HTD_KEY_PREFIX, Pattern.LITERAL);
60
61
62 private static final String ENABLED_KEY = "_ENABLED";
63
64 private static final String PRIORITY_KEY = "_PRIORITY";
65
66
67 private static final long MIN_PRIORITY = 0L;
68
69 private static final long UNSET_PRIORITY = MIN_PRIORITY - 1;
70
71 private static String COUNTER_KEY = "hbase.constraint.counter";
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public static void enable(HTableDescriptor desc) throws IOException {
86
87 String clazz = ConstraintProcessor.class.getName();
88 if (desc.hasCoprocessor(clazz)) {
89 return;
90 }
91
92
93 desc.addCoprocessor(clazz);
94 }
95
96
97
98
99
100
101
102
103
104 public static void disable(HTableDescriptor desc) {
105 desc.removeCoprocessor(ConstraintProcessor.class.getName());
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119 public static void remove(HTableDescriptor desc) {
120
121 disable(desc);
122
123
124 List<ImmutableBytesWritable> keys = new ArrayList<ImmutableBytesWritable>();
125
126 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e : desc
127 .getValues().entrySet()) {
128 String key = Bytes.toString((e.getKey().get()));
129 String[] className = CONSTRAINT_HTD_ATTR_KEY_PATTERN.split(key);
130 if (className.length == 2) {
131 keys.add(e.getKey());
132 }
133 }
134
135 for (ImmutableBytesWritable key : keys) {
136 desc.remove(key);
137 }
138 }
139
140
141
142
143
144
145
146
147
148
149
150 public static boolean has(HTableDescriptor desc,
151 Class<? extends Constraint> clazz) {
152 return getKeyValueForClass(desc, clazz) != null;
153 }
154
155
156
157
158
159
160
161
162
163
164
165 private static Pair<String, String> getKeyValueForClass(
166 HTableDescriptor desc, Class<? extends Constraint> clazz) {
167
168 String key = serializeConstraintClass(clazz);
169 String value = desc.getValue(key);
170
171 return value == null ? null : new Pair<String, String>(key, value);
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 public static void add(HTableDescriptor desc,
195 Class<? extends Constraint>... constraints) throws IOException {
196
197 enable(desc);
198 long priority = getNextPriority(desc);
199
200
201 for (Class<? extends Constraint> clazz : constraints) {
202 addConstraint(desc, clazz, null, priority++);
203 }
204 updateLatestPriority(desc, priority);
205 }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 public static void add(HTableDescriptor desc,
232 Pair<Class<? extends Constraint>, Configuration>... constraints)
233 throws IOException {
234 enable(desc);
235 long priority = getNextPriority(desc);
236 for (Pair<Class<? extends Constraint>, Configuration> pair : constraints) {
237 addConstraint(desc, pair.getFirst(), pair.getSecond(), priority++);
238 }
239 updateLatestPriority(desc, priority);
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 public static void add(HTableDescriptor desc,
262 Class<? extends Constraint> constraint, Configuration conf)
263 throws IOException {
264 enable(desc);
265 long priority = getNextPriority(desc);
266 addConstraint(desc, constraint, conf, priority++);
267
268 updateLatestPriority(desc, priority);
269 }
270
271
272
273
274
275
276
277
278
279
280 private static void addConstraint(HTableDescriptor desc,
281 Class<? extends Constraint> clazz, Configuration conf, long priority)
282 throws IOException {
283 writeConstraint(desc, serializeConstraintClass(clazz),
284 configure(conf, true, priority));
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298
299 private static Configuration configure(Configuration conf, boolean enabled,
300 long priority) {
301
302
303 Configuration toWrite = conf == null ? new Configuration()
304 : new Configuration(conf);
305
306
307 toWrite.setBooleanIfUnset(ENABLED_KEY, enabled);
308
309
310 if (toWrite.getLong(PRIORITY_KEY, UNSET_PRIORITY) == UNSET_PRIORITY) {
311 toWrite.setLong(PRIORITY_KEY, priority);
312 }
313
314 return toWrite;
315 }
316
317
318
319
320
321
322
323
324
325 private static String serializeConstraintClass(
326 Class<? extends Constraint> clazz) {
327 String constraintClazz = clazz.getName();
328 return CONSTRAINT_HTD_KEY_PREFIX + constraintClazz;
329 }
330
331
332
333
334
335 private static void writeConstraint(HTableDescriptor desc, String key,
336 Configuration conf) throws IOException {
337
338 desc.setValue(key, serializeConfiguration(conf));
339 }
340
341
342
343
344
345
346
347
348
349 private static String serializeConfiguration(Configuration conf)
350 throws IOException {
351
352 ByteArrayOutputStream bos = new ByteArrayOutputStream();
353 DataOutputStream dos = new DataOutputStream(bos);
354 conf.writeXml(dos);
355 dos.flush();
356 byte[] data = bos.toByteArray();
357 return Bytes.toString(data);
358 }
359
360
361
362
363
364
365
366
367 private static Configuration readConfiguration(byte[] bytes)
368 throws IOException {
369 ByteArrayInputStream is = new ByteArrayInputStream(bytes);
370 Configuration conf = new Configuration(false);
371 conf.addResource(is);
372 return conf;
373 }
374
375
376
377
378
379
380
381
382
383
384 private static Configuration readConfiguration(String bytes)
385 throws IOException {
386 return readConfiguration(Bytes.toBytes(bytes));
387 }
388
389 private static long getNextPriority(HTableDescriptor desc) {
390 String value = desc.getValue(COUNTER_KEY);
391
392 long priority;
393
394 if (value == null) {
395 priority = MIN_PRIORITY;
396 } else {
397 priority = Long.parseLong(value) + 1;
398 }
399
400 return priority;
401 }
402
403 private static void updateLatestPriority(HTableDescriptor desc, long priority) {
404
405 desc.setValue(COUNTER_KEY, Long.toString(priority));
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423 public static void setConfiguration(HTableDescriptor desc,
424 Class<? extends Constraint> clazz, Configuration configuration)
425 throws IOException, IllegalArgumentException {
426
427 Pair<String, String> e = getKeyValueForClass(desc, clazz);
428
429 if (e == null) {
430 throw new IllegalArgumentException("Constraint: " + clazz.getName()
431 + " is not associated with this table.");
432 }
433
434
435 Configuration conf = new Configuration(configuration);
436
437
438 Configuration internal = readConfiguration(e.getSecond());
439
440
441 conf.setIfUnset(ENABLED_KEY, internal.get(ENABLED_KEY));
442 conf.setIfUnset(PRIORITY_KEY, internal.get(PRIORITY_KEY));
443
444
445 writeConstraint(desc, e.getFirst(), conf);
446 }
447
448
449
450
451
452
453
454
455
456
457 public static void remove(HTableDescriptor desc,
458 Class<? extends Constraint> clazz) {
459 String key = serializeConstraintClass(clazz);
460 desc.remove(key);
461 }
462
463
464
465
466
467
468
469
470
471
472
473
474
475 public static void enableConstraint(HTableDescriptor desc,
476 Class<? extends Constraint> clazz) throws IOException {
477 changeConstraintEnabled(desc, clazz, true);
478 }
479
480
481
482
483
484
485
486
487
488
489
490
491
492 public static void disableConstraint(HTableDescriptor desc,
493 Class<? extends Constraint> clazz) throws IOException {
494 changeConstraintEnabled(desc, clazz, false);
495 }
496
497
498
499
500
501 private static void changeConstraintEnabled(HTableDescriptor desc,
502 Class<? extends Constraint> clazz, boolean enabled) throws IOException {
503
504 Pair<String, String> entry = getKeyValueForClass(desc, clazz);
505 if (entry == null) {
506 throw new IllegalArgumentException("Constraint: " + clazz.getName()
507 + " is not associated with this table. You can't enable it!");
508 }
509
510
511 Configuration conf = readConfiguration(entry.getSecond());
512
513
514 conf.setBoolean(ENABLED_KEY, enabled);
515
516
517 writeConstraint(desc, entry.getFirst(), conf);
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532 public static boolean enabled(HTableDescriptor desc,
533 Class<? extends Constraint> clazz) throws IOException {
534
535 Pair<String, String> entry = getKeyValueForClass(desc, clazz);
536
537 if (entry == null) {
538 return false;
539 }
540
541
542 Configuration conf = readConfiguration(entry.getSecond());
543
544 return conf.getBoolean(ENABLED_KEY, false);
545 }
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561 static List<? extends Constraint> getConstraints(HTableDescriptor desc,
562 ClassLoader classloader) throws IOException {
563 List<Constraint> constraints = new ArrayList<Constraint>();
564
565 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e : desc
566 .getValues().entrySet()) {
567
568 String key = Bytes.toString(e.getKey().get()).trim();
569 String[] className = CONSTRAINT_HTD_ATTR_KEY_PATTERN.split(key);
570 if (className.length == 2) {
571 key = className[1];
572 if (LOG.isDebugEnabled()) {
573 LOG.debug("Loading constraint:" + key);
574 }
575
576
577 Configuration conf;
578 try {
579 conf = readConfiguration(e.getValue().get());
580 } catch (IOException e1) {
581
582 LOG.warn("Corrupted configuration found for key:" + key
583 + ", skipping it.");
584 continue;
585 }
586
587 if (!conf.getBoolean(ENABLED_KEY, false)) {
588 if (LOG.isDebugEnabled())
589 LOG.debug("Constraint: " + key + " is DISABLED - skipping it");
590
591 continue;
592 }
593
594 try {
595
596 Class<? extends Constraint> clazz = classloader.loadClass(key)
597 .asSubclass(Constraint.class);
598 Constraint constraint = clazz.newInstance();
599 constraint.setConf(conf);
600 constraints.add(constraint);
601 } catch (ClassNotFoundException e1) {
602 throw new IOException(e1);
603 } catch (InstantiationException e1) {
604 throw new IOException(e1);
605 } catch (IllegalAccessException e1) {
606 throw new IOException(e1);
607 }
608 }
609 }
610
611 Collections.sort(constraints, constraintComparator);
612 return constraints;
613 }
614
615 private static final Comparator<Constraint> constraintComparator = new Comparator<Constraint>() {
616 @Override
617 public int compare(Constraint c1, Constraint c2) {
618
619 return Long.compare(c1.getConf().getLong(PRIORITY_KEY, DEFAULT_PRIORITY),
620 c2.getConf().getLong(PRIORITY_KEY, DEFAULT_PRIORITY));
621 }
622 };
623
624 }