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> create 't',
89     *    {NAME => 't', CONFIGURATION => {CACHE_DATA_IN_L1 => '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 => 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   /**
127    * Default compression type.
128    */
129   public static final String DEFAULT_COMPRESSION =
130     Compression.Algorithm.NONE.getName();
131 
132   /**
133    * Default value of the flag that enables data block encoding on disk, as
134    * opposed to encoding in cache only. We encode blocks everywhere by default,
135    * as long as {@link #DATA_BLOCK_ENCODING} is not NONE.
136    */
137   public static final boolean DEFAULT_ENCODE_ON_DISK = true;
138 
139   /** Default data block encoding algorithm. */
140   public static final String DEFAULT_DATA_BLOCK_ENCODING =
141       DataBlockEncoding.NONE.toString();
142 
143   /**
144    * Default number of versions of a record to keep.
145    */
146   public static final int DEFAULT_VERSIONS = HBaseConfiguration.create().getInt(
147     "hbase.column.max.version", 1);
148 
149   /**
150    * Default is not to keep a minimum of versions.
151    */
152   public static final int DEFAULT_MIN_VERSIONS = 0;
153 
154   /*
155    * Cache here the HCD value.
156    * Question: its OK to cache since when we're reenable, we create a new HCD?
157    */
158   private volatile Integer blocksize = null;
159 
160   /**
161    * Default setting for whether to try and serve this column family from memory or not.
162    */
163   public static final boolean DEFAULT_IN_MEMORY = false;
164 
165   /**
166    * Default setting for preventing deleted from being collected immediately.
167    */
168   public static final KeepDeletedCells DEFAULT_KEEP_DELETED = KeepDeletedCells.FALSE;
169 
170   /**
171    * Default setting for whether to use a block cache or not.
172    */
173   public static final boolean DEFAULT_BLOCKCACHE = true;
174 
175   /**
176    * Default setting for whether to cache data blocks on write if block caching
177    * is enabled.
178    */
179   public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
180 
181   /**
182    * Default setting for whether to cache data blocks in L1 tier.  Only makes sense if more than
183    * one tier in operations: i.e. if we have an L1 and a L2.  This will be the cases if we are
184    * using BucketCache.
185    */
186   public static final boolean DEFAULT_CACHE_DATA_IN_L1 = false;
187 
188   /**
189    * Default setting for whether to cache index blocks on write if block
190    * caching is enabled.
191    */
192   public static final boolean DEFAULT_CACHE_INDEX_ON_WRITE = false;
193 
194   /**
195    * Default size of blocks in files stored to the filesytem (hfiles).
196    */
197   public static final int DEFAULT_BLOCKSIZE = HConstants.DEFAULT_BLOCKSIZE;
198 
199   /**
200    * Default setting for whether or not to use bloomfilters.
201    */
202   public static final String DEFAULT_BLOOMFILTER = BloomType.ROW.toString();
203 
204   /**
205    * Default setting for whether to cache bloom filter blocks on write if block
206    * caching is enabled.
207    */
208   public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
209 
210   /**
211    * Default time to live of cell contents.
212    */
213   public static final int DEFAULT_TTL = HConstants.FOREVER;
214 
215   /**
216    * Default scope.
217    */
218   public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
219 
220   /**
221    * Default setting for whether to evict cached blocks from the blockcache on
222    * close.
223    */
224   public static final boolean DEFAULT_EVICT_BLOCKS_ON_CLOSE = false;
225 
226   /**
227    * Default compress tags along with any type of DataBlockEncoding.
228    */
229   public static final boolean DEFAULT_COMPRESS_TAGS = true;
230 
231   /*
232    * Default setting for whether to prefetch blocks into the blockcache on open.
233    */
234   public static final boolean DEFAULT_PREFETCH_BLOCKS_ON_OPEN = false;
235 
236   private final static Map<String, String> DEFAULT_VALUES
237     = new HashMap<String, String>();
238   private final static Set<Bytes> RESERVED_KEYWORDS
239       = new HashSet<Bytes>();
240 
241   static {
242       DEFAULT_VALUES.put(BLOOMFILTER, DEFAULT_BLOOMFILTER);
243       DEFAULT_VALUES.put(REPLICATION_SCOPE, String.valueOf(DEFAULT_REPLICATION_SCOPE));
244       DEFAULT_VALUES.put(HConstants.VERSIONS, String.valueOf(DEFAULT_VERSIONS));
245       DEFAULT_VALUES.put(MIN_VERSIONS, String.valueOf(DEFAULT_MIN_VERSIONS));
246       DEFAULT_VALUES.put(COMPRESSION, DEFAULT_COMPRESSION);
247       DEFAULT_VALUES.put(TTL, String.valueOf(DEFAULT_TTL));
248       DEFAULT_VALUES.put(BLOCKSIZE, String.valueOf(DEFAULT_BLOCKSIZE));
249       DEFAULT_VALUES.put(HConstants.IN_MEMORY, String.valueOf(DEFAULT_IN_MEMORY));
250       DEFAULT_VALUES.put(BLOCKCACHE, String.valueOf(DEFAULT_BLOCKCACHE));
251       DEFAULT_VALUES.put(KEEP_DELETED_CELLS, String.valueOf(DEFAULT_KEEP_DELETED));
252       DEFAULT_VALUES.put(DATA_BLOCK_ENCODING, String.valueOf(DEFAULT_DATA_BLOCK_ENCODING));
253       DEFAULT_VALUES.put(CACHE_DATA_ON_WRITE, String.valueOf(DEFAULT_CACHE_DATA_ON_WRITE));
254       DEFAULT_VALUES.put(CACHE_DATA_IN_L1, String.valueOf(DEFAULT_CACHE_DATA_IN_L1));
255       DEFAULT_VALUES.put(CACHE_INDEX_ON_WRITE, String.valueOf(DEFAULT_CACHE_INDEX_ON_WRITE));
256       DEFAULT_VALUES.put(CACHE_BLOOMS_ON_WRITE, String.valueOf(DEFAULT_CACHE_BLOOMS_ON_WRITE));
257       DEFAULT_VALUES.put(EVICT_BLOCKS_ON_CLOSE, String.valueOf(DEFAULT_EVICT_BLOCKS_ON_CLOSE));
258       DEFAULT_VALUES.put(PREFETCH_BLOCKS_ON_OPEN, String.valueOf(DEFAULT_PREFETCH_BLOCKS_ON_OPEN));
259       for (String s : DEFAULT_VALUES.keySet()) {
260         RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(s)));
261       }
262     RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(ENCRYPTION)));
263     RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(ENCRYPTION_KEY)));
264   }
265 
266   private static final int UNINITIALIZED = -1;
267 
268   // Column family name
269   private byte [] name;
270 
271   // Column metadata
272   private final Map<Bytes, Bytes> values =
273       new HashMap<Bytes, Bytes>();
274 
275   /**
276    * A map which holds the configuration specific to the column family.
277    * The keys of the map have the same names as config keys and override the defaults with
278    * cf-specific settings. Example usage may be for compactions, etc.
279    */
280   private final Map<String, String> configuration = new HashMap<String, String>();
281 
282   /*
283    * Cache the max versions rather than calculate it every time.
284    */
285   private int cachedMaxVersions = UNINITIALIZED;
286 
287   /**
288    * Default constructor.
289    * @deprecated As of release 0.96
290    *             (<a href="https://issues.apache.org/jira/browse/HBASE-5453">HBASE-5453</a>).
291    *             This will be made private in HBase 2.0.0.
292    *             Used by Writables and Writables are going away.
293    */
294   @Deprecated
295   // Make this private rather than remove after deprecation period elapses.  Its needed by pb
296   // deserializations.
297   public HColumnDescriptor() {
298     this.name = null;
299   }
300 
301   /**
302    * Construct a column descriptor specifying only the family name
303    * The other attributes are defaulted.
304    *
305    * @param familyName Column family name. Must be 'printable' -- digit or
306    * letter -- and may not contain a <code>:</code>
307    */
308   public HColumnDescriptor(final String familyName) {
309     this(Bytes.toBytes(familyName));
310   }
311 
312   /**
313    * Construct a column descriptor specifying only the family name
314    * The other attributes are defaulted.
315    *
316    * @param familyName Column family name. Must be 'printable' -- digit or
317    * letter -- and may not contain a <code>:</code>
318    */
319   public HColumnDescriptor(final byte [] familyName) {
320     this (familyName == null || familyName.length <= 0?
321       HConstants.EMPTY_BYTE_ARRAY: familyName, DEFAULT_VERSIONS,
322       DEFAULT_COMPRESSION, DEFAULT_IN_MEMORY, DEFAULT_BLOCKCACHE,
323       DEFAULT_TTL, DEFAULT_BLOOMFILTER);
324   }
325 
326   /**
327    * Constructor.
328    * Makes a deep copy of the supplied descriptor.
329    * Can make a modifiable descriptor from an UnmodifyableHColumnDescriptor.
330    * @param desc The descriptor.
331    */
332   public HColumnDescriptor(HColumnDescriptor desc) {
333     super();
334     this.name = desc.name.clone();
335     for (Map.Entry<Bytes, Bytes> e :
336         desc.values.entrySet()) {
337       this.values.put(e.getKey(), e.getValue());
338     }
339     for (Map.Entry<String, String> e : desc.configuration.entrySet()) {
340       this.configuration.put(e.getKey(), e.getValue());
341     }
342     setMaxVersions(desc.getMaxVersions());
343   }
344 
345   /**
346    * Constructor
347    * @param familyName Column family name. Must be 'printable' -- digit or
348    * letter -- and may not contain a <code>:</code>
349    * @param maxVersions Maximum number of versions to keep
350    * @param compression Compression type
351    * @param inMemory If true, column data should be kept in an HRegionServer's
352    * cache
353    * @param blockCacheEnabled If true, MapFile blocks should be cached
354    * @param timeToLive Time-to-live of cell contents, in seconds
355    * (use HConstants.FOREVER for unlimited TTL)
356    * @param bloomFilter Bloom filter type for this column
357    *
358    * @throws IllegalArgumentException if passed a family name that is made of
359    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
360    * a <code>:</code>
361    * @throws IllegalArgumentException if the number of versions is &lt;= 0
362    * @deprecated As of release 0.96
363    *             (<a href="https://issues.apache.org/jira/browse/HBASE-">HBASE-</a>).
364    *             This will be removed in HBase 2.0.0.
365    *             Use {@link #HColumnDescriptor(String)} and setters.
366    */
367   @Deprecated
368   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
369       final String compression, final boolean inMemory,
370       final boolean blockCacheEnabled,
371       final int timeToLive, final String bloomFilter) {
372     this(familyName, maxVersions, compression, inMemory, blockCacheEnabled,
373       DEFAULT_BLOCKSIZE, timeToLive, bloomFilter, DEFAULT_REPLICATION_SCOPE);
374   }
375 
376   /**
377    * Constructor
378    * @param familyName Column family name. Must be 'printable' -- digit or
379    * letter -- and may not contain a <code>:</code>
380    * @param maxVersions Maximum number of versions to keep
381    * @param compression Compression type
382    * @param inMemory If true, column data should be kept in an HRegionServer's
383    * cache
384    * @param blockCacheEnabled If true, MapFile blocks should be cached
385    * @param blocksize Block size to use when writing out storefiles.  Use
386    * smaller block sizes for faster random-access at expense of larger indices
387    * (more memory consumption).  Default is usually 64k.
388    * @param timeToLive Time-to-live of cell contents, in seconds
389    * (use HConstants.FOREVER for unlimited TTL)
390    * @param bloomFilter Bloom filter type for this column
391    * @param scope The scope tag for this column
392    *
393    * @throws IllegalArgumentException if passed a family name that is made of
394    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
395    * a <code>:</code>
396    * @throws IllegalArgumentException if the number of versions is &lt;= 0
397    * @deprecated As of release 0.96
398    *             (<a href="https://issues.apache.org/jira/browse/HBASE-">HBASE-</a>).
399    *             This will be removed in HBase 2.0.0.
400    *             Use {@link #HColumnDescriptor(String)} and setters.
401    */
402   @Deprecated
403   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
404       final String compression, final boolean inMemory,
405       final boolean blockCacheEnabled, final int blocksize,
406       final int timeToLive, final String bloomFilter, final int scope) {
407     this(familyName, DEFAULT_MIN_VERSIONS, maxVersions, DEFAULT_KEEP_DELETED,
408         compression, DEFAULT_ENCODE_ON_DISK, DEFAULT_DATA_BLOCK_ENCODING,
409         inMemory, blockCacheEnabled, blocksize, timeToLive, bloomFilter,
410         scope);
411   }
412 
413   /**
414    * Constructor
415    * @param familyName Column family name. Must be 'printable' -- digit or
416    * letter -- and may not contain a <code>:</code>
417    * @param minVersions Minimum number of versions to keep
418    * @param maxVersions Maximum number of versions to keep
419    * @param keepDeletedCells Whether to retain deleted cells until they expire
420    *        up to maxVersions versions.
421    * @param compression Compression type
422    * @param encodeOnDisk whether to use the specified data block encoding
423    *        on disk. If false, the encoding will be used in cache only.
424    * @param dataBlockEncoding data block encoding
425    * @param inMemory If true, column data should be kept in an HRegionServer's
426    * cache
427    * @param blockCacheEnabled If true, MapFile blocks should be cached
428    * @param blocksize Block size to use when writing out storefiles.  Use
429    * smaller blocksizes for faster random-access at expense of larger indices
430    * (more memory consumption).  Default is usually 64k.
431    * @param timeToLive Time-to-live of cell contents, in seconds
432    * (use HConstants.FOREVER for unlimited TTL)
433    * @param bloomFilter Bloom filter type for this column
434    * @param scope The scope tag for this column
435    *
436    * @throws IllegalArgumentException if passed a family name that is made of
437    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
438    * a <code>:</code>
439    * @throws IllegalArgumentException if the number of versions is &lt;= 0
440    * @deprecated As of release 0.96
441    *             (<a href="https://issues.apache.org/jira/browse/HBASE-">HBASE-</a>).
442    *             This will be removed in HBase 2.0.0.
443    *             Use {@link #HColumnDescriptor(String)} and setters.
444    */
445   @Deprecated
446   public HColumnDescriptor(final byte[] familyName, final int minVersions,
447       final int maxVersions, final KeepDeletedCells keepDeletedCells,
448       final String compression, final boolean encodeOnDisk,
449       final String dataBlockEncoding, final boolean inMemory,
450       final boolean blockCacheEnabled, final int blocksize,
451       final int timeToLive, final String bloomFilter, final int scope) {
452     isLegalFamilyName(familyName);
453     this.name = familyName;
454 
455     if (maxVersions <= 0) {
456       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
457       // Until there is support, consider 0 or < 0 -- a configuration error.
458       throw new IllegalArgumentException("Maximum versions must be positive");
459     }
460 
461     if (minVersions > 0) {
462       if (timeToLive == HConstants.FOREVER) {
463         throw new IllegalArgumentException("Minimum versions requires TTL.");
464       }
465       if (minVersions >= maxVersions) {
466         throw new IllegalArgumentException("Minimum versions must be < "
467             + "maximum versions.");
468       }
469     }
470 
471     setMaxVersions(maxVersions);
472     setMinVersions(minVersions);
473     setKeepDeletedCells(keepDeletedCells);
474     setInMemory(inMemory);
475     setBlockCacheEnabled(blockCacheEnabled);
476     setTimeToLive(timeToLive);
477     setCompressionType(Compression.Algorithm.
478       valueOf(compression.toUpperCase()));
479     setDataBlockEncoding(DataBlockEncoding.
480         valueOf(dataBlockEncoding.toUpperCase()));
481     setBloomFilterType(BloomType.
482       valueOf(bloomFilter.toUpperCase()));
483     setBlocksize(blocksize);
484     setScope(scope);
485   }
486 
487   /**
488    * @param b Family name.
489    * @return <code>b</code>
490    * @throws IllegalArgumentException If not null and not a legitimate family
491    * name: i.e. 'printable' and ends in a ':' (Null passes are allowed because
492    * <code>b</code> can be null when deserializing).  Cannot start with a '.'
493    * either. Also Family can not be an empty value or equal "recovered.edits".
494    */
495   public static byte [] isLegalFamilyName(final byte [] b) {
496     if (b == null) {
497       return b;
498     }
499     Preconditions.checkArgument(b.length != 0, "Family name can not be empty");
500     if (b[0] == '.') {
501       throw new IllegalArgumentException("Family names cannot start with a " +
502         "period: " + Bytes.toString(b));
503     }
504     for (int i = 0; i < b.length; i++) {
505       if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
506         throw new IllegalArgumentException("Illegal character <" + b[i] +
507           ">. Family names cannot contain control characters or colons: " +
508           Bytes.toString(b));
509       }
510     }
511     byte[] recoveredEdit = Bytes.toBytes(HConstants.RECOVERED_EDITS_DIR);
512     if (Bytes.equals(recoveredEdit, b)) {
513       throw new IllegalArgumentException("Family name cannot be: " +
514           HConstants.RECOVERED_EDITS_DIR);
515     }
516     return b;
517   }
518 
519   /**
520    * @return Name of this column family
521    */
522   public byte [] getName() {
523     return name;
524   }
525 
526   /**
527    * @return Name of this column family
528    */
529   public String getNameAsString() {
530     return Bytes.toString(this.name);
531   }
532 
533   /**
534    * @param key The key.
535    * @return The value.
536    */
537   public byte[] getValue(byte[] key) {
538     Bytes ibw = values.get(new Bytes(key));
539     if (ibw == null)
540       return null;
541     return ibw.get();
542   }
543 
544   /**
545    * @param key The key.
546    * @return The value as a string.
547    */
548   public String getValue(String key) {
549     byte[] value = getValue(Bytes.toBytes(key));
550     if (value == null)
551       return null;
552     return Bytes.toString(value);
553   }
554 
555   /**
556    * @return All values.
557    */
558   public Map<Bytes, Bytes> getValues() {
559     // shallow pointer copy
560     return Collections.unmodifiableMap(values);
561   }
562 
563   /**
564    * @param key The key.
565    * @param value The value.
566    * @return this (for chained invocation)
567    */
568   public HColumnDescriptor setValue(byte[] key, byte[] value) {
569     values.put(new Bytes(key),
570         new Bytes(value));
571     return this;
572   }
573 
574   /**
575    * @param key Key whose key and value we're to remove from HCD parameters.
576    */
577   public void remove(final byte [] key) {
578     values.remove(new Bytes(key));
579   }
580 
581   /**
582    * @param key The key.
583    * @param value The value.
584    * @return this (for chained invocation)
585    */
586   public HColumnDescriptor setValue(String key, String value) {
587     if (value == null) {
588       remove(Bytes.toBytes(key));
589     } else {
590       setValue(Bytes.toBytes(key), Bytes.toBytes(value));
591     }
592     return this;
593   }
594 
595   /** @return compression type being used for the column family */
596   public Compression.Algorithm getCompression() {
597     String n = getValue(COMPRESSION);
598     if (n == null) {
599       return Compression.Algorithm.NONE;
600     }
601     return Compression.Algorithm.valueOf(n.toUpperCase());
602   }
603 
604   /** @return compression type being used for the column family for major
605       compression */
606   public Compression.Algorithm getCompactionCompression() {
607     String n = getValue(COMPRESSION_COMPACT);
608     if (n == null) {
609       return getCompression();
610     }
611     return Compression.Algorithm.valueOf(n.toUpperCase());
612   }
613 
614   /** @return maximum number of versions */
615   public int getMaxVersions() {
616     if (this.cachedMaxVersions == UNINITIALIZED) {
617       String v = getValue(HConstants.VERSIONS);
618       this.cachedMaxVersions = Integer.parseInt(v);
619     }
620     return this.cachedMaxVersions;
621   }
622 
623   /**
624    * @param maxVersions maximum number of versions
625    * @return this (for chained invocation)
626    */
627   public HColumnDescriptor setMaxVersions(int maxVersions) {
628     if (maxVersions <= 0) {
629       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
630       // Until there is support, consider 0 or < 0 -- a configuration error.
631       throw new IllegalArgumentException("Maximum versions must be positive");
632     }
633     if (maxVersions < this.getMinVersions()) {
634         throw new IllegalArgumentException("Set MaxVersion to " + maxVersions
635             + " while minVersion is " + this.getMinVersions()
636             + ". Maximum versions must be >= minimum versions ");
637     }
638     setValue(HConstants.VERSIONS, Integer.toString(maxVersions));
639     cachedMaxVersions = maxVersions;
640     return this;
641   }
642 
643   /**
644    * @return The storefile/hfile blocksize for this column family.
645    */
646   public synchronized int getBlocksize() {
647     if (this.blocksize == null) {
648       String value = getValue(BLOCKSIZE);
649       this.blocksize = (value != null)?
650         Integer.decode(value): Integer.valueOf(DEFAULT_BLOCKSIZE);
651     }
652     return this.blocksize.intValue();
653 
654   }
655 
656   /**
657    * @param s Blocksize to use when writing out storefiles/hfiles on this
658    * column family.
659    * @return this (for chained invocation)
660    */
661   public HColumnDescriptor setBlocksize(int s) {
662     setValue(BLOCKSIZE, Integer.toString(s));
663     this.blocksize = null;
664     return this;
665   }
666 
667   /**
668    * @return Compression type setting.
669    */
670   public Compression.Algorithm getCompressionType() {
671     return getCompression();
672   }
673 
674   /**
675    * Compression types supported in hbase.
676    * LZO is not bundled as part of the hbase distribution.
677    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
678    * for how to enable it.
679    * @param type Compression type setting.
680    * @return this (for chained invocation)
681    */
682   public HColumnDescriptor setCompressionType(Compression.Algorithm type) {
683     return setValue(COMPRESSION, type.getName().toUpperCase());
684   }
685 
686   /**
687    * @return data block encoding algorithm used on disk
688    * @deprecated As of release 0.98
689    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9870">HBASE-9870</a>).
690    *             This will be removed in HBase 2.0.0. See {@link #getDataBlockEncoding()}}
691    */
692   @Deprecated
693   public DataBlockEncoding getDataBlockEncodingOnDisk() {
694     return getDataBlockEncoding();
695   }
696 
697   /**
698    * This method does nothing now. Flag ENCODE_ON_DISK is not used
699    * any more. Data blocks have the same encoding in cache as on disk.
700    * @return this (for chained invocation)
701    * @deprecated As of release 0.98
702    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9870">HBASE-9870</a>).
703    *             This will be removed in HBase 2.0.0. This method does nothing now.
704    */
705   @Deprecated
706   public HColumnDescriptor setEncodeOnDisk(boolean encodeOnDisk) {
707     return this;
708   }
709 
710   /**
711    * @return the data block encoding algorithm used in block cache and
712    *         optionally on disk
713    */
714   public DataBlockEncoding getDataBlockEncoding() {
715     String type = getValue(DATA_BLOCK_ENCODING);
716     if (type == null) {
717       type = DEFAULT_DATA_BLOCK_ENCODING;
718     }
719     return DataBlockEncoding.valueOf(type);
720   }
721 
722   /**
723    * Set data block encoding algorithm used in block cache.
724    * @param type What kind of data block encoding will be used.
725    * @return this (for chained invocation)
726    */
727   public HColumnDescriptor setDataBlockEncoding(DataBlockEncoding type) {
728     String name;
729     if (type != null) {
730       name = type.toString();
731     } else {
732       name = DataBlockEncoding.NONE.toString();
733     }
734     return setValue(DATA_BLOCK_ENCODING, name);
735   }
736 
737   /**
738    * Set whether the tags should be compressed along with DataBlockEncoding. When no
739    * DataBlockEncoding is been used, this is having no effect.
740    *
741    * @param compressTags
742    * @return this (for chained invocation)
743    */
744   public HColumnDescriptor setCompressTags(boolean compressTags) {
745     return setValue(COMPRESS_TAGS, String.valueOf(compressTags));
746   }
747 
748   /**
749    * @return Whether KV tags should be compressed along with DataBlockEncoding. When no
750    *         DataBlockEncoding is been used, this is having no effect.
751    * @deprecated As of release 1.0.0
752    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
753    *             This will be removed in HBase 2.0.0. Use {@link #isCompressTags()} instead.
754    */
755   @Deprecated
756   public boolean shouldCompressTags() {
757     String compressTagsStr = getValue(COMPRESS_TAGS);
758     boolean compressTags = DEFAULT_COMPRESS_TAGS;
759     if (compressTagsStr != null) {
760       compressTags = Boolean.parseBoolean(compressTagsStr);
761     }
762     return compressTags;
763   }
764 
765   /**
766    * @return Whether KV tags should be compressed along with DataBlockEncoding. When no
767    *         DataBlockEncoding is been used, this is having no effect.
768    */
769   public boolean isCompressTags() {
770     String compressTagsStr = getValue(COMPRESS_TAGS);
771     boolean compressTags = DEFAULT_COMPRESS_TAGS;
772     if (compressTagsStr != null) {
773       compressTags = Boolean.parseBoolean(compressTagsStr);
774     }
775     return compressTags;
776   }
777 
778   /**
779    * @return Compression type setting.
780    */
781   public Compression.Algorithm getCompactionCompressionType() {
782     return getCompactionCompression();
783   }
784 
785   /**
786    * Compression types supported in hbase.
787    * LZO is not bundled as part of the hbase distribution.
788    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
789    * for how to enable it.
790    * @param type Compression type setting.
791    * @return this (for chained invocation)
792    */
793   public HColumnDescriptor setCompactionCompressionType(
794       Compression.Algorithm type) {
795     return setValue(COMPRESSION_COMPACT, type.getName().toUpperCase());
796   }
797 
798   /**
799    * @return True if we are to favor keeping all values for this column family in the
800    * HRegionServer cache.
801    */
802   public boolean isInMemory() {
803     String value = getValue(HConstants.IN_MEMORY);
804     if (value != null) {
805       return Boolean.parseBoolean(value);
806     }
807     return DEFAULT_IN_MEMORY;
808   }
809 
810   /**
811    * @param inMemory True if we are to favor keeping all values for this column family in the
812    * HRegionServer cache
813    * @return this (for chained invocation)
814    */
815   public HColumnDescriptor setInMemory(boolean inMemory) {
816     return setValue(HConstants.IN_MEMORY, Boolean.toString(inMemory));
817   }
818 
819   public KeepDeletedCells getKeepDeletedCells() {
820     String value = getValue(KEEP_DELETED_CELLS);
821     if (value != null) {
822       // toUpperCase for backwards compatibility
823       return KeepDeletedCells.valueOf(value.toUpperCase());
824     }
825     return DEFAULT_KEEP_DELETED;
826   }
827 
828   /**
829    * @param keepDeletedCells True if deleted rows should not be collected
830    * immediately.
831    * @return this (for chained invocation)
832    * @deprecated As of release 1.0.0
833    *             (<a href="https://issues.apache.org/jira/browse/HBASE-12363">HBASE-12363</a>).
834    *             This will be removed in HBase 2.0.0.
835    *             Use {@link #setKeepDeletedCells(KeepDeletedCells)}.
836    */
837   @Deprecated
838   public HColumnDescriptor setKeepDeletedCells(boolean keepDeletedCells) {
839     return setValue(KEEP_DELETED_CELLS, (keepDeletedCells ? KeepDeletedCells.TRUE
840         : KeepDeletedCells.FALSE).toString());
841   }
842 
843   /**
844    * @param keepDeletedCells True if deleted rows should not be collected
845    * immediately.
846    * @return this (for chained invocation)
847    */
848   public HColumnDescriptor setKeepDeletedCells(KeepDeletedCells keepDeletedCells) {
849     return setValue(KEEP_DELETED_CELLS, keepDeletedCells.toString());
850   }
851 
852   /**
853    * @return Time-to-live of cell contents, in seconds.
854    */
855   public int getTimeToLive() {
856     String value = getValue(TTL);
857     return (value != null)? Integer.parseInt(value) : DEFAULT_TTL;
858   }
859 
860   /**
861    * @param timeToLive Time-to-live of cell contents, in seconds.
862    * @return this (for chained invocation)
863    */
864   public HColumnDescriptor setTimeToLive(int timeToLive) {
865     return setValue(TTL, Integer.toString(timeToLive));
866   }
867 
868   /**
869    * @return The minimum number of versions to keep.
870    */
871   public int getMinVersions() {
872     String value = getValue(MIN_VERSIONS);
873     return (value != null)? Integer.parseInt(value) : 0;
874   }
875 
876   /**
877    * @param minVersions The minimum number of versions to keep.
878    * (used when timeToLive is set)
879    * @return this (for chained invocation)
880    */
881   public HColumnDescriptor setMinVersions(int minVersions) {
882     return setValue(MIN_VERSIONS, Integer.toString(minVersions));
883   }
884 
885   /**
886    * @return True if hfile DATA type blocks should be cached (You cannot disable caching of INDEX
887    * and BLOOM type blocks).
888    */
889   public boolean isBlockCacheEnabled() {
890     String value = getValue(BLOCKCACHE);
891     if (value != null) {
892       return Boolean.parseBoolean(value);
893     }
894     return DEFAULT_BLOCKCACHE;
895   }
896 
897   /**
898    * @param blockCacheEnabled True if hfile DATA type blocks should be cached (We always cache
899    * INDEX and BLOOM blocks; you cannot turn this off).
900    * @return this (for chained invocation)
901    */
902   public HColumnDescriptor setBlockCacheEnabled(boolean blockCacheEnabled) {
903     return setValue(BLOCKCACHE, Boolean.toString(blockCacheEnabled));
904   }
905 
906   /**
907    * @return bloom filter type used for new StoreFiles in ColumnFamily
908    */
909   public BloomType getBloomFilterType() {
910     String n = getValue(BLOOMFILTER);
911     if (n == null) {
912       n = DEFAULT_BLOOMFILTER;
913     }
914     return BloomType.valueOf(n.toUpperCase());
915   }
916 
917   /**
918    * @param bt bloom filter type
919    * @return this (for chained invocation)
920    */
921   public HColumnDescriptor setBloomFilterType(final BloomType bt) {
922     return setValue(BLOOMFILTER, bt.toString());
923   }
924 
925    /**
926     * @return the scope tag
927     */
928   public int getScope() {
929     byte[] value = getValue(REPLICATION_SCOPE_BYTES);
930     if (value != null) {
931       return Integer.parseInt(Bytes.toString(value));
932     }
933     return DEFAULT_REPLICATION_SCOPE;
934   }
935 
936  /**
937   * @param scope the scope tag
938   * @return this (for chained invocation)
939   */
940   public HColumnDescriptor setScope(int scope) {
941     return setValue(REPLICATION_SCOPE, Integer.toString(scope));
942   }
943 
944   /**
945    * @return true if we should cache data blocks on write
946    * @deprecated As of release 1.0.0
947    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
948    *             This will be removed in HBase 2.0.0. Use {@link #isCacheDataOnWrite()}} instead.
949    */
950   @Deprecated
951   public boolean shouldCacheDataOnWrite() {
952     return setAndGetBoolean(CACHE_DATA_ON_WRITE, DEFAULT_CACHE_DATA_ON_WRITE);
953   }
954 
955   /**
956    * @return true if we should cache data blocks on write
957    */
958   public boolean isCacheDataOnWrite() {
959     return setAndGetBoolean(CACHE_DATA_ON_WRITE, DEFAULT_CACHE_DATA_ON_WRITE);
960   }
961 
962   /**
963    * @param value true if we should cache data blocks on write
964    * @return this (for chained invocation)
965    */
966   public HColumnDescriptor setCacheDataOnWrite(boolean value) {
967     return setValue(CACHE_DATA_ON_WRITE, Boolean.toString(value));
968   }
969 
970   /**
971    * @return true if we should cache data blocks in the L1 cache (if block cache deploy
972    * has more than one tier; e.g. we are using CombinedBlockCache).
973    * @deprecated As of release 1.0.0
974    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
975    *             This will be removed in HBase 2.0.0. Use {@link #isCacheDataInL1()}} instead.
976    */
977   @Deprecated
978   public boolean shouldCacheDataInL1() {
979     return setAndGetBoolean(CACHE_DATA_IN_L1, DEFAULT_CACHE_DATA_IN_L1);
980   }
981 
982   /**
983    * @return true if we should cache data blocks in the L1 cache (if block cache deploy has more
984    *         than one tier; e.g. we are using CombinedBlockCache).
985    */
986   public boolean isCacheDataInL1() {
987     return setAndGetBoolean(CACHE_DATA_IN_L1, DEFAULT_CACHE_DATA_IN_L1);
988   }
989 
990   /**
991    * @param value true if we should cache data blocks in the L1 cache (if block cache deploy
992    * has more than one tier; e.g. we are using CombinedBlockCache).
993    * @return this (for chained invocation)
994    */
995   public HColumnDescriptor setCacheDataInL1(boolean value) {
996     return setValue(CACHE_DATA_IN_L1, Boolean.toString(value));
997   }
998 
999   private boolean setAndGetBoolean(final String key, final boolean defaultSetting) {
1000     String value = getValue(key);
1001     if (value != null) {
1002       return Boolean.parseBoolean(value);
1003     }
1004     return defaultSetting;
1005   }
1006 
1007   /**
1008    * @return true if we should cache index blocks on write
1009    * @deprecated As of release 1.0.0
1010    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1011    *             This will be removed in HBase 2.0.0.
1012    *             Use {@link #isCacheIndexesOnWrite()} instead.
1013    */
1014   @Deprecated
1015   public boolean shouldCacheIndexesOnWrite() {
1016     return setAndGetBoolean(CACHE_INDEX_ON_WRITE, DEFAULT_CACHE_INDEX_ON_WRITE);
1017   }
1018 
1019   /**
1020    * @return true if we should cache index blocks on write
1021    */
1022   public boolean isCacheIndexesOnWrite() {
1023     return setAndGetBoolean(CACHE_INDEX_ON_WRITE, DEFAULT_CACHE_INDEX_ON_WRITE);
1024   }
1025 
1026   /**
1027    * @param value true if we should cache index blocks on write
1028    * @return this (for chained invocation)
1029    */
1030   public HColumnDescriptor setCacheIndexesOnWrite(boolean value) {
1031     return setValue(CACHE_INDEX_ON_WRITE, Boolean.toString(value));
1032   }
1033 
1034   /**
1035    * @return true if we should cache bloomfilter blocks on write
1036    * @deprecated As of release 1.0.0
1037    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1038    *             This will be removed in HBase 2.0.0.
1039    *             Use {@link #isCacheBloomsOnWrite()}} instead.
1040    */
1041   @Deprecated
1042   public boolean shouldCacheBloomsOnWrite() {
1043     return setAndGetBoolean(CACHE_BLOOMS_ON_WRITE, DEFAULT_CACHE_BLOOMS_ON_WRITE);
1044   }
1045 
1046   /**
1047    * @return true if we should cache bloomfilter blocks on write
1048    */
1049   public boolean isCacheBloomsOnWrite() {
1050     return setAndGetBoolean(CACHE_BLOOMS_ON_WRITE, DEFAULT_CACHE_BLOOMS_ON_WRITE);
1051   }
1052 
1053   /**
1054    * @param value true if we should cache bloomfilter blocks on write
1055    * @return this (for chained invocation)
1056    */
1057   public HColumnDescriptor setCacheBloomsOnWrite(boolean value) {
1058     return setValue(CACHE_BLOOMS_ON_WRITE, Boolean.toString(value));
1059   }
1060 
1061   /**
1062    * @return true if we should evict cached blocks from the blockcache on
1063    * close
1064    * @deprecated As of release 1.0.0
1065    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1066    *             This will be removed in HBase 2.0.0.
1067    *             Use {@link #isEvictBlocksOnClose()}} instead.
1068    */
1069   @Deprecated
1070   public boolean shouldEvictBlocksOnClose() {
1071     return setAndGetBoolean(EVICT_BLOCKS_ON_CLOSE, DEFAULT_EVICT_BLOCKS_ON_CLOSE);
1072   }
1073 
1074   /**
1075    * @return true if we should evict cached blocks from the blockcache on close
1076    */
1077   public boolean isEvictBlocksOnClose() {
1078     return setAndGetBoolean(EVICT_BLOCKS_ON_CLOSE, DEFAULT_EVICT_BLOCKS_ON_CLOSE);
1079   }
1080 
1081   /**
1082    * @param value true if we should evict cached blocks from the blockcache on
1083    * close
1084    * @return this (for chained invocation)
1085    */
1086   public HColumnDescriptor setEvictBlocksOnClose(boolean value) {
1087     return setValue(EVICT_BLOCKS_ON_CLOSE, Boolean.toString(value));
1088   }
1089 
1090   /**
1091    * @return true if we should prefetch blocks into the blockcache on open
1092    * @deprecated As of release 1.0.0
1093    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1094    *             This will be removed in HBase 2.0.0.
1095    *             Use {@link #isPrefetchBlocksOnOpen()}}} instead.
1096    */
1097   @Deprecated
1098   public boolean shouldPrefetchBlocksOnOpen() {
1099     return setAndGetBoolean(PREFETCH_BLOCKS_ON_OPEN, DEFAULT_PREFETCH_BLOCKS_ON_OPEN);
1100   }
1101 
1102   /**
1103    * @return true if we should prefetch blocks into the blockcache on open
1104    */
1105   public boolean isPrefetchBlocksOnOpen() {
1106     return setAndGetBoolean(PREFETCH_BLOCKS_ON_OPEN, DEFAULT_PREFETCH_BLOCKS_ON_OPEN);
1107   }
1108 
1109   /**
1110    * @param value true if we should prefetch blocks into the blockcache on open
1111    * @return this (for chained invocation)
1112    */
1113   public HColumnDescriptor setPrefetchBlocksOnOpen(boolean value) {
1114     return setValue(PREFETCH_BLOCKS_ON_OPEN, Boolean.toString(value));
1115   }
1116 
1117   /**
1118    * @see java.lang.Object#toString()
1119    */
1120   @Override
1121   public String toString() {
1122     StringBuilder s = new StringBuilder();
1123 
1124     s.append('{');
1125     s.append(HConstants.NAME);
1126     s.append(" => '");
1127     s.append(Bytes.toString(name));
1128     s.append("'");
1129     s.append(getValues(true));
1130     s.append('}');
1131     return s.toString();
1132   }
1133 
1134   /**
1135    * @return Column family descriptor with only the customized attributes.
1136    */
1137   public String toStringCustomizedValues() {
1138     StringBuilder s = new StringBuilder();
1139     s.append('{');
1140     s.append(HConstants.NAME);
1141     s.append(" => '");
1142     s.append(Bytes.toString(name));
1143     s.append("'");
1144     s.append(getValues(false));
1145     s.append('}');
1146     return s.toString();
1147   }
1148 
1149   private StringBuilder getValues(boolean printDefaults) {
1150     StringBuilder s = new StringBuilder();
1151 
1152     boolean hasConfigKeys = false;
1153 
1154     // print all reserved keys first
1155     for (Bytes k : values.keySet()) {
1156       if (!RESERVED_KEYWORDS.contains(k)) {
1157         hasConfigKeys = true;
1158         continue;
1159       }
1160       String key = Bytes.toString(k.get());
1161       String value = Bytes.toStringBinary(values.get(k).get());
1162       if (printDefaults
1163           || !DEFAULT_VALUES.containsKey(key)
1164           || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
1165         s.append(", ");
1166         s.append(key);
1167         s.append(" => ");
1168         s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
1169       }
1170     }
1171 
1172     // print all non-reserved, advanced config keys as a separate subset
1173     if (hasConfigKeys) {
1174       s.append(", ");
1175       s.append(HConstants.METADATA).append(" => ");
1176       s.append('{');
1177       boolean printComma = false;
1178       for (Bytes k : values.keySet()) {
1179         if (RESERVED_KEYWORDS.contains(k)) {
1180           continue;
1181         }
1182         String key = Bytes.toString(k.get());
1183         String value = Bytes.toStringBinary(values.get(k).get());
1184         if (printComma) {
1185           s.append(", ");
1186         }
1187         printComma = true;
1188         s.append('\'').append(key).append('\'');
1189         s.append(" => ");
1190         s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
1191       }
1192       s.append('}');
1193     }
1194 
1195     if (!configuration.isEmpty()) {
1196       s.append(", ");
1197       s.append(HConstants.CONFIGURATION).append(" => ");
1198       s.append('{');
1199       boolean printCommaForConfiguration = false;
1200       for (Map.Entry<String, String> e : configuration.entrySet()) {
1201         if (printCommaForConfiguration) s.append(", ");
1202         printCommaForConfiguration = true;
1203         s.append('\'').append(e.getKey()).append('\'');
1204         s.append(" => ");
1205         s.append('\'').append(PrettyPrinter.format(e.getValue(), getUnit(e.getKey()))).append('\'');
1206       }
1207       s.append("}");
1208     }
1209     return s;
1210   }
1211 
1212   public static Unit getUnit(String key) {
1213     Unit unit;
1214       /* TTL for now, we can add more as we neeed */
1215     if (key.equals(HColumnDescriptor.TTL)) {
1216       unit = Unit.TIME_INTERVAL;
1217     } else {
1218       unit = Unit.NONE;
1219     }
1220     return unit;
1221   }
1222 
1223   public static Map<String, String> getDefaultValues() {
1224     return Collections.unmodifiableMap(DEFAULT_VALUES);
1225   }
1226 
1227   /**
1228    * @see java.lang.Object#equals(java.lang.Object)
1229    */
1230   @Override
1231   public boolean equals(Object obj) {
1232     if (this == obj) {
1233       return true;
1234     }
1235     if (obj == null) {
1236       return false;
1237     }
1238     if (!(obj instanceof HColumnDescriptor)) {
1239       return false;
1240     }
1241     return compareTo((HColumnDescriptor)obj) == 0;
1242   }
1243 
1244   /**
1245    * @see java.lang.Object#hashCode()
1246    */
1247   @Override
1248   public int hashCode() {
1249     int result = Bytes.hashCode(this.name);
1250     result ^= (int) COLUMN_DESCRIPTOR_VERSION;
1251     result ^= values.hashCode();
1252     result ^= configuration.hashCode();
1253     return result;
1254   }
1255 
1256   // Comparable
1257   @Override
1258   public int compareTo(HColumnDescriptor o) {
1259     int result = Bytes.compareTo(this.name, o.getName());
1260     if (result == 0) {
1261       // punt on comparison for ordering, just calculate difference
1262       result = this.values.hashCode() - o.values.hashCode();
1263       if (result < 0)
1264         result = -1;
1265       else if (result > 0)
1266         result = 1;
1267     }
1268     if (result == 0) {
1269       result = this.configuration.hashCode() - o.configuration.hashCode();
1270       if (result < 0)
1271         result = -1;
1272       else if (result > 0)
1273         result = 1;
1274     }
1275     return result;
1276   }
1277 
1278   /**
1279    * @return This instance serialized with pb with pb magic prefix
1280    * @see #parseFrom(byte[])
1281    */
1282   public byte [] toByteArray() {
1283     return ProtobufUtil.prependPBMagic(convert().toByteArray());
1284   }
1285 
1286   /**
1287    * @param bytes A pb serialized {@link HColumnDescriptor} instance with pb magic prefix
1288    * @return An instance of {@link HColumnDescriptor} made from <code>bytes</code>
1289    * @throws DeserializationException
1290    * @see #toByteArray()
1291    */
1292   public static HColumnDescriptor parseFrom(final byte [] bytes) throws DeserializationException {
1293     if (!ProtobufUtil.isPBMagicPrefix(bytes)) throw new DeserializationException("No magic");
1294     int pblen = ProtobufUtil.lengthOfPBMagic();
1295     ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1296     ColumnFamilySchema cfs = null;
1297     try {
1298       cfs = builder.mergeFrom(bytes, pblen, bytes.length - pblen).build();
1299     } catch (InvalidProtocolBufferException e) {
1300       throw new DeserializationException(e);
1301     }
1302     return convert(cfs);
1303   }
1304 
1305   /**
1306    * @param cfs
1307    * @return An {@link HColumnDescriptor} made from the passed in <code>cfs</code>
1308    */
1309   public static HColumnDescriptor convert(final ColumnFamilySchema cfs) {
1310     // Use the empty constructor so we preserve the initial values set on construction for things
1311     // like maxVersion.  Otherwise, we pick up wrong values on deserialization which makes for
1312     // unrelated-looking test failures that are hard to trace back to here.
1313     HColumnDescriptor hcd = new HColumnDescriptor();
1314     hcd.name = cfs.getName().toByteArray();
1315     for (BytesBytesPair a: cfs.getAttributesList()) {
1316       hcd.setValue(a.getFirst().toByteArray(), a.getSecond().toByteArray());
1317     }
1318     for (NameStringPair a: cfs.getConfigurationList()) {
1319       hcd.setConfiguration(a.getName(), a.getValue());
1320     }
1321     return hcd;
1322   }
1323 
1324   /**
1325    * @return Convert this instance to a the pb column family type
1326    */
1327   public ColumnFamilySchema convert() {
1328     ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1329     builder.setName(ByteStringer.wrap(getName()));
1330     for (Map.Entry<Bytes, Bytes> e : this.values.entrySet()) {
1331       BytesBytesPair.Builder aBuilder = BytesBytesPair.newBuilder();
1332       aBuilder.setFirst(ByteStringer.wrap(e.getKey().get()));
1333       aBuilder.setSecond(ByteStringer.wrap(e.getValue().get()));
1334       builder.addAttributes(aBuilder.build());
1335     }
1336     for (Map.Entry<String, String> e : this.configuration.entrySet()) {
1337       NameStringPair.Builder aBuilder = NameStringPair.newBuilder();
1338       aBuilder.setName(e.getKey());
1339       aBuilder.setValue(e.getValue());
1340       builder.addConfiguration(aBuilder.build());
1341     }
1342     return builder.build();
1343   }
1344 
1345   /**
1346    * Getter for accessing the configuration value by key.
1347    */
1348   public String getConfigurationValue(String key) {
1349     return configuration.get(key);
1350   }
1351 
1352   /**
1353    * Getter for fetching an unmodifiable {@link #configuration} map.
1354    */
1355   public Map<String, String> getConfiguration() {
1356     // shallow pointer copy
1357     return Collections.unmodifiableMap(configuration);
1358   }
1359 
1360   /**
1361    * Setter for storing a configuration setting in {@link #configuration} map.
1362    * @param key Config key. Same as XML config key e.g. hbase.something.or.other.
1363    * @param value String value. If null, removes the configuration.
1364    */
1365   public HColumnDescriptor setConfiguration(String key, String value) {
1366     if (value == null) {
1367       removeConfiguration(key);
1368     } else {
1369       configuration.put(key, value);
1370     }
1371     return this;
1372   }
1373 
1374   /**
1375    * Remove a configuration setting represented by the key from the {@link #configuration} map.
1376    */
1377   public void removeConfiguration(final String key) {
1378     configuration.remove(key);
1379   }
1380 
1381   /**
1382    * Return the encryption algorithm in use by this family
1383    */
1384   public String getEncryptionType() {
1385     return getValue(ENCRYPTION);
1386   }
1387 
1388   /**
1389    * Set the encryption algorithm for use with this family
1390    * @param algorithm
1391    */
1392   public HColumnDescriptor setEncryptionType(String algorithm) {
1393     setValue(ENCRYPTION, algorithm);
1394     return this;
1395   }
1396 
1397   /** Return the raw crypto key attribute for the family, or null if not set  */
1398   public byte[] getEncryptionKey() {
1399     return getValue(Bytes.toBytes(ENCRYPTION_KEY));
1400   }
1401 
1402   /** Set the raw crypto key attribute for the family */
1403   public HColumnDescriptor setEncryptionKey(byte[] keyBytes) {
1404     setValue(Bytes.toBytes(ENCRYPTION_KEY), keyBytes);
1405     return this;
1406   }
1407 }