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.Collections;
26 import java.util.HashMap;
27 import java.util.Map;
28
29 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
30 import org.apache.hadoop.hbase.io.hfile.Compression;
31 import org.apache.hadoop.hbase.io.hfile.HFile;
32 import org.apache.hadoop.hbase.regionserver.StoreFile;
33 import org.apache.hadoop.hbase.regionserver.StoreFile.BloomType;
34 import org.apache.hadoop.hbase.util.Bytes;
35 import org.apache.hadoop.io.Text;
36 import org.apache.hadoop.io.WritableComparable;
37
38
39
40
41
42
43
44
45
46
47 public class HColumnDescriptor implements WritableComparable<HColumnDescriptor> {
48
49
50
51
52
53
54
55
56 private static final byte COLUMN_DESCRIPTOR_VERSION = (byte)8;
57
58 public static final String COMPRESSION = "COMPRESSION";
59 public static final String COMPRESSION_COMPACT = "COMPRESSION_COMPACT";
60 public static final String BLOCKCACHE = "BLOCKCACHE";
61
62
63
64
65
66
67 public static final String BLOCKSIZE = "BLOCKSIZE";
68
69 public static final String LENGTH = "LENGTH";
70 public static final String TTL = "TTL";
71 public static final String BLOOMFILTER = "BLOOMFILTER";
72 public static final String FOREVER = "FOREVER";
73 public static final String REPLICATION_SCOPE = "REPLICATION_SCOPE";
74 public static final String MIN_VERSIONS = "MIN_VERSIONS";
75
76
77
78
79 public static final String DEFAULT_COMPRESSION =
80 Compression.Algorithm.NONE.getName();
81
82
83
84
85 public static final int DEFAULT_VERSIONS = 3;
86
87
88
89
90 public static final int DEFAULT_MIN_VERSIONS = 0;
91
92
93
94
95
96 private volatile Integer blocksize = null;
97
98
99
100
101 public static final boolean DEFAULT_IN_MEMORY = false;
102
103
104
105
106 public static final boolean DEFAULT_BLOCKCACHE = true;
107
108
109
110
111 public static final int DEFAULT_BLOCKSIZE = HFile.DEFAULT_BLOCKSIZE;
112
113
114
115
116 public static final String DEFAULT_BLOOMFILTER = StoreFile.BloomType.NONE.toString();
117
118
119
120
121 public static final int DEFAULT_TTL = HConstants.FOREVER;
122
123
124
125
126 public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
127
128 private final static Map<String, String> DEFAULT_VALUES = new HashMap<String, String>();
129 static {
130 DEFAULT_VALUES.put(BLOOMFILTER, DEFAULT_BLOOMFILTER);
131 DEFAULT_VALUES.put(REPLICATION_SCOPE, String.valueOf(DEFAULT_REPLICATION_SCOPE));
132 DEFAULT_VALUES.put(HConstants.VERSIONS, String.valueOf(DEFAULT_VERSIONS));
133 DEFAULT_VALUES.put(COMPRESSION, DEFAULT_COMPRESSION);
134 DEFAULT_VALUES.put(TTL, String.valueOf(DEFAULT_TTL));
135 DEFAULT_VALUES.put(BLOCKSIZE, String.valueOf(DEFAULT_BLOCKSIZE));
136 DEFAULT_VALUES.put(HConstants.IN_MEMORY, String.valueOf(DEFAULT_IN_MEMORY));
137 DEFAULT_VALUES.put(BLOCKCACHE, String.valueOf(DEFAULT_BLOCKCACHE));
138 }
139
140
141 private byte [] name;
142
143
144 protected Map<ImmutableBytesWritable,ImmutableBytesWritable> values =
145 new HashMap<ImmutableBytesWritable,ImmutableBytesWritable>();
146
147
148
149
150 private int cachedMaxVersions = -1;
151
152
153
154
155 public HColumnDescriptor() {
156 this.name = null;
157 }
158
159
160
161
162
163
164
165
166 public HColumnDescriptor(final String familyName) {
167 this(Bytes.toBytes(familyName));
168 }
169
170
171
172
173
174
175
176
177 public HColumnDescriptor(final byte [] familyName) {
178 this (familyName == null || familyName.length <= 0?
179 HConstants.EMPTY_BYTE_ARRAY: familyName, DEFAULT_VERSIONS,
180 DEFAULT_COMPRESSION, DEFAULT_IN_MEMORY, DEFAULT_BLOCKCACHE,
181 DEFAULT_TTL, DEFAULT_BLOOMFILTER);
182 }
183
184
185
186
187
188
189
190 public HColumnDescriptor(HColumnDescriptor desc) {
191 super();
192 this.name = desc.name.clone();
193 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
194 desc.values.entrySet()) {
195 this.values.put(e.getKey(), e.getValue());
196 }
197 setMaxVersions(desc.getMaxVersions());
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 public HColumnDescriptor(final byte [] familyName, final int maxVersions,
219 final String compression, final boolean inMemory,
220 final boolean blockCacheEnabled,
221 final int timeToLive, final String bloomFilter) {
222 this(familyName, maxVersions, compression, inMemory, blockCacheEnabled,
223 DEFAULT_BLOCKSIZE, timeToLive, bloomFilter, DEFAULT_REPLICATION_SCOPE);
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 public HColumnDescriptor(final byte [] familyName, final int maxVersions,
249 final String compression, final boolean inMemory,
250 final boolean blockCacheEnabled, final int blocksize,
251 final int timeToLive, final String bloomFilter, final int scope) {
252 this(familyName, DEFAULT_MIN_VERSIONS, maxVersions, compression, inMemory,
253 blockCacheEnabled, blocksize, timeToLive, bloomFilter, scope);
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 public HColumnDescriptor(final byte [] familyName, final int minVersions,
280 final int maxVersions, final String compression, final boolean inMemory,
281 final boolean blockCacheEnabled, final int blocksize,
282 final int timeToLive, final String bloomFilter, final int scope) {
283 isLegalFamilyName(familyName);
284 this.name = familyName;
285
286 if (maxVersions <= 0) {
287
288
289 throw new IllegalArgumentException("Maximum versions must be positive");
290 }
291
292 if (minVersions > 0) {
293 if (timeToLive == HConstants.FOREVER) {
294 throw new IllegalArgumentException("Minimum versions requires TTL.");
295 }
296 if (minVersions > maxVersions) {
297 throw new IllegalArgumentException("Minimum versions must be <= "+
298 "maximum versions.");
299 }
300 }
301
302 setMaxVersions(maxVersions);
303 setMinVersions(minVersions);
304 setInMemory(inMemory);
305 setBlockCacheEnabled(blockCacheEnabled);
306 setTimeToLive(timeToLive);
307 setCompressionType(Compression.Algorithm.
308 valueOf(compression.toUpperCase()));
309 setBloomFilterType(StoreFile.BloomType.
310 valueOf(bloomFilter.toUpperCase()));
311 setBlocksize(blocksize);
312 setScope(scope);
313 }
314
315
316
317
318
319
320
321
322
323 public static byte [] isLegalFamilyName(final byte [] b) {
324 if (b == null) {
325 return b;
326 }
327 if (b[0] == '.') {
328 throw new IllegalArgumentException("Family names cannot start with a " +
329 "period: " + Bytes.toString(b));
330 }
331 for (int i = 0; i < b.length; i++) {
332 if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
333 throw new IllegalArgumentException("Illegal character <" + b[i] +
334 ">. Family names cannot contain control characters or colons: " +
335 Bytes.toString(b));
336 }
337 }
338 return b;
339 }
340
341
342
343
344 public byte [] getName() {
345 return name;
346 }
347
348
349
350
351 public String getNameAsString() {
352 return Bytes.toString(this.name);
353 }
354
355
356
357
358
359 public byte[] getValue(byte[] key) {
360 ImmutableBytesWritable ibw = values.get(new ImmutableBytesWritable(key));
361 if (ibw == null)
362 return null;
363 return ibw.get();
364 }
365
366
367
368
369
370 public String getValue(String key) {
371 byte[] value = getValue(Bytes.toBytes(key));
372 if (value == null)
373 return null;
374 return Bytes.toString(value);
375 }
376
377
378
379
380 public Map<ImmutableBytesWritable,ImmutableBytesWritable> getValues() {
381 return Collections.unmodifiableMap(values);
382 }
383
384
385
386
387
388 public void setValue(byte[] key, byte[] value) {
389 values.put(new ImmutableBytesWritable(key),
390 new ImmutableBytesWritable(value));
391 }
392
393
394
395
396 public void remove(final byte [] key) {
397 values.remove(new ImmutableBytesWritable(key));
398 }
399
400
401
402
403
404 public void setValue(String key, String value) {
405 setValue(Bytes.toBytes(key), Bytes.toBytes(value));
406 }
407
408
409 public Compression.Algorithm getCompression() {
410 String n = getValue(COMPRESSION);
411 if (n == null) {
412 return Compression.Algorithm.NONE;
413 }
414 return Compression.Algorithm.valueOf(n.toUpperCase());
415 }
416
417
418
419 public Compression.Algorithm getCompactionCompression() {
420 String n = getValue(COMPRESSION_COMPACT);
421 if (n == null) {
422 return getCompression();
423 }
424 return Compression.Algorithm.valueOf(n.toUpperCase());
425 }
426
427
428 public int getMaxVersions() {
429 return this.cachedMaxVersions;
430 }
431
432
433
434
435 public void setMaxVersions(int maxVersions) {
436 setValue(HConstants.VERSIONS, Integer.toString(maxVersions));
437 cachedMaxVersions = maxVersions;
438 }
439
440
441
442
443 public synchronized int getBlocksize() {
444 if (this.blocksize == null) {
445 String value = getValue(BLOCKSIZE);
446 this.blocksize = (value != null)?
447 Integer.decode(value): Integer.valueOf(DEFAULT_BLOCKSIZE);
448 }
449 return this.blocksize.intValue();
450 }
451
452
453
454
455
456 public void setBlocksize(int s) {
457 setValue(BLOCKSIZE, Integer.toString(s));
458 this.blocksize = null;
459 }
460
461
462
463
464 public Compression.Algorithm getCompressionType() {
465 return getCompression();
466 }
467
468
469
470
471
472
473
474
475 public void setCompressionType(Compression.Algorithm type) {
476 String compressionType;
477 switch (type) {
478 case LZO: compressionType = "LZO"; break;
479 case GZ: compressionType = "GZ"; break;
480 case SNAPPY: compressionType = "SNAPPY"; break;
481 default: compressionType = "NONE"; break;
482 }
483 setValue(COMPRESSION, compressionType);
484 }
485
486
487
488
489 public Compression.Algorithm getCompactionCompressionType() {
490 return getCompactionCompression();
491 }
492
493
494
495
496
497
498
499
500 public void setCompactionCompressionType(Compression.Algorithm type) {
501 String compressionType;
502 switch (type) {
503 case LZO: compressionType = "LZO"; break;
504 case GZ: compressionType = "GZ"; break;
505 case SNAPPY: compressionType = "SNAPPY"; break;
506 default: compressionType = "NONE"; break;
507 }
508 setValue(COMPRESSION_COMPACT, compressionType);
509 }
510
511
512
513
514 public boolean isInMemory() {
515 String value = getValue(HConstants.IN_MEMORY);
516 if (value != null)
517 return Boolean.valueOf(value).booleanValue();
518 return DEFAULT_IN_MEMORY;
519 }
520
521
522
523
524
525 public void setInMemory(boolean inMemory) {
526 setValue(HConstants.IN_MEMORY, Boolean.toString(inMemory));
527 }
528
529
530
531
532 public int getTimeToLive() {
533 String value = getValue(TTL);
534 return (value != null)? Integer.valueOf(value).intValue(): DEFAULT_TTL;
535 }
536
537
538
539
540 public void setTimeToLive(int timeToLive) {
541 setValue(TTL, Integer.toString(timeToLive));
542 }
543
544
545
546
547 public int getMinVersions() {
548 String value = getValue(MIN_VERSIONS);
549 return (value != null)? Integer.valueOf(value).intValue(): 0;
550 }
551
552
553
554
555
556 public void setMinVersions(int minVersions) {
557 setValue(MIN_VERSIONS, Integer.toString(minVersions));
558 }
559
560
561
562
563 public boolean isBlockCacheEnabled() {
564 String value = getValue(BLOCKCACHE);
565 if (value != null)
566 return Boolean.valueOf(value).booleanValue();
567 return DEFAULT_BLOCKCACHE;
568 }
569
570
571
572
573 public void setBlockCacheEnabled(boolean blockCacheEnabled) {
574 setValue(BLOCKCACHE, Boolean.toString(blockCacheEnabled));
575 }
576
577
578
579
580 public StoreFile.BloomType getBloomFilterType() {
581 String n = getValue(BLOOMFILTER);
582 if (n == null) {
583 n = DEFAULT_BLOOMFILTER;
584 }
585 return StoreFile.BloomType.valueOf(n.toUpperCase());
586 }
587
588
589
590
591 public void setBloomFilterType(final StoreFile.BloomType bt) {
592 setValue(BLOOMFILTER, bt.toString());
593 }
594
595
596
597
598 public int getScope() {
599 String value = getValue(REPLICATION_SCOPE);
600 if (value != null) {
601 return Integer.valueOf(value).intValue();
602 }
603 return DEFAULT_REPLICATION_SCOPE;
604 }
605
606
607
608
609 public void setScope(int scope) {
610 setValue(REPLICATION_SCOPE, Integer.toString(scope));
611 }
612
613
614
615
616 @Override
617 public String toString() {
618 StringBuilder s = new StringBuilder();
619 s.append('{');
620 s.append(HConstants.NAME);
621 s.append(" => '");
622 s.append(Bytes.toString(name));
623 s.append("'");
624 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
625 values.entrySet()) {
626 String key = Bytes.toString(e.getKey().get());
627 String value = Bytes.toString(e.getValue().get());
628 s.append(", ");
629 s.append(key);
630 s.append(" => '");
631 s.append(value);
632 s.append("'");
633 }
634 s.append('}');
635 return s.toString();
636 }
637
638
639
640
641 public String toStringCustomizedValues() {
642 StringBuilder s = new StringBuilder();
643 s.append('{');
644 s.append(HConstants.NAME);
645 s.append(" => '");
646 s.append(Bytes.toString(name));
647 s.append("'");
648 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
649 values.entrySet()) {
650 String key = Bytes.toString(e.getKey().get());
651 String value = Bytes.toString(e.getValue().get());
652 if(DEFAULT_VALUES.get(key) == null || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
653 s.append(", ");
654 s.append(key);
655 s.append(" => '");
656 s.append(value);
657 s.append("'");
658 }
659 }
660 s.append('}');
661 return s.toString();
662 }
663
664
665
666
667 @Override
668 public boolean equals(Object obj) {
669 if (this == obj) {
670 return true;
671 }
672 if (obj == null) {
673 return false;
674 }
675 if (!(obj instanceof HColumnDescriptor)) {
676 return false;
677 }
678 return compareTo((HColumnDescriptor)obj) == 0;
679 }
680
681
682
683
684 @Override
685 public int hashCode() {
686 int result = Bytes.hashCode(this.name);
687 result ^= Byte.valueOf(COLUMN_DESCRIPTOR_VERSION).hashCode();
688 result ^= values.hashCode();
689 return result;
690 }
691
692
693
694 public void readFields(DataInput in) throws IOException {
695 int version = in.readByte();
696 if (version < 6) {
697 if (version <= 2) {
698 Text t = new Text();
699 t.readFields(in);
700 this.name = t.getBytes();
701
702
703
704
705 } else {
706 this.name = Bytes.readByteArray(in);
707 }
708 this.values.clear();
709 setMaxVersions(in.readInt());
710 int ordinal = in.readInt();
711 setCompressionType(Compression.Algorithm.values()[ordinal]);
712 setInMemory(in.readBoolean());
713 setBloomFilterType(in.readBoolean() ? BloomType.ROW : BloomType.NONE);
714 if (getBloomFilterType() != BloomType.NONE && version < 5) {
715
716
717
718
719 throw new UnsupportedClassVersionError(this.getClass().getName() +
720 " does not support backward compatibility with versions older " +
721 "than version 5");
722 }
723 if (version > 1) {
724 setBlockCacheEnabled(in.readBoolean());
725 }
726 if (version > 2) {
727 setTimeToLive(in.readInt());
728 }
729 } else {
730
731 this.name = Bytes.readByteArray(in);
732 this.values.clear();
733 int numValues = in.readInt();
734 for (int i = 0; i < numValues; i++) {
735 ImmutableBytesWritable key = new ImmutableBytesWritable();
736 ImmutableBytesWritable value = new ImmutableBytesWritable();
737 key.readFields(in);
738 value.readFields(in);
739
740
741 if (version < 8 && Bytes.toString(key.get()).equals(BLOOMFILTER)) {
742 value.set(Bytes.toBytes(
743 Boolean.getBoolean(Bytes.toString(value.get()))
744 ? BloomType.ROW.toString()
745 : BloomType.NONE.toString()));
746 }
747
748 values.put(key, value);
749 }
750 if (version == 6) {
751
752 setValue(COMPRESSION, Compression.Algorithm.NONE.getName());
753 }
754 String value = getValue(HConstants.VERSIONS);
755 this.cachedMaxVersions = (value != null)?
756 Integer.valueOf(value).intValue(): DEFAULT_VERSIONS;
757 }
758 }
759
760 public void write(DataOutput out) throws IOException {
761 out.writeByte(COLUMN_DESCRIPTOR_VERSION);
762 Bytes.writeByteArray(out, this.name);
763 out.writeInt(values.size());
764 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
765 values.entrySet()) {
766 e.getKey().write(out);
767 e.getValue().write(out);
768 }
769 }
770
771
772
773 public int compareTo(HColumnDescriptor o) {
774 int result = Bytes.compareTo(this.name, o.getName());
775 if (result == 0) {
776
777 result = this.values.hashCode() - o.values.hashCode();
778 if (result < 0)
779 result = -1;
780 else if (result > 0)
781 result = 1;
782 }
783 return result;
784 }
785 }