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 java.io.DataInput;
23 import java.io.DataOutput;
24 import java.io.IOException;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TreeMap;
32
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
35 import org.apache.hadoop.hbase.io.hfile.Compression;
36 import org.apache.hadoop.hbase.regionserver.StoreFile;
37 import org.apache.hadoop.hbase.util.Bytes;
38 import org.apache.hadoop.io.WritableComparable;
39
40
41
42
43
44 public class HTableDescriptor implements WritableComparable<HTableDescriptor> {
45
46
47
48
49
50 public static final byte TABLE_DESCRIPTOR_VERSION = 5;
51
52 private byte [] name = HConstants.EMPTY_BYTE_ARRAY;
53 private String nameAsString = "";
54
55
56 protected Map<ImmutableBytesWritable, ImmutableBytesWritable> values =
57 new HashMap<ImmutableBytesWritable, ImmutableBytesWritable>();
58
59 public static final String FAMILIES = "FAMILIES";
60 public static final ImmutableBytesWritable FAMILIES_KEY =
61 new ImmutableBytesWritable(Bytes.toBytes(FAMILIES));
62 public static final String MAX_FILESIZE = "MAX_FILESIZE";
63 public static final ImmutableBytesWritable MAX_FILESIZE_KEY =
64 new ImmutableBytesWritable(Bytes.toBytes(MAX_FILESIZE));
65 public static final String READONLY = "READONLY";
66 public static final ImmutableBytesWritable READONLY_KEY =
67 new ImmutableBytesWritable(Bytes.toBytes(READONLY));
68 public static final String MEMSTORE_FLUSHSIZE = "MEMSTORE_FLUSHSIZE";
69 public static final ImmutableBytesWritable MEMSTORE_FLUSHSIZE_KEY =
70 new ImmutableBytesWritable(Bytes.toBytes(MEMSTORE_FLUSHSIZE));
71 public static final String IS_ROOT = "IS_ROOT";
72 public static final ImmutableBytesWritable IS_ROOT_KEY =
73 new ImmutableBytesWritable(Bytes.toBytes(IS_ROOT));
74 public static final String IS_META = "IS_META";
75
76 public static final ImmutableBytesWritable IS_META_KEY =
77 new ImmutableBytesWritable(Bytes.toBytes(IS_META));
78
79 public static final String DEFERRED_LOG_FLUSH = "DEFERRED_LOG_FLUSH";
80 public static final ImmutableBytesWritable DEFERRED_LOG_FLUSH_KEY =
81 new ImmutableBytesWritable(Bytes.toBytes(DEFERRED_LOG_FLUSH));
82
83
84
85
86
87 private static final ImmutableBytesWritable FALSE =
88 new ImmutableBytesWritable(Bytes.toBytes(Boolean.FALSE.toString()));
89 private static final ImmutableBytesWritable TRUE =
90 new ImmutableBytesWritable(Bytes.toBytes(Boolean.TRUE.toString()));
91
92 public static final boolean DEFAULT_READONLY = false;
93
94 public static final long DEFAULT_MEMSTORE_FLUSH_SIZE = 1024*1024*64L;
95
96 public static final long DEFAULT_MAX_FILESIZE = 1024*1024*256L;
97
98 public static final boolean DEFAULT_DEFERRED_LOG_FLUSH = false;
99
100 private volatile Boolean meta = null;
101 private volatile Boolean root = null;
102 private Boolean isDeferredLog = null;
103
104
105 public final Map<byte [], HColumnDescriptor> families =
106 new TreeMap<byte [], HColumnDescriptor>(Bytes.BYTES_RAWCOMPARATOR);
107
108
109
110
111
112 protected HTableDescriptor(final byte [] name, HColumnDescriptor[] families) {
113 this.name = name.clone();
114 this.nameAsString = Bytes.toString(this.name);
115 setMetaFlags(name);
116 for(HColumnDescriptor descriptor : families) {
117 this.families.put(descriptor.getName(), descriptor);
118 }
119 }
120
121
122
123
124
125 protected HTableDescriptor(final byte [] name, HColumnDescriptor[] families,
126 Map<ImmutableBytesWritable,ImmutableBytesWritable> values) {
127 this.name = name.clone();
128 this.nameAsString = Bytes.toString(this.name);
129 setMetaFlags(name);
130 for(HColumnDescriptor descriptor : families) {
131 this.families.put(descriptor.getName(), descriptor);
132 }
133 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> entry:
134 values.entrySet()) {
135 this.values.put(entry.getKey(), entry.getValue());
136 }
137 }
138
139
140
141
142
143
144
145 public HTableDescriptor() {
146 super();
147 }
148
149
150
151
152
153
154
155
156
157 public HTableDescriptor(final String name) {
158 this(Bytes.toBytes(name));
159 }
160
161
162
163
164
165
166
167
168
169 public HTableDescriptor(final byte [] name) {
170 super();
171 setMetaFlags(this.name);
172 this.name = this.isMetaRegion()? name: isLegalTableName(name);
173 this.nameAsString = Bytes.toString(this.name);
174 }
175
176
177
178
179
180
181
182
183 public HTableDescriptor(final HTableDescriptor desc) {
184 super();
185 this.name = desc.name.clone();
186 this.nameAsString = Bytes.toString(this.name);
187 setMetaFlags(this.name);
188 for (HColumnDescriptor c: desc.families.values()) {
189 this.families.put(c.getName(), new HColumnDescriptor(c));
190 }
191 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
192 desc.values.entrySet()) {
193 this.values.put(e.getKey(), e.getValue());
194 }
195 }
196
197
198
199
200
201
202 private void setMetaFlags(final byte [] name) {
203 setRootRegion(Bytes.equals(name, HConstants.ROOT_TABLE_NAME));
204 setMetaRegion(isRootRegion() ||
205 Bytes.equals(name, HConstants.META_TABLE_NAME));
206 }
207
208
209 public boolean isRootRegion() {
210 if (this.root == null) {
211 this.root = isSomething(IS_ROOT_KEY, false)? Boolean.TRUE: Boolean.FALSE;
212 }
213 return this.root.booleanValue();
214 }
215
216
217 protected void setRootRegion(boolean isRoot) {
218
219 values.put(IS_ROOT_KEY, isRoot? TRUE: FALSE);
220 }
221
222
223 public boolean isMetaRegion() {
224 if (this.meta == null) {
225 this.meta = calculateIsMetaRegion();
226 }
227 return this.meta.booleanValue();
228 }
229
230 private synchronized Boolean calculateIsMetaRegion() {
231 byte [] value = getValue(IS_META_KEY);
232 return (value != null)? Boolean.valueOf(Bytes.toString(value)): Boolean.FALSE;
233 }
234
235 private boolean isSomething(final ImmutableBytesWritable key,
236 final boolean valueIfNull) {
237 byte [] value = getValue(key);
238 if (value != null) {
239
240 return Boolean.valueOf(Bytes.toString(value)).booleanValue();
241 }
242 return valueIfNull;
243 }
244
245
246
247
248 protected void setMetaRegion(boolean isMeta) {
249 values.put(IS_META_KEY, isMeta? TRUE: FALSE);
250 }
251
252
253 public boolean isMetaTable() {
254 return isMetaRegion() && !isRootRegion();
255 }
256
257
258
259
260
261 public static boolean isMetaTable(final byte [] n) {
262 return Bytes.equals(n, HConstants.ROOT_TABLE_NAME) ||
263 Bytes.equals(n, HConstants.META_TABLE_NAME);
264 }
265
266
267
268
269
270
271
272
273
274
275 public static byte [] isLegalTableName(final byte [] b) {
276 if (b == null || b.length <= 0) {
277 throw new IllegalArgumentException("Name is null or empty");
278 }
279 if (b[0] == '.' || b[0] == '-') {
280 throw new IllegalArgumentException("Illegal first character <" + b[0] +
281 "> at 0. User-space table names can only start with 'word " +
282 "characters': i.e. [a-zA-Z_0-9]: " + Bytes.toString(b));
283 }
284 for (int i = 0; i < b.length; i++) {
285 if (Character.isLetterOrDigit(b[i]) || b[i] == '_' || b[i] == '-' ||
286 b[i] == '.') {
287 continue;
288 }
289 throw new IllegalArgumentException("Illegal character <" + b[i] +
290 "> at " + i + ". User-space table names can only contain " +
291 "'word characters': i.e. [a-zA-Z_0-9-.]: " + Bytes.toString(b));
292 }
293 return b;
294 }
295
296
297
298
299
300 public byte[] getValue(byte[] key) {
301 return getValue(new ImmutableBytesWritable(key));
302 }
303
304 private byte[] getValue(final ImmutableBytesWritable key) {
305 ImmutableBytesWritable ibw = values.get(key);
306 if (ibw == null)
307 return null;
308 return ibw.get();
309 }
310
311
312
313
314
315 public String getValue(String key) {
316 byte[] value = getValue(Bytes.toBytes(key));
317 if (value == null)
318 return null;
319 return Bytes.toString(value);
320 }
321
322
323
324
325 public Map<ImmutableBytesWritable,ImmutableBytesWritable> getValues() {
326 return Collections.unmodifiableMap(values);
327 }
328
329
330
331
332
333 public void setValue(byte[] key, byte[] value) {
334 setValue(new ImmutableBytesWritable(key), value);
335 }
336
337
338
339
340
341 private void setValue(final ImmutableBytesWritable key,
342 final byte[] value) {
343 values.put(key, new ImmutableBytesWritable(value));
344 }
345
346
347
348
349
350 private void setValue(final ImmutableBytesWritable key,
351 final ImmutableBytesWritable value) {
352 values.put(key, value);
353 }
354
355
356
357
358
359 public void setValue(String key, String value) {
360 setValue(Bytes.toBytes(key), Bytes.toBytes(value));
361 }
362
363
364
365
366 public void remove(final byte [] key) {
367 values.remove(new ImmutableBytesWritable(key));
368 }
369
370
371
372
373 public boolean isReadOnly() {
374 return isSomething(READONLY_KEY, DEFAULT_READONLY);
375 }
376
377
378
379
380
381 public void setReadOnly(final boolean readOnly) {
382 setValue(READONLY_KEY, readOnly? TRUE: FALSE);
383 }
384
385
386
387
388 public synchronized boolean isDeferredLogFlush() {
389 if(this.isDeferredLog == null) {
390 this.isDeferredLog =
391 isSomething(DEFERRED_LOG_FLUSH_KEY, DEFAULT_DEFERRED_LOG_FLUSH);
392 }
393 return this.isDeferredLog;
394 }
395
396
397
398
399
400 public void setDeferredLogFlush(final boolean isDeferredLogFlush) {
401 setValue(DEFERRED_LOG_FLUSH_KEY, isDeferredLogFlush? TRUE: FALSE);
402 }
403
404
405 public byte [] getName() {
406 return name;
407 }
408
409
410 public String getNameAsString() {
411 return this.nameAsString;
412 }
413
414
415 public long getMaxFileSize() {
416 byte [] value = getValue(MAX_FILESIZE_KEY);
417 if (value != null)
418 return Long.valueOf(Bytes.toString(value)).longValue();
419 return HConstants.DEFAULT_MAX_FILE_SIZE;
420 }
421
422
423 public void setName(byte[] name) {
424 this.name = name;
425 }
426
427
428
429
430
431 public void setMaxFileSize(long maxFileSize) {
432 setValue(MAX_FILESIZE_KEY, Bytes.toBytes(Long.toString(maxFileSize)));
433 }
434
435
436
437
438 public long getMemStoreFlushSize() {
439 byte [] value = getValue(MEMSTORE_FLUSHSIZE_KEY);
440 if (value != null)
441 return Long.valueOf(Bytes.toString(value)).longValue();
442 return DEFAULT_MEMSTORE_FLUSH_SIZE;
443 }
444
445
446
447
448 public void setMemStoreFlushSize(long memstoreFlushSize) {
449 setValue(MEMSTORE_FLUSHSIZE_KEY,
450 Bytes.toBytes(Long.toString(memstoreFlushSize)));
451 }
452
453
454
455
456
457 public void addFamily(final HColumnDescriptor family) {
458 if (family.getName() == null || family.getName().length <= 0) {
459 throw new NullPointerException("Family name cannot be null or empty");
460 }
461 this.families.put(family.getName(), family);
462 }
463
464
465
466
467
468
469 public boolean hasFamily(final byte [] c) {
470 return families.containsKey(c);
471 }
472
473
474
475
476
477
478 @Override
479 public String toString() {
480 StringBuilder s = new StringBuilder();
481 s.append('{');
482 s.append(HConstants.NAME);
483 s.append(" => '");
484 s.append(Bytes.toString(name));
485 s.append("'");
486 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
487 values.entrySet()) {
488 String key = Bytes.toString(e.getKey().get());
489 String value = Bytes.toString(e.getValue().get());
490 if (key == null) {
491 continue;
492 }
493 String upperCase = key.toUpperCase();
494 if (upperCase.equals(IS_ROOT) || upperCase.equals(IS_META)) {
495
496 if (value.toLowerCase().equals(Boolean.FALSE.toString())) {
497 continue;
498 }
499 }
500 s.append(", ");
501 s.append(Bytes.toString(e.getKey().get()));
502 s.append(" => '");
503 s.append(Bytes.toString(e.getValue().get()));
504 s.append("'");
505 }
506 s.append(", ");
507 s.append(FAMILIES);
508 s.append(" => ");
509 s.append(families.values());
510 s.append('}');
511 return s.toString();
512 }
513
514
515
516
517 @Override
518 public boolean equals(Object obj) {
519 if (this == obj) {
520 return true;
521 }
522 if (obj == null) {
523 return false;
524 }
525 if (!(obj instanceof HTableDescriptor)) {
526 return false;
527 }
528 return compareTo((HTableDescriptor)obj) == 0;
529 }
530
531
532
533
534 @Override
535 public int hashCode() {
536 int result = Bytes.hashCode(this.name);
537 result ^= Byte.valueOf(TABLE_DESCRIPTOR_VERSION).hashCode();
538 if (this.families != null && this.families.size() > 0) {
539 for (HColumnDescriptor e: this.families.values()) {
540 result ^= e.hashCode();
541 }
542 }
543 result ^= values.hashCode();
544 return result;
545 }
546
547
548
549 public void readFields(DataInput in) throws IOException {
550 int version = in.readInt();
551 if (version < 3)
552 throw new IOException("versions < 3 are not supported (and never existed!?)");
553
554 name = Bytes.readByteArray(in);
555 nameAsString = Bytes.toString(this.name);
556 setRootRegion(in.readBoolean());
557 setMetaRegion(in.readBoolean());
558 values.clear();
559 int numVals = in.readInt();
560 for (int i = 0; i < numVals; i++) {
561 ImmutableBytesWritable key = new ImmutableBytesWritable();
562 ImmutableBytesWritable value = new ImmutableBytesWritable();
563 key.readFields(in);
564 value.readFields(in);
565 values.put(key, value);
566 }
567 families.clear();
568 int numFamilies = in.readInt();
569 for (int i = 0; i < numFamilies; i++) {
570 HColumnDescriptor c = new HColumnDescriptor();
571 c.readFields(in);
572 families.put(c.getName(), c);
573 }
574 if (version < 4) {
575 return;
576 }
577 }
578
579 public void write(DataOutput out) throws IOException {
580 out.writeInt(TABLE_DESCRIPTOR_VERSION);
581 Bytes.writeByteArray(out, name);
582 out.writeBoolean(isRootRegion());
583 out.writeBoolean(isMetaRegion());
584 out.writeInt(values.size());
585 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
586 values.entrySet()) {
587 e.getKey().write(out);
588 e.getValue().write(out);
589 }
590 out.writeInt(families.size());
591 for(Iterator<HColumnDescriptor> it = families.values().iterator();
592 it.hasNext(); ) {
593 HColumnDescriptor family = it.next();
594 family.write(out);
595 }
596 }
597
598
599
600 public int compareTo(final HTableDescriptor other) {
601 int result = Bytes.compareTo(this.name, other.name);
602 if (result == 0) {
603 result = families.size() - other.families.size();
604 }
605 if (result == 0 && families.size() != other.families.size()) {
606 result = Integer.valueOf(families.size()).compareTo(
607 Integer.valueOf(other.families.size()));
608 }
609 if (result == 0) {
610 for (Iterator<HColumnDescriptor> it = families.values().iterator(),
611 it2 = other.families.values().iterator(); it.hasNext(); ) {
612 result = it.next().compareTo(it2.next());
613 if (result != 0) {
614 break;
615 }
616 }
617 }
618 if (result == 0) {
619
620 result = this.values.hashCode() - other.values.hashCode();
621 if (result < 0)
622 result = -1;
623 else if (result > 0)
624 result = 1;
625 }
626 return result;
627 }
628
629
630
631
632 public Collection<HColumnDescriptor> getFamilies() {
633 return Collections.unmodifiableCollection(this.families.values());
634 }
635
636
637
638
639 public Set<byte[]> getFamiliesKeys() {
640 return Collections.unmodifiableSet(this.families.keySet());
641 }
642
643 public HColumnDescriptor[] getColumnFamilies() {
644 return getFamilies().toArray(new HColumnDescriptor[0]);
645 }
646
647
648
649
650
651
652 public HColumnDescriptor getFamily(final byte [] column) {
653 return this.families.get(column);
654 }
655
656
657
658
659
660
661 public HColumnDescriptor removeFamily(final byte [] column) {
662 return this.families.remove(column);
663 }
664
665
666
667
668
669
670 public static Path getTableDir(Path rootdir, final byte [] tableName) {
671 return new Path(rootdir, Bytes.toString(tableName));
672 }
673
674
675 public static final HTableDescriptor ROOT_TABLEDESC = new HTableDescriptor(
676 HConstants.ROOT_TABLE_NAME,
677 new HColumnDescriptor[] { new HColumnDescriptor(HConstants.CATALOG_FAMILY,
678 10,
679 Compression.Algorithm.NONE.getName(), true, true, 8 * 1024,
680 HConstants.FOREVER, StoreFile.BloomType.NONE.toString(),
681 HConstants.REPLICATION_SCOPE_LOCAL) });
682
683
684 public static final HTableDescriptor META_TABLEDESC = new HTableDescriptor(
685 HConstants.META_TABLE_NAME, new HColumnDescriptor[] {
686 new HColumnDescriptor(HConstants.CATALOG_FAMILY,
687 10,
688 Compression.Algorithm.NONE.getName(), true, true, 8 * 1024,
689 HConstants.FOREVER, StoreFile.BloomType.NONE.toString(),
690 HConstants.REPLICATION_SCOPE_LOCAL)});
691 }