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