View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.hadoop.hbase.classification.InterfaceAudience;
28  import org.apache.hadoop.hbase.classification.InterfaceStability;
29  import org.apache.hadoop.hbase.exceptions.DeserializationException;
30  import org.apache.hadoop.hbase.io.compress.Compression;
31  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
32  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
33  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.BytesBytesPair;
34  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ColumnFamilySchema;
35  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
36  import org.apache.hadoop.hbase.regionserver.BloomType;
37  import org.apache.hadoop.hbase.util.ByteStringer;
38  import org.apache.hadoop.hbase.util.Bytes;
39  import org.apache.hadoop.hbase.util.PrettyPrinter;
40  import org.apache.hadoop.hbase.util.PrettyPrinter.Unit;
41  
42  import com.google.common.base.Preconditions;
43  import com.google.protobuf.InvalidProtocolBufferException;
44  
45  /**
46   * An HColumnDescriptor contains information about a column family such as the
47   * number of versions, compression settings, etc.
48   *
49   * It is used as input when creating a table or adding a column.
50   */
51  @InterfaceAudience.Public
52  @InterfaceStability.Evolving
53  public class HColumnDescriptor implements Comparable<HColumnDescriptor> {
54    // For future backward compatibility
55  
56    // Version  3 was when column names become byte arrays and when we picked up
57    // Time-to-live feature.  Version 4 was when we moved to byte arrays, HBASE-82.
58    // Version  5 was when bloom filter descriptors were removed.
59    // Version  6 adds metadata as a map where keys and values are byte[].
60    // Version  7 -- add new compression and hfile blocksize to HColumnDescriptor (HBASE-1217)
61    // Version  8 -- reintroduction of bloom filters, changed from boolean to enum
62    // Version  9 -- add data block encoding
63    // Version 10 -- change metadata to standard type.
64    // Version 11 -- add column family level configuration.
65    private static final byte COLUMN_DESCRIPTOR_VERSION = (byte) 11;
66  
67    // These constants are used as FileInfo keys
68    public static final String COMPRESSION = "COMPRESSION";
69    public static final String COMPRESSION_COMPACT = "COMPRESSION_COMPACT";
70    public static final String ENCODE_ON_DISK = // To be removed, it is not used anymore
71        "ENCODE_ON_DISK";
72    public static final String DATA_BLOCK_ENCODING =
73        "DATA_BLOCK_ENCODING";
74    /**
75     * Key for the BLOCKCACHE attribute.
76     * A more exact name would be CACHE_DATA_ON_READ because this flag sets whether or not we
77     * cache DATA blocks.  We always cache INDEX and BLOOM blocks; caching these blocks cannot be
78     * disabled.
79     */
80    public static final String BLOCKCACHE = "BLOCKCACHE";
81    public static final String CACHE_DATA_ON_WRITE = "CACHE_DATA_ON_WRITE";
82    public static final String CACHE_INDEX_ON_WRITE = "CACHE_INDEX_ON_WRITE";
83    public static final String CACHE_BLOOMS_ON_WRITE = "CACHE_BLOOMS_ON_WRITE";
84    public static final String EVICT_BLOCKS_ON_CLOSE = "EVICT_BLOCKS_ON_CLOSE";
85    /**
86     * Key for cache data into L1 if cache is set up with more than one tier.
87     * To set in the shell, do something like this:
88     * <code>hbase(main):003:0&gt; create 't',
89     *    {NAME =&gt; 't', CONFIGURATION =&gt; {CACHE_DATA_IN_L1 =&gt; 'true'}}</code>
90     */
91    public static final String CACHE_DATA_IN_L1 = "CACHE_DATA_IN_L1";
92  
93    /**
94     * Key for the PREFETCH_BLOCKS_ON_OPEN attribute.
95     * If set, all INDEX, BLOOM, and DATA blocks of HFiles belonging to this
96     * family will be loaded into the cache as soon as the file is opened. These
97     * loads will not count as cache misses.
98     */
99    public static final String PREFETCH_BLOCKS_ON_OPEN = "PREFETCH_BLOCKS_ON_OPEN";
100 
101   /**
102    * Size of storefile/hfile 'blocks'.  Default is {@link #DEFAULT_BLOCKSIZE}.
103    * Use smaller block sizes for faster random-access at expense of larger
104    * indices (more memory consumption).
105    */
106   public static final String BLOCKSIZE = "BLOCKSIZE";
107 
108   public static final String LENGTH = "LENGTH";
109   public static final String TTL = "TTL";
110   public static final String BLOOMFILTER = "BLOOMFILTER";
111   public static final String FOREVER = "FOREVER";
112   public static final String REPLICATION_SCOPE = "REPLICATION_SCOPE";
113   public static final byte[] REPLICATION_SCOPE_BYTES = Bytes.toBytes(REPLICATION_SCOPE);
114   public static final String MIN_VERSIONS = "MIN_VERSIONS";
115   /**
116    * Retain all cells across flushes and compactions even if they fall behind
117    * a delete tombstone. To see all retained cells, do a 'raw' scan; see
118    * Scan#setRaw or pass RAW =&gt; true attribute in the shell.
119    */
120   public static final String KEEP_DELETED_CELLS = "KEEP_DELETED_CELLS";
121   public static final String COMPRESS_TAGS = "COMPRESS_TAGS";
122 
123   public static final String ENCRYPTION = "ENCRYPTION";
124   public static final String ENCRYPTION_KEY = "ENCRYPTION_KEY";
125 
126   public static final String IS_MOB = "IS_MOB";
127   public static final byte[] IS_MOB_BYTES = Bytes.toBytes(IS_MOB);
128   public static final String MOB_THRESHOLD = "MOB_THRESHOLD";
129   public static final byte[] MOB_THRESHOLD_BYTES = Bytes.toBytes(MOB_THRESHOLD);
130   public static final long DEFAULT_MOB_THRESHOLD = 100 * 1024; // 100k
131 
132   /**
133    * Default compression type.
134    */
135   public static final String DEFAULT_COMPRESSION =
136     Compression.Algorithm.NONE.getName();
137 
138   /**
139    * Default value of the flag that enables data block encoding on disk, as
140    * opposed to encoding in cache only. We encode blocks everywhere by default,
141    * as long as {@link #DATA_BLOCK_ENCODING} is not NONE.
142    */
143   public static final boolean DEFAULT_ENCODE_ON_DISK = true;
144 
145   /** Default data block encoding algorithm. */
146   public static final String DEFAULT_DATA_BLOCK_ENCODING =
147       DataBlockEncoding.NONE.toString();
148 
149   /**
150    * Default number of versions of a record to keep.
151    */
152   public static final int DEFAULT_VERSIONS = HBaseConfiguration.create().getInt(
153     "hbase.column.max.version", 1);
154 
155   /**
156    * Default is not to keep a minimum of versions.
157    */
158   public static final int DEFAULT_MIN_VERSIONS = 0;
159 
160   /*
161    * Cache here the HCD value.
162    * Question: its OK to cache since when we're reenable, we create a new HCD?
163    */
164   private volatile Integer blocksize = null;
165 
166   /**
167    * Default setting for whether to try and serve this column family from memory or not.
168    */
169   public static final boolean DEFAULT_IN_MEMORY = false;
170 
171   /**
172    * Default setting for preventing deleted from being collected immediately.
173    */
174   public static final KeepDeletedCells DEFAULT_KEEP_DELETED = KeepDeletedCells.FALSE;
175 
176   /**
177    * Default setting for whether to use a block cache or not.
178    */
179   public static final boolean DEFAULT_BLOCKCACHE = true;
180 
181   /**
182    * Default setting for whether to cache data blocks on write if block caching
183    * is enabled.
184    */
185   public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
186 
187   /**
188    * Default setting for whether to cache data blocks in L1 tier.  Only makes sense if more than
189    * one tier in operations: i.e. if we have an L1 and a L2.  This will be the cases if we are
190    * using BucketCache.
191    */
192   public static final boolean DEFAULT_CACHE_DATA_IN_L1 = false;
193 
194   /**
195    * Default setting for whether to cache index blocks on write if block
196    * caching is enabled.
197    */
198   public static final boolean DEFAULT_CACHE_INDEX_ON_WRITE = false;
199 
200   /**
201    * Default size of blocks in files stored to the filesytem (hfiles).
202    */
203   public static final int DEFAULT_BLOCKSIZE = HConstants.DEFAULT_BLOCKSIZE;
204 
205   /**
206    * Default setting for whether or not to use bloomfilters.
207    */
208   public static final String DEFAULT_BLOOMFILTER = BloomType.ROW.toString();
209 
210   /**
211    * Default setting for whether to cache bloom filter blocks on write if block
212    * caching is enabled.
213    */
214   public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
215 
216   /**
217    * Default time to live of cell contents.
218    */
219   public static final int DEFAULT_TTL = HConstants.FOREVER;
220 
221   /**
222    * Default scope.
223    */
224   public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
225 
226   /**
227    * Default setting for whether to evict cached blocks from the blockcache on
228    * close.
229    */
230   public static final boolean DEFAULT_EVICT_BLOCKS_ON_CLOSE = false;
231 
232   /**
233    * Default compress tags along with any type of DataBlockEncoding.
234    */
235   public static final boolean DEFAULT_COMPRESS_TAGS = true;
236 
237   /*
238    * Default setting for whether to prefetch blocks into the blockcache on open.
239    */
240   public static final boolean DEFAULT_PREFETCH_BLOCKS_ON_OPEN = false;
241 
242   private final static Map<String, String> DEFAULT_VALUES
243     = new HashMap<String, String>();
244   private final static Set<Bytes> RESERVED_KEYWORDS
245       = new HashSet<Bytes>();
246 
247   static {
248       DEFAULT_VALUES.put(BLOOMFILTER, DEFAULT_BLOOMFILTER);
249       DEFAULT_VALUES.put(REPLICATION_SCOPE, String.valueOf(DEFAULT_REPLICATION_SCOPE));
250       DEFAULT_VALUES.put(HConstants.VERSIONS, String.valueOf(DEFAULT_VERSIONS));
251       DEFAULT_VALUES.put(MIN_VERSIONS, String.valueOf(DEFAULT_MIN_VERSIONS));
252       DEFAULT_VALUES.put(COMPRESSION, DEFAULT_COMPRESSION);
253       DEFAULT_VALUES.put(TTL, String.valueOf(DEFAULT_TTL));
254       DEFAULT_VALUES.put(BLOCKSIZE, String.valueOf(DEFAULT_BLOCKSIZE));
255       DEFAULT_VALUES.put(HConstants.IN_MEMORY, String.valueOf(DEFAULT_IN_MEMORY));
256       DEFAULT_VALUES.put(BLOCKCACHE, String.valueOf(DEFAULT_BLOCKCACHE));
257       DEFAULT_VALUES.put(KEEP_DELETED_CELLS, String.valueOf(DEFAULT_KEEP_DELETED));
258       DEFAULT_VALUES.put(DATA_BLOCK_ENCODING, String.valueOf(DEFAULT_DATA_BLOCK_ENCODING));
259       DEFAULT_VALUES.put(CACHE_DATA_ON_WRITE, String.valueOf(DEFAULT_CACHE_DATA_ON_WRITE));
260       DEFAULT_VALUES.put(CACHE_DATA_IN_L1, String.valueOf(DEFAULT_CACHE_DATA_IN_L1));
261       DEFAULT_VALUES.put(CACHE_INDEX_ON_WRITE, String.valueOf(DEFAULT_CACHE_INDEX_ON_WRITE));
262       DEFAULT_VALUES.put(CACHE_BLOOMS_ON_WRITE, String.valueOf(DEFAULT_CACHE_BLOOMS_ON_WRITE));
263       DEFAULT_VALUES.put(EVICT_BLOCKS_ON_CLOSE, String.valueOf(DEFAULT_EVICT_BLOCKS_ON_CLOSE));
264       DEFAULT_VALUES.put(PREFETCH_BLOCKS_ON_OPEN, String.valueOf(DEFAULT_PREFETCH_BLOCKS_ON_OPEN));
265       for (String s : DEFAULT_VALUES.keySet()) {
266         RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(s)));
267       }
268       RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(ENCRYPTION)));
269       RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(ENCRYPTION_KEY)));
270       RESERVED_KEYWORDS.add(new Bytes(IS_MOB_BYTES));
271       RESERVED_KEYWORDS.add(new Bytes(MOB_THRESHOLD_BYTES));
272   }
273 
274   private static final int UNINITIALIZED = -1;
275 
276   // Column family name
277   private byte [] name;
278 
279   // Column metadata
280   private final Map<Bytes, Bytes> values =
281       new HashMap<Bytes, Bytes>();
282 
283   /**
284    * A map which holds the configuration specific to the column family.
285    * The keys of the map have the same names as config keys and override the defaults with
286    * cf-specific settings. Example usage may be for compactions, etc.
287    */
288   private final Map<String, String> configuration = new HashMap<String, String>();
289 
290   /*
291    * Cache the max versions rather than calculate it every time.
292    */
293   private int cachedMaxVersions = UNINITIALIZED;
294 
295   /**
296    * Default constructor. Must be present for PB deserializations.
297    */
298   private HColumnDescriptor() {
299     this.name = null;
300   }
301 
302   /**
303    * Construct a column descriptor specifying only the family name
304    * The other attributes are defaulted.
305    *
306    * @param familyName Column family name. Must be 'printable' -- digit or
307    * letter -- and may not contain a <code>:</code>
308    */
309   public HColumnDescriptor(final String familyName) {
310     this(Bytes.toBytes(familyName));
311   }
312 
313   /**
314    * Construct a column descriptor specifying only the family name
315    * The other attributes are defaulted.
316    *
317    * @param familyName Column family name. Must be 'printable' -- digit or
318    * letter -- and may not contain a <code>:</code>
319    */
320   public HColumnDescriptor(final byte [] familyName) {
321     isLegalFamilyName(familyName);
322     this.name = familyName;
323 
324     setMaxVersions(DEFAULT_VERSIONS);
325     setMinVersions(DEFAULT_MIN_VERSIONS);
326     setKeepDeletedCells(DEFAULT_KEEP_DELETED);
327     setInMemory(DEFAULT_IN_MEMORY);
328     setBlockCacheEnabled(DEFAULT_BLOCKCACHE);
329     setTimeToLive(DEFAULT_TTL);
330     setCompressionType(Compression.Algorithm.valueOf(DEFAULT_COMPRESSION.toUpperCase()));
331     setDataBlockEncoding(DataBlockEncoding.valueOf(DEFAULT_DATA_BLOCK_ENCODING.toUpperCase()));
332     setBloomFilterType(BloomType.valueOf(DEFAULT_BLOOMFILTER.toUpperCase()));
333     setBlocksize(DEFAULT_BLOCKSIZE);
334     setScope(DEFAULT_REPLICATION_SCOPE);
335   }
336 
337   /**
338    * Constructor.
339    * Makes a deep copy of the supplied descriptor.
340    * Can make a modifiable descriptor from an UnmodifyableHColumnDescriptor.
341    * @param desc The descriptor.
342    */
343   public HColumnDescriptor(HColumnDescriptor desc) {
344     super();
345     this.name = desc.name.clone();
346     for (Map.Entry<Bytes, Bytes> e :
347         desc.values.entrySet()) {
348       this.values.put(e.getKey(), e.getValue());
349     }
350     for (Map.Entry<String, String> e : desc.configuration.entrySet()) {
351       this.configuration.put(e.getKey(), e.getValue());
352     }
353     setMaxVersions(desc.getMaxVersions());
354   }
355 
356   /**
357    * @param b Family name.
358    * @return <code>b</code>
359    * @throws IllegalArgumentException If not null and not a legitimate family
360    * name: i.e. 'printable' and ends in a ':' (Null passes are allowed because
361    * <code>b</code> can be null when deserializing).  Cannot start with a '.'
362    * either. Also Family can not be an empty value or equal "recovered.edits".
363    */
364   public static byte [] isLegalFamilyName(final byte [] b) {
365     if (b == null) {
366       return b;
367     }
368     Preconditions.checkArgument(b.length != 0, "Family name can not be empty");
369     if (b[0] == '.') {
370       throw new IllegalArgumentException("Family names cannot start with a " +
371         "period: " + Bytes.toString(b));
372     }
373     for (int i = 0; i < b.length; i++) {
374       if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
375         throw new IllegalArgumentException("Illegal character <" + b[i] +
376           ">. Family names cannot contain control characters or colons: " +
377           Bytes.toString(b));
378       }
379     }
380     byte[] recoveredEdit = Bytes.toBytes(HConstants.RECOVERED_EDITS_DIR);
381     if (Bytes.equals(recoveredEdit, b)) {
382       throw new IllegalArgumentException("Family name cannot be: " +
383           HConstants.RECOVERED_EDITS_DIR);
384     }
385     return b;
386   }
387 
388   /**
389    * @return Name of this column family
390    */
391   public byte [] getName() {
392     return name;
393   }
394 
395   /**
396    * @return Name of this column family
397    */
398   public String getNameAsString() {
399     return Bytes.toString(this.name);
400   }
401 
402   /**
403    * @param key The key.
404    * @return The value.
405    */
406   public byte[] getValue(byte[] key) {
407     Bytes ibw = values.get(new Bytes(key));
408     if (ibw == null)
409       return null;
410     return ibw.get();
411   }
412 
413   /**
414    * @param key The key.
415    * @return The value as a string.
416    */
417   public String getValue(String key) {
418     byte[] value = getValue(Bytes.toBytes(key));
419     if (value == null)
420       return null;
421     return Bytes.toString(value);
422   }
423 
424   /**
425    * @return All values.
426    */
427   public Map<Bytes, Bytes> getValues() {
428     // shallow pointer copy
429     return Collections.unmodifiableMap(values);
430   }
431 
432   /**
433    * @param key The key.
434    * @param value The value.
435    * @return this (for chained invocation)
436    */
437   public HColumnDescriptor setValue(byte[] key, byte[] value) {
438     values.put(new Bytes(key),
439         new Bytes(value));
440     return this;
441   }
442 
443   /**
444    * @param key Key whose key and value we're to remove from HCD parameters.
445    */
446   public void remove(final byte [] key) {
447     values.remove(new Bytes(key));
448   }
449 
450   /**
451    * @param key The key.
452    * @param value The value.
453    * @return this (for chained invocation)
454    */
455   public HColumnDescriptor setValue(String key, String value) {
456     if (value == null) {
457       remove(Bytes.toBytes(key));
458     } else {
459       setValue(Bytes.toBytes(key), Bytes.toBytes(value));
460     }
461     return this;
462   }
463 
464   /**
465    * @return compression type being used for the column family
466    * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0
467    *             (<a href="https://issues.apache.org/jira/browse/HBASE-13655">HBASE-13655</a>).
468    *             Use {@link #getCompressionType()}.
469    */
470   @Deprecated
471   public Compression.Algorithm getCompression() {
472     return getCompressionType();
473   }
474 
475   /**
476    *  @return compression type being used for the column family for major compaction
477    *  @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0
478    *             (<a href="https://issues.apache.org/jira/browse/HBASE-13655">HBASE-13655</a>).
479    *             Use {@link #getCompactionCompressionType()}.
480    */
481   @Deprecated
482   public Compression.Algorithm getCompactionCompression() {
483     return getCompactionCompressionType();
484   }
485 
486   /** @return maximum number of versions */
487   public int getMaxVersions() {
488     if (this.cachedMaxVersions == UNINITIALIZED) {
489       String v = getValue(HConstants.VERSIONS);
490       this.cachedMaxVersions = Integer.parseInt(v);
491     }
492     return this.cachedMaxVersions;
493   }
494 
495   /**
496    * @param maxVersions maximum number of versions
497    * @return this (for chained invocation)
498    */
499   public HColumnDescriptor setMaxVersions(int maxVersions) {
500     if (maxVersions <= 0) {
501       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
502       // Until there is support, consider 0 or < 0 -- a configuration error.
503       throw new IllegalArgumentException("Maximum versions must be positive");
504     }
505     if (maxVersions < this.getMinVersions()) {
506         throw new IllegalArgumentException("Set MaxVersion to " + maxVersions
507             + " while minVersion is " + this.getMinVersions()
508             + ". Maximum versions must be >= minimum versions ");
509     }
510     setValue(HConstants.VERSIONS, Integer.toString(maxVersions));
511     cachedMaxVersions = maxVersions;
512     return this;
513   }
514 
515   /**
516    * Set minimum and maximum versions to keep
517    *
518    * @param minVersions minimal number of versions
519    * @param maxVersions maximum number of versions
520    * @return this (for chained invocation)
521    */
522   public HColumnDescriptor setVersions(int minVersions, int maxVersions) {
523     if (minVersions <= 0) {
524       // TODO: Allow minVersion and maxVersion of 0 to be the way you say "Keep all versions".
525       // Until there is support, consider 0 or < 0 -- a configuration error.
526       throw new IllegalArgumentException("Minimum versions must be positive");
527     }
528 
529     if (maxVersions < minVersions) {
530       throw new IllegalArgumentException("Unable to set MaxVersion to " + maxVersions
531         + " and set MinVersion to " + minVersions
532         + ", as maximum versions must be >= minimum versions.");
533     }
534     setMinVersions(minVersions);
535     setMaxVersions(maxVersions);
536     return this;
537   }
538 
539   /**
540    * @return The storefile/hfile blocksize for this column family.
541    */
542   public synchronized int getBlocksize() {
543     if (this.blocksize == null) {
544       String value = getValue(BLOCKSIZE);
545       this.blocksize = (value != null)?
546         Integer.decode(value): Integer.valueOf(DEFAULT_BLOCKSIZE);
547     }
548     return this.blocksize.intValue();
549 
550   }
551 
552   /**
553    * @param s Blocksize to use when writing out storefiles/hfiles on this
554    * column family.
555    * @return this (for chained invocation)
556    */
557   public HColumnDescriptor setBlocksize(int s) {
558     setValue(BLOCKSIZE, Integer.toString(s));
559     this.blocksize = null;
560     return this;
561   }
562 
563   /**
564    * @return Compression type setting.
565    */
566   public Compression.Algorithm getCompressionType() {
567     String n = getValue(COMPRESSION);
568     if (n == null) {
569       return Compression.Algorithm.NONE;
570     }
571     return Compression.Algorithm.valueOf(n.toUpperCase());
572   }
573 
574   /**
575    * Compression types supported in hbase.
576    * LZO is not bundled as part of the hbase distribution.
577    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
578    * for how to enable it.
579    * @param type Compression type setting.
580    * @return this (for chained invocation)
581    */
582   public HColumnDescriptor setCompressionType(Compression.Algorithm type) {
583     return setValue(COMPRESSION, type.getName().toUpperCase());
584   }
585 
586   /**
587    * @return the data block encoding algorithm used in block cache and
588    *         optionally on disk
589    */
590   public DataBlockEncoding getDataBlockEncoding() {
591     String type = getValue(DATA_BLOCK_ENCODING);
592     if (type == null) {
593       type = DEFAULT_DATA_BLOCK_ENCODING;
594     }
595     return DataBlockEncoding.valueOf(type);
596   }
597 
598   /**
599    * Set data block encoding algorithm used in block cache.
600    * @param type What kind of data block encoding will be used.
601    * @return this (for chained invocation)
602    */
603   public HColumnDescriptor setDataBlockEncoding(DataBlockEncoding type) {
604     String name;
605     if (type != null) {
606       name = type.toString();
607     } else {
608       name = DataBlockEncoding.NONE.toString();
609     }
610     return setValue(DATA_BLOCK_ENCODING, name);
611   }
612 
613   /**
614    * Set whether the tags should be compressed along with DataBlockEncoding. When no
615    * DataBlockEncoding is been used, this is having no effect.
616    *
617    * @param compressTags
618    * @return this (for chained invocation)
619    */
620   public HColumnDescriptor setCompressTags(boolean compressTags) {
621     return setValue(COMPRESS_TAGS, String.valueOf(compressTags));
622   }
623 
624   /**
625    * @return Whether KV tags should be compressed along with DataBlockEncoding. When no
626    *         DataBlockEncoding is been used, this is having no effect.
627    */
628   public boolean isCompressTags() {
629     String compressTagsStr = getValue(COMPRESS_TAGS);
630     boolean compressTags = DEFAULT_COMPRESS_TAGS;
631     if (compressTagsStr != null) {
632       compressTags = Boolean.parseBoolean(compressTagsStr);
633     }
634     return compressTags;
635   }
636 
637   /**
638    * @return Compression type setting.
639    */
640   public Compression.Algorithm getCompactionCompressionType() {
641     String n = getValue(COMPRESSION_COMPACT);
642     if (n == null) {
643       return getCompressionType();
644     }
645     return Compression.Algorithm.valueOf(n.toUpperCase());
646   }
647 
648   /**
649    * Compression types supported in hbase.
650    * LZO is not bundled as part of the hbase distribution.
651    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
652    * for how to enable it.
653    * @param type Compression type setting.
654    * @return this (for chained invocation)
655    */
656   public HColumnDescriptor setCompactionCompressionType(
657       Compression.Algorithm type) {
658     return setValue(COMPRESSION_COMPACT, type.getName().toUpperCase());
659   }
660 
661   /**
662    * @return True if we are to favor keeping all values for this column family in the
663    * HRegionServer cache.
664    */
665   public boolean isInMemory() {
666     String value = getValue(HConstants.IN_MEMORY);
667     if (value != null) {
668       return Boolean.parseBoolean(value);
669     }
670     return DEFAULT_IN_MEMORY;
671   }
672 
673   /**
674    * @param inMemory True if we are to favor keeping all values for this column family in the
675    * HRegionServer cache
676    * @return this (for chained invocation)
677    */
678   public HColumnDescriptor setInMemory(boolean inMemory) {
679     return setValue(HConstants.IN_MEMORY, Boolean.toString(inMemory));
680   }
681 
682   public KeepDeletedCells getKeepDeletedCells() {
683     String value = getValue(KEEP_DELETED_CELLS);
684     if (value != null) {
685       // toUpperCase for backwards compatibility
686       return KeepDeletedCells.valueOf(value.toUpperCase());
687     }
688     return DEFAULT_KEEP_DELETED;
689   }
690 
691   /**
692    * @param keepDeletedCells True if deleted rows should not be collected
693    * immediately.
694    * @return this (for chained invocation)
695    */
696   public HColumnDescriptor setKeepDeletedCells(KeepDeletedCells keepDeletedCells) {
697     return setValue(KEEP_DELETED_CELLS, keepDeletedCells.toString());
698   }
699 
700   /**
701    * @return Time-to-live of cell contents, in seconds.
702    */
703   public int getTimeToLive() {
704     String value = getValue(TTL);
705     return (value != null)? Integer.parseInt(value) : DEFAULT_TTL;
706   }
707 
708   /**
709    * @param timeToLive Time-to-live of cell contents, in seconds.
710    * @return this (for chained invocation)
711    */
712   public HColumnDescriptor setTimeToLive(int timeToLive) {
713     return setValue(TTL, Integer.toString(timeToLive));
714   }
715 
716   /**
717    * @return The minimum number of versions to keep.
718    */
719   public int getMinVersions() {
720     String value = getValue(MIN_VERSIONS);
721     return (value != null)? Integer.parseInt(value) : 0;
722   }
723 
724   /**
725    * @param minVersions The minimum number of versions to keep.
726    * (used when timeToLive is set)
727    * @return this (for chained invocation)
728    */
729   public HColumnDescriptor setMinVersions(int minVersions) {
730     return setValue(MIN_VERSIONS, Integer.toString(minVersions));
731   }
732 
733   /**
734    * @return True if hfile DATA type blocks should be cached (You cannot disable caching of INDEX
735    * and BLOOM type blocks).
736    */
737   public boolean isBlockCacheEnabled() {
738     String value = getValue(BLOCKCACHE);
739     if (value != null) {
740       return Boolean.parseBoolean(value);
741     }
742     return DEFAULT_BLOCKCACHE;
743   }
744 
745   /**
746    * @param blockCacheEnabled True if hfile DATA type blocks should be cached (We always cache
747    * INDEX and BLOOM blocks; you cannot turn this off).
748    * @return this (for chained invocation)
749    */
750   public HColumnDescriptor setBlockCacheEnabled(boolean blockCacheEnabled) {
751     return setValue(BLOCKCACHE, Boolean.toString(blockCacheEnabled));
752   }
753 
754   /**
755    * @return bloom filter type used for new StoreFiles in ColumnFamily
756    */
757   public BloomType getBloomFilterType() {
758     String n = getValue(BLOOMFILTER);
759     if (n == null) {
760       n = DEFAULT_BLOOMFILTER;
761     }
762     return BloomType.valueOf(n.toUpperCase());
763   }
764 
765   /**
766    * @param bt bloom filter type
767    * @return this (for chained invocation)
768    */
769   public HColumnDescriptor setBloomFilterType(final BloomType bt) {
770     return setValue(BLOOMFILTER, bt.toString());
771   }
772 
773    /**
774     * @return the scope tag
775     */
776   public int getScope() {
777     byte[] value = getValue(REPLICATION_SCOPE_BYTES);
778     if (value != null) {
779       return Integer.parseInt(Bytes.toString(value));
780     }
781     return DEFAULT_REPLICATION_SCOPE;
782   }
783 
784  /**
785   * @param scope the scope tag
786   * @return this (for chained invocation)
787   */
788   public HColumnDescriptor setScope(int scope) {
789     return setValue(REPLICATION_SCOPE, Integer.toString(scope));
790   }
791 
792   /**
793    * @return true if we should cache data blocks on write
794    */
795   public boolean isCacheDataOnWrite() {
796     return setAndGetBoolean(CACHE_DATA_ON_WRITE, DEFAULT_CACHE_DATA_ON_WRITE);
797   }
798 
799   /**
800    * @param value true if we should cache data blocks on write
801    * @return this (for chained invocation)
802    */
803   public HColumnDescriptor setCacheDataOnWrite(boolean value) {
804     return setValue(CACHE_DATA_ON_WRITE, Boolean.toString(value));
805   }
806 
807   /**
808    * @return true if we should cache data blocks in the L1 cache (if block cache deploy has more
809    *         than one tier; e.g. we are using CombinedBlockCache).
810    */
811   public boolean isCacheDataInL1() {
812     return setAndGetBoolean(CACHE_DATA_IN_L1, DEFAULT_CACHE_DATA_IN_L1);
813   }
814 
815   /**
816    * @param value true if we should cache data blocks in the L1 cache (if block cache deploy
817    * has more than one tier; e.g. we are using CombinedBlockCache).
818    * @return this (for chained invocation)
819    */
820   public HColumnDescriptor setCacheDataInL1(boolean value) {
821     return setValue(CACHE_DATA_IN_L1, Boolean.toString(value));
822   }
823 
824   private boolean setAndGetBoolean(final String key, final boolean defaultSetting) {
825     String value = getValue(key);
826     if (value != null) {
827       return Boolean.parseBoolean(value);
828     }
829     return defaultSetting;
830   }
831 
832   /**
833    * @return true if we should cache index blocks on write
834    */
835   public boolean isCacheIndexesOnWrite() {
836     return setAndGetBoolean(CACHE_INDEX_ON_WRITE, DEFAULT_CACHE_INDEX_ON_WRITE);
837   }
838 
839   /**
840    * @param value true if we should cache index blocks on write
841    * @return this (for chained invocation)
842    */
843   public HColumnDescriptor setCacheIndexesOnWrite(boolean value) {
844     return setValue(CACHE_INDEX_ON_WRITE, Boolean.toString(value));
845   }
846 
847   /**
848    * @return true if we should cache bloomfilter blocks on write
849    */
850   public boolean isCacheBloomsOnWrite() {
851     return setAndGetBoolean(CACHE_BLOOMS_ON_WRITE, DEFAULT_CACHE_BLOOMS_ON_WRITE);
852   }
853 
854   /**
855    * @param value true if we should cache bloomfilter blocks on write
856    * @return this (for chained invocation)
857    */
858   public HColumnDescriptor setCacheBloomsOnWrite(boolean value) {
859     return setValue(CACHE_BLOOMS_ON_WRITE, Boolean.toString(value));
860   }
861 
862   /**
863    * @return true if we should evict cached blocks from the blockcache on close
864    */
865   public boolean isEvictBlocksOnClose() {
866     return setAndGetBoolean(EVICT_BLOCKS_ON_CLOSE, DEFAULT_EVICT_BLOCKS_ON_CLOSE);
867   }
868 
869   /**
870    * @param value true if we should evict cached blocks from the blockcache on
871    * close
872    * @return this (for chained invocation)
873    */
874   public HColumnDescriptor setEvictBlocksOnClose(boolean value) {
875     return setValue(EVICT_BLOCKS_ON_CLOSE, Boolean.toString(value));
876   }
877 
878   /**
879    * @return true if we should prefetch blocks into the blockcache on open
880    */
881   public boolean isPrefetchBlocksOnOpen() {
882     return setAndGetBoolean(PREFETCH_BLOCKS_ON_OPEN, DEFAULT_PREFETCH_BLOCKS_ON_OPEN);
883   }
884 
885   /**
886    * @param value true if we should prefetch blocks into the blockcache on open
887    * @return this (for chained invocation)
888    */
889   public HColumnDescriptor setPrefetchBlocksOnOpen(boolean value) {
890     return setValue(PREFETCH_BLOCKS_ON_OPEN, Boolean.toString(value));
891   }
892 
893   /**
894    * @see java.lang.Object#toString()
895    */
896   @Override
897   public String toString() {
898     StringBuilder s = new StringBuilder();
899 
900     s.append('{');
901     s.append(HConstants.NAME);
902     s.append(" => '");
903     s.append(Bytes.toString(name));
904     s.append("'");
905     s.append(getValues(true));
906     s.append('}');
907     return s.toString();
908   }
909 
910   /**
911    * @return Column family descriptor with only the customized attributes.
912    */
913   public String toStringCustomizedValues() {
914     StringBuilder s = new StringBuilder();
915     s.append('{');
916     s.append(HConstants.NAME);
917     s.append(" => '");
918     s.append(Bytes.toString(name));
919     s.append("'");
920     s.append(getValues(false));
921     s.append('}');
922     return s.toString();
923   }
924 
925   private StringBuilder getValues(boolean printDefaults) {
926     StringBuilder s = new StringBuilder();
927 
928     boolean hasConfigKeys = false;
929 
930     // print all reserved keys first
931     for (Map.Entry<Bytes, Bytes> entry : values.entrySet()) {
932       if (!RESERVED_KEYWORDS.contains(entry.getKey())) {
933         hasConfigKeys = true;
934         continue;
935       }
936       String key = Bytes.toString(entry.getKey().get());
937       String value = Bytes.toStringBinary(entry.getValue().get());
938       if (printDefaults
939           || !DEFAULT_VALUES.containsKey(key)
940           || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
941         s.append(", ");
942         s.append(key);
943         s.append(" => ");
944         s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
945       }
946     }
947 
948     // print all non-reserved, advanced config keys as a separate subset
949     if (hasConfigKeys) {
950       s.append(", ");
951       s.append(HConstants.METADATA).append(" => ");
952       s.append('{');
953       boolean printComma = false;
954       for (Bytes k : values.keySet()) {
955         if (RESERVED_KEYWORDS.contains(k)) {
956           continue;
957         }
958         String key = Bytes.toString(k.get());
959         String value = Bytes.toStringBinary(values.get(k).get());
960         if (printComma) {
961           s.append(", ");
962         }
963         printComma = true;
964         s.append('\'').append(key).append('\'');
965         s.append(" => ");
966         s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
967       }
968       s.append('}');
969     }
970 
971     if (!configuration.isEmpty()) {
972       s.append(", ");
973       s.append(HConstants.CONFIGURATION).append(" => ");
974       s.append('{');
975       boolean printCommaForConfiguration = false;
976       for (Map.Entry<String, String> e : configuration.entrySet()) {
977         if (printCommaForConfiguration) s.append(", ");
978         printCommaForConfiguration = true;
979         s.append('\'').append(e.getKey()).append('\'');
980         s.append(" => ");
981         s.append('\'').append(PrettyPrinter.format(e.getValue(), getUnit(e.getKey()))).append('\'');
982       }
983       s.append("}");
984     }
985     return s;
986   }
987 
988   public static Unit getUnit(String key) {
989     Unit unit;
990       /* TTL for now, we can add more as we neeed */
991     if (key.equals(HColumnDescriptor.TTL)) {
992       unit = Unit.TIME_INTERVAL;
993     } else if (key.equals(HColumnDescriptor.MOB_THRESHOLD)) {
994       unit = Unit.LONG;
995     } else if (key.equals(HColumnDescriptor.IS_MOB)) {
996       unit = Unit.BOOLEAN;
997     } else {
998       unit = Unit.NONE;
999     }
1000     return unit;
1001   }
1002 
1003   public static Map<String, String> getDefaultValues() {
1004     return Collections.unmodifiableMap(DEFAULT_VALUES);
1005   }
1006 
1007   /**
1008    * @see java.lang.Object#equals(java.lang.Object)
1009    */
1010   @Override
1011   public boolean equals(Object obj) {
1012     if (this == obj) {
1013       return true;
1014     }
1015     if (obj == null) {
1016       return false;
1017     }
1018     if (!(obj instanceof HColumnDescriptor)) {
1019       return false;
1020     }
1021     return compareTo((HColumnDescriptor)obj) == 0;
1022   }
1023 
1024   /**
1025    * @see java.lang.Object#hashCode()
1026    */
1027   @Override
1028   public int hashCode() {
1029     int result = Bytes.hashCode(this.name);
1030     result ^= (int) COLUMN_DESCRIPTOR_VERSION;
1031     result ^= values.hashCode();
1032     result ^= configuration.hashCode();
1033     return result;
1034   }
1035 
1036   // Comparable
1037   @Override
1038   public int compareTo(HColumnDescriptor o) {
1039     int result = Bytes.compareTo(this.name, o.getName());
1040     if (result == 0) {
1041       // punt on comparison for ordering, just calculate difference
1042       result = this.values.hashCode() - o.values.hashCode();
1043       if (result < 0)
1044         result = -1;
1045       else if (result > 0)
1046         result = 1;
1047     }
1048     if (result == 0) {
1049       result = this.configuration.hashCode() - o.configuration.hashCode();
1050       if (result < 0)
1051         result = -1;
1052       else if (result > 0)
1053         result = 1;
1054     }
1055     return result;
1056   }
1057 
1058   /**
1059    * @return This instance serialized with pb with pb magic prefix
1060    * @see #parseFrom(byte[])
1061    */
1062   public byte [] toByteArray() {
1063     return ProtobufUtil.prependPBMagic(convert().toByteArray());
1064   }
1065 
1066   /**
1067    * @param bytes A pb serialized {@link HColumnDescriptor} instance with pb magic prefix
1068    * @return An instance of {@link HColumnDescriptor} made from <code>bytes</code>
1069    * @throws DeserializationException
1070    * @see #toByteArray()
1071    */
1072   public static HColumnDescriptor parseFrom(final byte [] bytes) throws DeserializationException {
1073     if (!ProtobufUtil.isPBMagicPrefix(bytes)) throw new DeserializationException("No magic");
1074     int pblen = ProtobufUtil.lengthOfPBMagic();
1075     ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1076     ColumnFamilySchema cfs = null;
1077     try {
1078       cfs = builder.mergeFrom(bytes, pblen, bytes.length - pblen).build();
1079     } catch (InvalidProtocolBufferException e) {
1080       throw new DeserializationException(e);
1081     }
1082     return convert(cfs);
1083   }
1084 
1085   /**
1086    * @param cfs
1087    * @return An {@link HColumnDescriptor} made from the passed in <code>cfs</code>
1088    */
1089   public static HColumnDescriptor convert(final ColumnFamilySchema cfs) {
1090     // Use the empty constructor so we preserve the initial values set on construction for things
1091     // like maxVersion.  Otherwise, we pick up wrong values on deserialization which makes for
1092     // unrelated-looking test failures that are hard to trace back to here.
1093     HColumnDescriptor hcd = new HColumnDescriptor();
1094     hcd.name = cfs.getName().toByteArray();
1095     for (BytesBytesPair a: cfs.getAttributesList()) {
1096       hcd.setValue(a.getFirst().toByteArray(), a.getSecond().toByteArray());
1097     }
1098     for (NameStringPair a: cfs.getConfigurationList()) {
1099       hcd.setConfiguration(a.getName(), a.getValue());
1100     }
1101     return hcd;
1102   }
1103 
1104   /**
1105    * @return Convert this instance to a the pb column family type
1106    */
1107   public ColumnFamilySchema convert() {
1108     ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1109     builder.setName(ByteStringer.wrap(getName()));
1110     for (Map.Entry<Bytes, Bytes> e : this.values.entrySet()) {
1111       BytesBytesPair.Builder aBuilder = BytesBytesPair.newBuilder();
1112       aBuilder.setFirst(ByteStringer.wrap(e.getKey().get()));
1113       aBuilder.setSecond(ByteStringer.wrap(e.getValue().get()));
1114       builder.addAttributes(aBuilder.build());
1115     }
1116     for (Map.Entry<String, String> e : this.configuration.entrySet()) {
1117       NameStringPair.Builder aBuilder = NameStringPair.newBuilder();
1118       aBuilder.setName(e.getKey());
1119       aBuilder.setValue(e.getValue());
1120       builder.addConfiguration(aBuilder.build());
1121     }
1122     return builder.build();
1123   }
1124 
1125   /**
1126    * Getter for accessing the configuration value by key.
1127    */
1128   public String getConfigurationValue(String key) {
1129     return configuration.get(key);
1130   }
1131 
1132   /**
1133    * Getter for fetching an unmodifiable {@link #configuration} map.
1134    */
1135   public Map<String, String> getConfiguration() {
1136     // shallow pointer copy
1137     return Collections.unmodifiableMap(configuration);
1138   }
1139 
1140   /**
1141    * Setter for storing a configuration setting in {@link #configuration} map.
1142    * @param key Config key. Same as XML config key e.g. hbase.something.or.other.
1143    * @param value String value. If null, removes the configuration.
1144    */
1145   public HColumnDescriptor setConfiguration(String key, String value) {
1146     if (value == null) {
1147       removeConfiguration(key);
1148     } else {
1149       configuration.put(key, value);
1150     }
1151     return this;
1152   }
1153 
1154   /**
1155    * Remove a configuration setting represented by the key from the {@link #configuration} map.
1156    */
1157   public void removeConfiguration(final String key) {
1158     configuration.remove(key);
1159   }
1160 
1161   /**
1162    * Return the encryption algorithm in use by this family
1163    */
1164   public String getEncryptionType() {
1165     return getValue(ENCRYPTION);
1166   }
1167 
1168   /**
1169    * Set the encryption algorithm for use with this family
1170    * @param algorithm
1171    */
1172   public HColumnDescriptor setEncryptionType(String algorithm) {
1173     setValue(ENCRYPTION, algorithm);
1174     return this;
1175   }
1176 
1177   /** Return the raw crypto key attribute for the family, or null if not set  */
1178   public byte[] getEncryptionKey() {
1179     return getValue(Bytes.toBytes(ENCRYPTION_KEY));
1180   }
1181 
1182   /** Set the raw crypto key attribute for the family */
1183   public HColumnDescriptor setEncryptionKey(byte[] keyBytes) {
1184     setValue(Bytes.toBytes(ENCRYPTION_KEY), keyBytes);
1185     return this;
1186   }
1187 
1188   /**
1189    * Gets the mob threshold of the family.
1190    * If the size of a cell value is larger than this threshold, it's regarded as a mob.
1191    * The default threshold is 1024*100(100K)B.
1192    * @return The mob threshold.
1193    */
1194   public long getMobThreshold() {
1195     byte[] threshold = getValue(MOB_THRESHOLD_BYTES);
1196     return threshold != null && threshold.length == Bytes.SIZEOF_LONG ? Bytes.toLong(threshold)
1197         : DEFAULT_MOB_THRESHOLD;
1198   }
1199 
1200   /**
1201    * Sets the mob threshold of the family.
1202    * @param threshold The mob threshold.
1203    * @return this (for chained invocation)
1204    */
1205   public HColumnDescriptor setMobThreshold(long threshold) {
1206     setValue(MOB_THRESHOLD_BYTES, Bytes.toBytes(threshold));
1207     return this;
1208   }
1209 
1210   /**
1211    * Gets whether the mob is enabled for the family.
1212    * @return True if the mob is enabled for the family.
1213    */
1214   public boolean isMobEnabled() {
1215     byte[] isMobEnabled = getValue(IS_MOB_BYTES);
1216     return isMobEnabled != null && isMobEnabled.length == Bytes.SIZEOF_BOOLEAN
1217         && Bytes.toBoolean(isMobEnabled);
1218   }
1219 
1220   /**
1221    * Enables the mob for the family.
1222    * @param isMobEnabled Whether to enable the mob for the family.
1223    * @return this (for chained invocation)
1224    */
1225   public HColumnDescriptor setMobEnabled(boolean isMobEnabled) {
1226     setValue(IS_MOB_BYTES, Bytes.toBytes(isMobEnabled));
1227     return this;
1228   }
1229 }