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.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.hbase.classification.InterfaceStability;
33  import org.apache.hadoop.hbase.exceptions.DeserializationException;
34  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
35  import org.apache.hadoop.hbase.io.compress.Compression;
36  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
37  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
38  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.BytesBytesPair;
39  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ColumnFamilySchema;
40  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
41  import org.apache.hadoop.hbase.protobuf.generated.WALProtos;
42  import org.apache.hadoop.hbase.regionserver.BloomType;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.apache.hadoop.hbase.util.PrettyPrinter;
45  import org.apache.hadoop.hbase.util.PrettyPrinter.Unit;
46  import org.apache.hadoop.io.Text;
47  import org.apache.hadoop.io.WritableComparable;
48  
49  import com.google.common.base.Preconditions;
50  import org.apache.hadoop.hbase.util.ByteStringer;
51  
52  /**
53   * An HColumnDescriptor contains information about a column family such as the
54   * number of versions, compression settings, etc.
55   *
56   * It is used as input when creating a table or adding a column.
57   */
58  @InterfaceAudience.Public
59  @InterfaceStability.Evolving
60  public class HColumnDescriptor implements WritableComparable<HColumnDescriptor> {
61    // For future backward compatibility
62  
63    // Version  3 was when column names become byte arrays and when we picked up
64    // Time-to-live feature.  Version 4 was when we moved to byte arrays, HBASE-82.
65    // Version  5 was when bloom filter descriptors were removed.
66    // Version  6 adds metadata as a map where keys and values are byte[].
67    // Version  7 -- add new compression and hfile blocksize to HColumnDescriptor (HBASE-1217)
68    // Version  8 -- reintroduction of bloom filters, changed from boolean to enum
69    // Version  9 -- add data block encoding
70    // Version 10 -- change metadata to standard type.
71    // Version 11 -- add column family level configuration.
72    private static final byte COLUMN_DESCRIPTOR_VERSION = (byte) 11;
73  
74    // These constants are used as FileInfo keys
75    public static final String COMPRESSION = "COMPRESSION";
76    public static final String COMPRESSION_COMPACT = "COMPRESSION_COMPACT";
77    public static final String ENCODE_ON_DISK = // To be removed, it is not used anymore
78        "ENCODE_ON_DISK";
79    public static final String DATA_BLOCK_ENCODING =
80        "DATA_BLOCK_ENCODING";
81    /**
82     * Key for the BLOCKCACHE attribute.
83     * A more exact name would be CACHE_DATA_ON_READ because this flag sets whether or not we
84     * cache DATA blocks.  We always cache INDEX and BLOOM blocks; caching these blocks cannot be
85     * disabled.
86     */
87    public static final String BLOCKCACHE = "BLOCKCACHE";
88    public static final String CACHE_DATA_ON_WRITE = "CACHE_DATA_ON_WRITE";
89    public static final String CACHE_INDEX_ON_WRITE = "CACHE_INDEX_ON_WRITE";
90    public static final String CACHE_BLOOMS_ON_WRITE = "CACHE_BLOOMS_ON_WRITE";
91    public static final String EVICT_BLOCKS_ON_CLOSE = "EVICT_BLOCKS_ON_CLOSE";
92    /**
93     * Key for cache data into L1 if cache is set up with more than one tier.
94     * To set in the shell, do something like this:
95     * <code>hbase(main):003:0&gt; create 't',
96     *    {NAME =&gt; 't', CONFIGURATION =&gt; {CACHE_DATA_IN_L1 =&gt; 'true'}}</code>
97     */
98    public static final String CACHE_DATA_IN_L1 = "CACHE_DATA_IN_L1";
99  
100   /**
101    * Key for the PREFETCH_BLOCKS_ON_OPEN attribute.
102    * If set, all INDEX, BLOOM, and DATA blocks of HFiles belonging to this
103    * family will be loaded into the cache as soon as the file is opened. These
104    * loads will not count as cache misses.
105    */
106   public static final String PREFETCH_BLOCKS_ON_OPEN = "PREFETCH_BLOCKS_ON_OPEN";
107 
108   /**
109    * Size of storefile/hfile 'blocks'.  Default is {@link #DEFAULT_BLOCKSIZE}.
110    * Use smaller block sizes for faster random-access at expense of larger
111    * indices (more memory consumption).
112    */
113   public static final String BLOCKSIZE = "BLOCKSIZE";
114 
115   public static final String LENGTH = "LENGTH";
116   public static final String TTL = "TTL";
117   public static final String BLOOMFILTER = "BLOOMFILTER";
118   public static final String FOREVER = "FOREVER";
119   public static final String REPLICATION_SCOPE = "REPLICATION_SCOPE";
120   public static final byte[] REPLICATION_SCOPE_BYTES = Bytes.toBytes(REPLICATION_SCOPE);
121   public static final String MIN_VERSIONS = "MIN_VERSIONS";
122   public static final String KEEP_DELETED_CELLS = "KEEP_DELETED_CELLS";
123   public static final String COMPRESS_TAGS = "COMPRESS_TAGS";
124 
125   public static final String ENCRYPTION = "ENCRYPTION";
126   public static final String ENCRYPTION_KEY = "ENCRYPTION_KEY";
127 
128   public static final String DFS_REPLICATION = "DFS_REPLICATION";
129   public static final short DEFAULT_DFS_REPLICATION = 0;
130 
131   public static final String STORAGE_POLICY = "STORAGE_POLICY";
132 
133   /**
134    * Default compression type.
135    */
136   public static final String DEFAULT_COMPRESSION =
137     Compression.Algorithm.NONE.getName();
138 
139   /**
140    * Default value of the flag that enables data block encoding on disk, as
141    * opposed to encoding in cache only. We encode blocks everywhere by default,
142    * as long as {@link #DATA_BLOCK_ENCODING} is not NONE.
143    */
144   public static final boolean DEFAULT_ENCODE_ON_DISK = true;
145 
146   /** Default data block encoding algorithm. */
147   public static final String DEFAULT_DATA_BLOCK_ENCODING =
148       DataBlockEncoding.NONE.toString();
149 
150   /**
151    * Default number of versions of a record to keep.
152    */
153   public static final int DEFAULT_VERSIONS = HBaseConfiguration.create().getInt(
154     "hbase.column.max.version", 1);
155 
156   /**
157    * Default is not to keep a minimum of versions.
158    */
159   public static final int DEFAULT_MIN_VERSIONS = 0;
160 
161   /*
162    * Cache here the HCD value.
163    * Question: its OK to cache since when we're reenable, we create a new HCD?
164    */
165   private volatile Integer blocksize = null;
166 
167   /**
168    * Default setting for whether to try and serve this column family from memory or not.
169    */
170   public static final boolean DEFAULT_IN_MEMORY = false;
171 
172   /**
173    * Default setting for preventing deleted from being collected immediately.
174    */
175   public static final KeepDeletedCells DEFAULT_KEEP_DELETED = KeepDeletedCells.FALSE;
176 
177   /**
178    * Default setting for whether to use a block cache or not.
179    */
180   public static final boolean DEFAULT_BLOCKCACHE = true;
181 
182   /**
183    * Default setting for whether to cache data blocks on write if block caching
184    * is enabled.
185    */
186   public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
187 
188   /**
189    * Default setting for whether to cache data blocks in L1 tier.  Only makes sense if more than
190    * one tier in operations: i.e. if we have an L1 and a L2.  This will be the cases if we are
191    * using BucketCache.
192    */
193   public static final boolean DEFAULT_CACHE_DATA_IN_L1 = false;
194 
195   /**
196    * Default setting for whether to cache index blocks on write if block
197    * caching is enabled.
198    */
199   public static final boolean DEFAULT_CACHE_INDEX_ON_WRITE = false;
200 
201   /**
202    * Default size of blocks in files stored to the filesytem (hfiles).
203    */
204   public static final int DEFAULT_BLOCKSIZE = HConstants.DEFAULT_BLOCKSIZE;
205 
206   /**
207    * Default setting for whether or not to use bloomfilters.
208    */
209   public static final String DEFAULT_BLOOMFILTER = BloomType.ROW.toString();
210 
211   /**
212    * Default setting for whether to cache bloom filter blocks on write if block
213    * caching is enabled.
214    */
215   public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
216 
217   /**
218    * Default time to live of cell contents.
219    */
220   public static final int DEFAULT_TTL = HConstants.FOREVER;
221 
222   /**
223    * Default scope.
224    */
225   public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
226 
227   /**
228    * Default setting for whether to evict cached blocks from the blockcache on
229    * close.
230    */
231   public static final boolean DEFAULT_EVICT_BLOCKS_ON_CLOSE = false;
232 
233   /**
234    * Default compress tags along with any type of DataBlockEncoding.
235    */
236   public static final boolean DEFAULT_COMPRESS_TAGS = true;
237 
238   /*
239    * Default setting for whether to prefetch blocks into the blockcache on open.
240    */
241   public static final boolean DEFAULT_PREFETCH_BLOCKS_ON_OPEN = false;
242 
243   private final static Map<String, String> DEFAULT_VALUES
244     = new HashMap<String, String>();
245   private final static Set<ImmutableBytesWritable> RESERVED_KEYWORDS
246     = new HashSet<ImmutableBytesWritable>();
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 ImmutableBytesWritable(Bytes.toBytes(s)));
267       }
268       RESERVED_KEYWORDS.add(new ImmutableBytesWritable(Bytes.toBytes(ENCRYPTION)));
269       RESERVED_KEYWORDS.add(new ImmutableBytesWritable(Bytes.toBytes(ENCRYPTION_KEY)));
270   }
271 
272   private static final int UNINITIALIZED = -1;
273 
274   // Column family name
275   private byte [] name;
276 
277   // Column metadata
278   private final Map<ImmutableBytesWritable, ImmutableBytesWritable> values =
279     new HashMap<ImmutableBytesWritable,ImmutableBytesWritable>();
280 
281   /**
282    * A map which holds the configuration specific to the column family.
283    * The keys of the map have the same names as config keys and override the defaults with
284    * cf-specific settings. Example usage may be for compactions, etc.
285    */
286   private final Map<String, String> configuration = new HashMap<String, String>();
287 
288   /*
289    * Cache the max versions rather than calculate it every time.
290    */
291   private int cachedMaxVersions = UNINITIALIZED;
292 
293   /**
294    * Default constructor.
295    * @deprecated As of release 0.96
296    *             (<a href="https://issues.apache.org/jira/browse/HBASE-5453">HBASE-5453</a>).
297    *             This will be made private in HBase 2.0.0.
298    *             Used by Writables and Writables are going away.
299    */
300   @Deprecated
301   // Make this private rather than remove after deprecation period elapses.  Its needed by pb
302   // deserializations.
303   public HColumnDescriptor() {
304     this.name = null;
305   }
306 
307   /**
308    * Construct a column descriptor specifying only the family name
309    * The other attributes are defaulted.
310    *
311    * @param familyName Column family name. Must be 'printable' -- digit or
312    * letter -- and may not contain a <code>:</code>
313    */
314   public HColumnDescriptor(final String familyName) {
315     this(Bytes.toBytes(familyName));
316   }
317 
318   /**
319    * Construct a column descriptor specifying only the family name
320    * The other attributes are defaulted.
321    *
322    * @param familyName Column family name. Must be 'printable' -- digit or
323    * letter -- and may not contain a <code>:</code>
324    */
325   public HColumnDescriptor(final byte [] familyName) {
326     this (familyName == null || familyName.length <= 0?
327       HConstants.EMPTY_BYTE_ARRAY: familyName, DEFAULT_VERSIONS,
328       DEFAULT_COMPRESSION, DEFAULT_IN_MEMORY, DEFAULT_BLOCKCACHE,
329       DEFAULT_TTL, DEFAULT_BLOOMFILTER);
330   }
331 
332   /**
333    * Constructor.
334    * Makes a deep copy of the supplied descriptor.
335    * Can make a modifiable descriptor from an UnmodifyableHColumnDescriptor.
336    * @param desc The descriptor.
337    */
338   public HColumnDescriptor(HColumnDescriptor desc) {
339     super();
340     this.name = desc.name.clone();
341     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
342         desc.values.entrySet()) {
343       this.values.put(e.getKey(), e.getValue());
344     }
345     for (Map.Entry<String, String> e : desc.configuration.entrySet()) {
346       this.configuration.put(e.getKey(), e.getValue());
347     }
348     setMaxVersions(desc.getMaxVersions());
349   }
350 
351   /**
352    * Constructor
353    * @param familyName Column family name. Must be 'printable' -- digit or
354    * letter -- and may not contain a <code>:</code>
355    * @param maxVersions Maximum number of versions to keep
356    * @param compression Compression type
357    * @param inMemory If true, column data should be kept in an HRegionServer's
358    * cache
359    * @param blockCacheEnabled If true, MapFile blocks should be cached
360    * @param timeToLive Time-to-live of cell contents, in seconds
361    * (use HConstants.FOREVER for unlimited TTL)
362    * @param bloomFilter Bloom filter type for this column
363    *
364    * @throws IllegalArgumentException if passed a family name that is made of
365    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
366    * a <code>:</code>
367    * @throws IllegalArgumentException if the number of versions is &lt;= 0
368    * @deprecated As of release 0.96
369    *             (<a href="https://issues.apache.org/jira/browse/HBASE-">HBASE-</a>).
370    *             This will be removed in HBase 2.0.0.
371    *             Use {@link #HColumnDescriptor(String)} and setters.
372    */
373   @Deprecated
374   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
375       final String compression, final boolean inMemory,
376       final boolean blockCacheEnabled,
377       final int timeToLive, final String bloomFilter) {
378     this(familyName, maxVersions, compression, inMemory, blockCacheEnabled,
379       DEFAULT_BLOCKSIZE, timeToLive, bloomFilter, DEFAULT_REPLICATION_SCOPE);
380   }
381 
382   /**
383    * Constructor
384    * @param familyName Column family name. Must be 'printable' -- digit or
385    * letter -- and may not contain a <code>:</code>
386    * @param maxVersions Maximum number of versions to keep
387    * @param compression Compression type
388    * @param inMemory If true, column data should be kept in an HRegionServer's
389    * cache
390    * @param blockCacheEnabled If true, MapFile blocks should be cached
391    * @param blocksize Block size to use when writing out storefiles.  Use
392    * smaller block sizes for faster random-access at expense of larger indices
393    * (more memory consumption).  Default is usually 64k.
394    * @param timeToLive Time-to-live of cell contents, in seconds
395    * (use HConstants.FOREVER for unlimited TTL)
396    * @param bloomFilter Bloom filter type for this column
397    * @param scope The scope tag for this column
398    *
399    * @throws IllegalArgumentException if passed a family name that is made of
400    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
401    * a <code>:</code>
402    * @throws IllegalArgumentException if the number of versions is &lt;= 0
403    * @deprecated As of release 0.96
404    *             (<a href="https://issues.apache.org/jira/browse/HBASE-">HBASE-</a>).
405    *             This will be removed in HBase 2.0.0.
406    *             Use {@link #HColumnDescriptor(String)} and setters.
407    */
408   @Deprecated
409   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
410       final String compression, final boolean inMemory,
411       final boolean blockCacheEnabled, final int blocksize,
412       final int timeToLive, final String bloomFilter, final int scope) {
413     this(familyName, DEFAULT_MIN_VERSIONS, maxVersions, DEFAULT_KEEP_DELETED,
414         compression, DEFAULT_ENCODE_ON_DISK, DEFAULT_DATA_BLOCK_ENCODING,
415         inMemory, blockCacheEnabled, blocksize, timeToLive, bloomFilter,
416         scope);
417   }
418 
419   /**
420    * Constructor
421    * @param familyName Column family name. Must be 'printable' -- digit or
422    * letter -- and may not contain a <code>:</code>
423    * @param minVersions Minimum number of versions to keep
424    * @param maxVersions Maximum number of versions to keep
425    * @param keepDeletedCells Whether to retain deleted cells until they expire
426    *        up to maxVersions versions.
427    * @param compression Compression type
428    * @param encodeOnDisk whether to use the specified data block encoding
429    *        on disk. If false, the encoding will be used in cache only.
430    * @param dataBlockEncoding data block encoding
431    * @param inMemory If true, column data should be kept in an HRegionServer's
432    * cache
433    * @param blockCacheEnabled If true, MapFile blocks should be cached
434    * @param blocksize Block size to use when writing out storefiles.  Use
435    * smaller blocksizes for faster random-access at expense of larger indices
436    * (more memory consumption).  Default is usually 64k.
437    * @param timeToLive Time-to-live of cell contents, in seconds
438    * (use HConstants.FOREVER for unlimited TTL)
439    * @param bloomFilter Bloom filter type for this column
440    * @param scope The scope tag for this column
441    *
442    * @throws IllegalArgumentException if passed a family name that is made of
443    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
444    * a <code>:</code>
445    * @throws IllegalArgumentException if the number of versions is &lt;= 0
446    * @deprecated As of release 0.96
447    *             (<a href="https://issues.apache.org/jira/browse/HBASE-">HBASE-</a>).
448    *             This will be removed in HBase 2.0.0.
449    *             Use {@link #HColumnDescriptor(String)} and setters.
450    */
451   @Deprecated
452   public HColumnDescriptor(final byte[] familyName, final int minVersions,
453       final int maxVersions, final KeepDeletedCells keepDeletedCells,
454       final String compression, final boolean encodeOnDisk,
455       final String dataBlockEncoding, final boolean inMemory,
456       final boolean blockCacheEnabled, final int blocksize,
457       final int timeToLive, final String bloomFilter, final int scope) {
458     isLegalFamilyName(familyName);
459     this.name = familyName;
460 
461     if (maxVersions <= 0) {
462       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
463       // Until there is support, consider 0 or < 0 -- a configuration error.
464       throw new IllegalArgumentException("Maximum versions must be positive");
465     }
466 
467     if (minVersions > 0) {
468       if (timeToLive == HConstants.FOREVER) {
469         throw new IllegalArgumentException("Minimum versions requires TTL.");
470       }
471       if (minVersions >= maxVersions) {
472         throw new IllegalArgumentException("Minimum versions must be < "
473             + "maximum versions.");
474       }
475     }
476 
477     setMaxVersions(maxVersions);
478     setMinVersions(minVersions);
479     setKeepDeletedCells(keepDeletedCells);
480     setInMemory(inMemory);
481     setBlockCacheEnabled(blockCacheEnabled);
482     setTimeToLive(timeToLive);
483     setCompressionType(Compression.Algorithm.
484       valueOf(compression.toUpperCase(Locale.ROOT)));
485     setDataBlockEncoding(DataBlockEncoding.
486         valueOf(dataBlockEncoding.toUpperCase(Locale.ROOT)));
487     setBloomFilterType(BloomType.
488       valueOf(bloomFilter.toUpperCase(Locale.ROOT)));
489     setBlocksize(blocksize);
490     setScope(scope);
491   }
492 
493   /**
494    * @param b Family name.
495    * @return <code>b</code>
496    * @throws IllegalArgumentException If not null and not a legitimate family
497    * name: i.e. 'printable' and ends in a ':' (Null passes are allowed because
498    * <code>b</code> can be null when deserializing).  Cannot start with a '.'
499    * either. Also Family can not be an empty value or equal "recovered.edits".
500    */
501   public static byte [] isLegalFamilyName(final byte [] b) {
502     if (b == null) {
503       return b;
504     }
505     Preconditions.checkArgument(b.length != 0, "Family name can not be empty");
506     if (b[0] == '.') {
507       throw new IllegalArgumentException("Family names cannot start with a " +
508         "period: " + Bytes.toString(b));
509     }
510     for (int i = 0; i < b.length; i++) {
511       if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
512         throw new IllegalArgumentException("Illegal character <" + b[i] +
513           ">. Family names cannot contain control characters or colons: " +
514           Bytes.toString(b));
515       }
516     }
517     byte[] recoveredEdit = Bytes.toBytes(HConstants.RECOVERED_EDITS_DIR);
518     if (Bytes.equals(recoveredEdit, b)) {
519       throw new IllegalArgumentException("Family name cannot be: " +
520           HConstants.RECOVERED_EDITS_DIR);
521     }
522     return b;
523   }
524 
525   /**
526    * @return Name of this column family
527    */
528   public byte [] getName() {
529     return name;
530   }
531 
532   /**
533    * @return Name of this column family
534    */
535   public String getNameAsString() {
536     return Bytes.toString(this.name);
537   }
538 
539   /**
540    * @param key The key.
541    * @return The value.
542    */
543   public byte[] getValue(byte[] key) {
544     ImmutableBytesWritable ibw = values.get(new ImmutableBytesWritable(key));
545     if (ibw == null)
546       return null;
547     return ibw.get();
548   }
549 
550   /**
551    * @param key The key.
552    * @return The value as a string.
553    */
554   public String getValue(String key) {
555     byte[] value = getValue(Bytes.toBytes(key));
556     if (value == null)
557       return null;
558     return Bytes.toString(value);
559   }
560 
561   /**
562    * @return All values.
563    */
564   public Map<ImmutableBytesWritable,ImmutableBytesWritable> getValues() {
565     // shallow pointer copy
566     return Collections.unmodifiableMap(values);
567   }
568 
569   /**
570    * @param key The key.
571    * @param value The value.
572    * @return this (for chained invocation)
573    */
574   public HColumnDescriptor setValue(byte[] key, byte[] value) {
575     if (Bytes.compareTo(Bytes.toBytes(HConstants.VERSIONS), key) == 0) {
576       cachedMaxVersions = UNINITIALIZED;
577     } else if (Bytes.compareTo(REPLICATION_SCOPE_BYTES, key) == 0) {
578       // as bytes are encoded from string, we have to decode value as string
579       int scopeType = Integer.parseInt(Bytes.toString(value));
580       if (scopeType != WALProtos.ScopeType.REPLICATION_SCOPE_GLOBAL_VALUE &&
581           scopeType != WALProtos.ScopeType.REPLICATION_SCOPE_LOCAL_VALUE) {
582         throw new IllegalArgumentException("Invalid value '" + scopeType +
583             "' for REPLICATION_SCOPE.");
584       }
585     }
586     if (value == null || value.length == 0) {
587       remove(key);
588     } else {
589       values.put(new ImmutableBytesWritable(key),
590         new ImmutableBytesWritable(value));
591     }
592     return this;
593   }
594 
595   /**
596    * @param key Key whose key and value we're to remove from HCD parameters.
597    */
598   public void remove(final byte [] key) {
599     values.remove(new ImmutableBytesWritable(key));
600   }
601 
602   /**
603    * @param key The key.
604    * @param value The value.
605    * @return this (for chained invocation)
606    */
607   public HColumnDescriptor setValue(String key, String value) {
608     if (value == null || value.length() == 0) {
609       remove(Bytes.toBytes(key));
610     } else {
611       setValue(Bytes.toBytes(key), Bytes.toBytes(value));
612     }
613     return this;
614   }
615 
616   /** @return compression type being used for the column family */
617   public Compression.Algorithm getCompression() {
618     String n = getValue(COMPRESSION);
619     if (n == null) {
620       return Compression.Algorithm.NONE;
621     }
622     return Compression.Algorithm.valueOf(n.toUpperCase(Locale.ROOT));
623   }
624 
625   /** @return compression type being used for the column family for major
626       compression */
627   public Compression.Algorithm getCompactionCompression() {
628     String n = getValue(COMPRESSION_COMPACT);
629     if (n == null) {
630       return getCompression();
631     }
632     return Compression.Algorithm.valueOf(n.toUpperCase(Locale.ROOT));
633   }
634 
635   /** @return maximum number of versions */
636   public int getMaxVersions() {
637     if (this.cachedMaxVersions == UNINITIALIZED) {
638       String v = getValue(HConstants.VERSIONS);
639       this.cachedMaxVersions = Integer.parseInt(v);
640     }
641     return this.cachedMaxVersions;
642   }
643 
644   /**
645    * @param maxVersions maximum number of versions
646    * @return this (for chained invocation)
647    */
648   public HColumnDescriptor setMaxVersions(int maxVersions) {
649     if (maxVersions <= 0) {
650       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
651       // Until there is support, consider 0 or < 0 -- a configuration error.
652       throw new IllegalArgumentException("Maximum versions must be positive");
653     }
654     if (maxVersions < this.getMinVersions()) {
655         throw new IllegalArgumentException("Set MaxVersion to " + maxVersions
656             + " while minVersion is " + this.getMinVersions()
657             + ". Maximum versions must be >= minimum versions ");
658     }
659     setValue(HConstants.VERSIONS, Integer.toString(maxVersions));
660     cachedMaxVersions = maxVersions;
661     return this;
662   }
663 
664   /**
665    * Set minimum and maximum versions to keep
666    *
667    * @param minVersions minimal number of versions
668    * @param maxVersions maximum number of versions
669    * @return this (for chained invocation)
670    */
671   public HColumnDescriptor setVersions(int minVersions, int maxVersions) {
672     if (minVersions <= 0) {
673       // TODO: Allow minVersion and maxVersion of 0 to be the way you say "Keep all versions".
674       // Until there is support, consider 0 or < 0 -- a configuration error.
675       throw new IllegalArgumentException("Minimum versions must be positive");
676     }
677 
678     if (maxVersions < minVersions) {
679       throw new IllegalArgumentException("Unable to set MaxVersion to " + maxVersions
680         + " and set MinVersion to " + minVersions
681         + ", as maximum versions must be >= minimum versions.");
682     }
683     setMinVersions(minVersions);
684     setMaxVersions(maxVersions);
685     return this;
686   }
687 
688   /**
689    * @return The storefile/hfile blocksize for this column family.
690    */
691   public synchronized int getBlocksize() {
692     if (this.blocksize == null) {
693       String value = getValue(BLOCKSIZE);
694       this.blocksize = (value != null)?
695         Integer.decode(value): Integer.valueOf(DEFAULT_BLOCKSIZE);
696     }
697     return this.blocksize.intValue();
698   }
699 
700   /**
701    * @param s Blocksize to use when writing out storefiles/hfiles on this
702    * column family.
703    * @return this (for chained invocation)
704    */
705   public HColumnDescriptor setBlocksize(int s) {
706     setValue(BLOCKSIZE, Integer.toString(s));
707     this.blocksize = null;
708     return this;
709   }
710 
711   /**
712    * @return Compression type setting.
713    */
714   public Compression.Algorithm getCompressionType() {
715     return getCompression();
716   }
717 
718   /**
719    * Compression types supported in hbase.
720    * LZO is not bundled as part of the hbase distribution.
721    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
722    * for how to enable it.
723    * @param type Compression type setting.
724    * @return this (for chained invocation)
725    */
726   public HColumnDescriptor setCompressionType(Compression.Algorithm type) {
727     return setValue(COMPRESSION, type.getName().toUpperCase(Locale.ROOT));
728   }
729 
730   /**
731    * @return data block encoding algorithm used on disk
732    * @deprecated As of release 0.98
733    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9870">HBASE-9870</a>).
734    *             This will be removed in HBase 2.0.0. See {@link #getDataBlockEncoding()}}
735    */
736   @Deprecated
737   public DataBlockEncoding getDataBlockEncodingOnDisk() {
738     return getDataBlockEncoding();
739   }
740 
741   /**
742    * This method does nothing now. Flag ENCODE_ON_DISK is not used
743    * any more. Data blocks have the same encoding in cache as on disk.
744    * @return this (for chained invocation)
745    * @deprecated As of release 0.98
746    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9870">HBASE-9870</a>).
747    *             This will be removed in HBase 2.0.0. This method does nothing now.
748    */
749   @Deprecated
750   public HColumnDescriptor setEncodeOnDisk(boolean encodeOnDisk) {
751     return this;
752   }
753 
754   /**
755    * @return the data block encoding algorithm used in block cache and
756    *         optionally on disk
757    */
758   public DataBlockEncoding getDataBlockEncoding() {
759     String type = getValue(DATA_BLOCK_ENCODING);
760     if (type == null) {
761       type = DEFAULT_DATA_BLOCK_ENCODING;
762     }
763     return DataBlockEncoding.valueOf(type);
764   }
765 
766   /**
767    * Set data block encoding algorithm used in block cache.
768    * @param type What kind of data block encoding will be used.
769    * @return this (for chained invocation)
770    */
771   public HColumnDescriptor setDataBlockEncoding(DataBlockEncoding type) {
772     String name;
773     if (type != null) {
774       name = type.toString();
775     } else {
776       name = DataBlockEncoding.NONE.toString();
777     }
778     return setValue(DATA_BLOCK_ENCODING, name);
779   }
780 
781   /**
782    * Set whether the tags should be compressed along with DataBlockEncoding. When no
783    * DataBlockEncoding is been used, this is having no effect.
784    *
785    * @param compressTags
786    * @return this (for chained invocation)
787    */
788   public HColumnDescriptor setCompressTags(boolean compressTags) {
789     return setValue(COMPRESS_TAGS, String.valueOf(compressTags));
790   }
791 
792   /**
793    * @return Whether KV tags should be compressed along with DataBlockEncoding. When no
794    *         DataBlockEncoding is been used, this is having no effect.
795    * @deprecated As of release 1.0.0
796    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
797    *             This will be removed in HBase 2.0.0. Use {@link #isCompressTags()} instead.
798    */
799   @Deprecated
800   public boolean shouldCompressTags() {
801     String compressTagsStr = getValue(COMPRESS_TAGS);
802     boolean compressTags = DEFAULT_COMPRESS_TAGS;
803     if (compressTagsStr != null) {
804       compressTags = Boolean.valueOf(compressTagsStr);
805     }
806     return compressTags;
807   }
808 
809   /**
810    * @return Whether KV tags should be compressed along with DataBlockEncoding. When no
811    *         DataBlockEncoding is been used, this is having no effect.
812    */
813   public boolean isCompressTags() {
814     String compressTagsStr = getValue(COMPRESS_TAGS);
815     boolean compressTags = DEFAULT_COMPRESS_TAGS;
816     if (compressTagsStr != null) {
817       compressTags = Boolean.valueOf(compressTagsStr);
818     }
819     return compressTags;
820   }
821 
822   /**
823    * @return Compression type setting.
824    */
825   public Compression.Algorithm getCompactionCompressionType() {
826     return getCompactionCompression();
827   }
828 
829   /**
830    * Compression types supported in hbase.
831    * LZO is not bundled as part of the hbase distribution.
832    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
833    * for how to enable it.
834    * @param type Compression type setting.
835    * @return this (for chained invocation)
836    */
837   public HColumnDescriptor setCompactionCompressionType(
838       Compression.Algorithm type) {
839     return setValue(COMPRESSION_COMPACT, type.getName().toUpperCase(Locale.ROOT));
840   }
841 
842   /**
843    * @return True if we are to favor keeping all values for this column family in the
844    * HRegionServer cache.
845    */
846   public boolean isInMemory() {
847     String value = getValue(HConstants.IN_MEMORY);
848     if (value != null)
849       return Boolean.valueOf(value).booleanValue();
850     return DEFAULT_IN_MEMORY;
851   }
852 
853   /**
854    * @param inMemory True if we are to favor keeping all values for this column family in the
855    * HRegionServer cache
856    * @return this (for chained invocation)
857    */
858   public HColumnDescriptor setInMemory(boolean inMemory) {
859     return setValue(HConstants.IN_MEMORY, Boolean.toString(inMemory));
860   }
861 
862   public KeepDeletedCells getKeepDeletedCells() {
863     String value = getValue(KEEP_DELETED_CELLS);
864     if (value != null) {
865       // toUpperCase for backwards compatibility
866       return KeepDeletedCells.valueOf(value.toUpperCase(Locale.ROOT));
867     }
868     return DEFAULT_KEEP_DELETED;
869   }
870 
871   /**
872    * @param keepDeletedCells True if deleted rows should not be collected
873    * immediately.
874    * @return this (for chained invocation)
875    * @deprecated As of release 1.0.0
876    *             (<a href="https://issues.apache.org/jira/browse/HBASE-12363">HBASE-12363</a>).
877    *             This will be removed in HBase 2.0.0.
878    *             Use {@link #setKeepDeletedCells(KeepDeletedCells)}.
879    */
880   @Deprecated
881   public HColumnDescriptor setKeepDeletedCells(boolean keepDeletedCells) {
882     return setValue(KEEP_DELETED_CELLS, (keepDeletedCells ? KeepDeletedCells.TRUE
883         : KeepDeletedCells.FALSE).toString());
884   }
885 
886   /**
887    * @param keepDeletedCells True if deleted rows should not be collected
888    * immediately.
889    * @return this (for chained invocation)
890    */
891   public HColumnDescriptor setKeepDeletedCells(KeepDeletedCells keepDeletedCells) {
892     return setValue(KEEP_DELETED_CELLS, keepDeletedCells.toString());
893   }
894 
895   /**
896    * @return Time-to-live of cell contents, in seconds.
897    */
898   public int getTimeToLive() {
899     String value = getValue(TTL);
900     return (value != null)? Integer.parseInt(value): DEFAULT_TTL;
901   }
902 
903   /**
904    * @param timeToLive Time-to-live of cell contents, in seconds.
905    * @return this (for chained invocation)
906    */
907   public HColumnDescriptor setTimeToLive(int timeToLive) {
908     return setValue(TTL, Integer.toString(timeToLive));
909   }
910 
911   /**
912    * @return The minimum number of versions to keep.
913    */
914   public int getMinVersions() {
915     String value = getValue(MIN_VERSIONS);
916     return (value != null)? Integer.parseInt(value): 0;
917   }
918 
919   /**
920    * @param minVersions The minimum number of versions to keep.
921    * (used when timeToLive is set)
922    * @return this (for chained invocation)
923    */
924   public HColumnDescriptor setMinVersions(int minVersions) {
925     return setValue(MIN_VERSIONS, Integer.toString(minVersions));
926   }
927 
928   /**
929    * Retain all versions for a given TTL(retentionInterval), and then only a specific number
930    * of versions(versionAfterInterval) after that interval elapses.
931    *
932    * @param retentionInterval Retain all versions for this interval
933    * @param versionAfterInterval Retain no of versions to retain after retentionInterval
934    * @return this (for chained invocation)
935    */
936   public HColumnDescriptor setVersionsWithTimeToLive(final int retentionInterval,
937       final int versionAfterInterval) {
938     HColumnDescriptor hColumnDescriptor =
939       setVersions(versionAfterInterval, Integer.MAX_VALUE);
940     hColumnDescriptor.setTimeToLive(retentionInterval);
941     hColumnDescriptor.setKeepDeletedCells(KeepDeletedCells.TTL);
942     return hColumnDescriptor;
943   }
944 
945   /**
946    * @return True if hfile DATA type blocks should be cached (You cannot disable caching of INDEX
947    * and BLOOM type blocks).
948    */
949   public boolean isBlockCacheEnabled() {
950     String value = getValue(BLOCKCACHE);
951     if (value != null)
952       return Boolean.parseBoolean(value);
953     return DEFAULT_BLOCKCACHE;
954   }
955 
956   /**
957    * @param blockCacheEnabled True if hfile DATA type blocks should be cached (We always cache
958    * INDEX and BLOOM blocks; you cannot turn this off).
959    * @return this (for chained invocation)
960    */
961   public HColumnDescriptor setBlockCacheEnabled(boolean blockCacheEnabled) {
962     return setValue(BLOCKCACHE, Boolean.toString(blockCacheEnabled));
963   }
964 
965   /**
966    * @return bloom filter type used for new StoreFiles in ColumnFamily
967    */
968   public BloomType getBloomFilterType() {
969     String n = getValue(BLOOMFILTER);
970     if (n == null) {
971       n = DEFAULT_BLOOMFILTER;
972     }
973     return BloomType.valueOf(n.toUpperCase(Locale.ROOT));
974   }
975 
976   /**
977    * @param bt bloom filter type
978    * @return this (for chained invocation)
979    */
980   public HColumnDescriptor setBloomFilterType(final BloomType bt) {
981     return setValue(BLOOMFILTER, bt.toString());
982   }
983 
984    /**
985     * @return the scope tag
986     */
987   public int getScope() {
988     byte[] value = getValue(REPLICATION_SCOPE_BYTES);
989     if (value != null) {
990       return Integer.parseInt(Bytes.toString(value));
991     }
992     return DEFAULT_REPLICATION_SCOPE;
993   }
994 
995  /**
996   * @param scope the scope tag
997   * @return this (for chained invocation)
998   */
999   public HColumnDescriptor setScope(int scope) {
1000     return setValue(REPLICATION_SCOPE, Integer.toString(scope));
1001   }
1002 
1003   /**
1004    * @return true if we should cache data blocks on write
1005    * @deprecated As of release 1.0.0
1006    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1007    *             This will be removed in HBase 2.0.0. Use {@link #isCacheDataOnWrite()}} instead.
1008    */
1009   @Deprecated
1010   public boolean shouldCacheDataOnWrite() {
1011     return setAndGetBoolean(CACHE_DATA_ON_WRITE, DEFAULT_CACHE_DATA_ON_WRITE);
1012   }
1013 
1014   /**
1015    * @return true if we should cache data blocks on write
1016    */
1017   public boolean isCacheDataOnWrite() {
1018     return setAndGetBoolean(CACHE_DATA_ON_WRITE, DEFAULT_CACHE_DATA_ON_WRITE);
1019   }
1020 
1021   /**
1022    * @param value true if we should cache data blocks on write
1023    * @return this (for chained invocation)
1024    */
1025   public HColumnDescriptor setCacheDataOnWrite(boolean value) {
1026     return setValue(CACHE_DATA_ON_WRITE, Boolean.toString(value));
1027   }
1028 
1029   /**
1030    * @return true if we should cache data blocks in the L1 cache (if block cache deploy
1031    * has more than one tier; e.g. we are using CombinedBlockCache).
1032    * @deprecated As of release 1.0.0
1033    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1034    *             This will be removed in HBase 2.0.0. Use {@link #isCacheDataInL1()}} instead.
1035    */
1036   @Deprecated
1037   public boolean shouldCacheDataInL1() {
1038     return setAndGetBoolean(CACHE_DATA_IN_L1, DEFAULT_CACHE_DATA_IN_L1);
1039   }
1040 
1041   /**
1042    * @return true if we should cache data blocks in the L1 cache (if block cache deploy has more
1043    *         than one tier; e.g. we are using CombinedBlockCache).
1044    */
1045   public boolean isCacheDataInL1() {
1046     return setAndGetBoolean(CACHE_DATA_IN_L1, DEFAULT_CACHE_DATA_IN_L1);
1047   }
1048 
1049   /**
1050    * @param value true if we should cache data blocks in the L1 cache (if block cache deploy
1051    * has more than one tier; e.g. we are using CombinedBlockCache).
1052    * @return this (for chained invocation)
1053    */
1054   public HColumnDescriptor setCacheDataInL1(boolean value) {
1055     return setValue(CACHE_DATA_IN_L1, Boolean.toString(value));
1056   }
1057 
1058   private boolean setAndGetBoolean(final String key, final boolean defaultSetting) {
1059     String value = getValue(key);
1060     if (value != null) return Boolean.parseBoolean(value);
1061     return defaultSetting;
1062   }
1063 
1064   /**
1065    * @return true if we should cache index blocks on write
1066    * @deprecated As of release 1.0.0
1067    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1068    *             This will be removed in HBase 2.0.0.
1069    *             Use {@link #isCacheIndexesOnWrite()} instead.
1070    */
1071   @Deprecated
1072   public boolean shouldCacheIndexesOnWrite() {
1073     return setAndGetBoolean(CACHE_INDEX_ON_WRITE, DEFAULT_CACHE_INDEX_ON_WRITE);
1074   }
1075 
1076   /**
1077    * @return true if we should cache index blocks on write
1078    */
1079   public boolean isCacheIndexesOnWrite() {
1080     return setAndGetBoolean(CACHE_INDEX_ON_WRITE, DEFAULT_CACHE_INDEX_ON_WRITE);
1081   }
1082 
1083   /**
1084    * @param value true if we should cache index blocks on write
1085    * @return this (for chained invocation)
1086    */
1087   public HColumnDescriptor setCacheIndexesOnWrite(boolean value) {
1088     return setValue(CACHE_INDEX_ON_WRITE, Boolean.toString(value));
1089   }
1090 
1091   /**
1092    * @return true if we should cache bloomfilter blocks on write
1093    * @deprecated As of release 1.0.0
1094    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1095    *             This will be removed in HBase 2.0.0.
1096    *             Use {@link #isCacheBloomsOnWrite()}} instead.
1097    */
1098   @Deprecated
1099   public boolean shouldCacheBloomsOnWrite() {
1100     return setAndGetBoolean(CACHE_BLOOMS_ON_WRITE, DEFAULT_CACHE_BLOOMS_ON_WRITE);
1101   }
1102 
1103   /**
1104    * @return true if we should cache bloomfilter blocks on write
1105    */
1106   public boolean isCacheBloomsOnWrite() {
1107     return setAndGetBoolean(CACHE_BLOOMS_ON_WRITE, DEFAULT_CACHE_BLOOMS_ON_WRITE);
1108   }
1109 
1110   /**
1111    * @param value true if we should cache bloomfilter blocks on write
1112    * @return this (for chained invocation)
1113    */
1114   public HColumnDescriptor setCacheBloomsOnWrite(boolean value) {
1115     return setValue(CACHE_BLOOMS_ON_WRITE, Boolean.toString(value));
1116   }
1117 
1118   /**
1119    * @return true if we should evict cached blocks from the blockcache on
1120    * close
1121    * @deprecated As of release 1.0.0
1122    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1123    *             This will be removed in HBase 2.0.0.
1124    *             Use {@link #isEvictBlocksOnClose()}} instead.
1125    */
1126   @Deprecated
1127   public boolean shouldEvictBlocksOnClose() {
1128     return setAndGetBoolean(EVICT_BLOCKS_ON_CLOSE, DEFAULT_EVICT_BLOCKS_ON_CLOSE);
1129   }
1130 
1131   /**
1132    * @return true if we should evict cached blocks from the blockcache on close
1133    */
1134   public boolean isEvictBlocksOnClose() {
1135     return setAndGetBoolean(EVICT_BLOCKS_ON_CLOSE, DEFAULT_EVICT_BLOCKS_ON_CLOSE);
1136   }
1137 
1138   /**
1139    * @param value true if we should evict cached blocks from the blockcache on
1140    * close
1141    * @return this (for chained invocation)
1142    */
1143   public HColumnDescriptor setEvictBlocksOnClose(boolean value) {
1144     return setValue(EVICT_BLOCKS_ON_CLOSE, Boolean.toString(value));
1145   }
1146 
1147   /**
1148    * @return true if we should prefetch blocks into the blockcache on open
1149    * @deprecated As of release 1.0.0
1150    *             (<a href="https://issues.apache.org/jira/browse/HBASE-10870">HBASE-10870</a>).
1151    *             This will be removed in HBase 2.0.0.
1152    *             Use {@link #isPrefetchBlocksOnOpen()}}} instead.
1153    */
1154   @Deprecated
1155   public boolean shouldPrefetchBlocksOnOpen() {
1156     return setAndGetBoolean(PREFETCH_BLOCKS_ON_OPEN, DEFAULT_PREFETCH_BLOCKS_ON_OPEN);
1157   }
1158 
1159   /**
1160    * @return true if we should prefetch blocks into the blockcache on open
1161    */
1162   public boolean isPrefetchBlocksOnOpen() {
1163     return setAndGetBoolean(PREFETCH_BLOCKS_ON_OPEN, DEFAULT_PREFETCH_BLOCKS_ON_OPEN);
1164   }
1165 
1166   /**
1167    * @param value true if we should prefetch blocks into the blockcache on open
1168    * @return this (for chained invocation)
1169    */
1170   public HColumnDescriptor setPrefetchBlocksOnOpen(boolean value) {
1171     return setValue(PREFETCH_BLOCKS_ON_OPEN, Boolean.toString(value));
1172   }
1173 
1174   /**
1175    * @see java.lang.Object#toString()
1176    */
1177   @Override
1178   public String toString() {
1179     StringBuilder s = new StringBuilder();
1180 
1181     s.append('{');
1182     s.append(HConstants.NAME);
1183     s.append(" => '");
1184     s.append(Bytes.toString(name));
1185     s.append("'");
1186     s.append(getValues(true));
1187     s.append('}');
1188     return s.toString();
1189   }
1190 
1191   /**
1192    * @return Column family descriptor with only the customized attributes.
1193    */
1194   public String toStringCustomizedValues() {
1195     StringBuilder s = new StringBuilder();
1196     s.append('{');
1197     s.append(HConstants.NAME);
1198     s.append(" => '");
1199     s.append(Bytes.toString(name));
1200     s.append("'");
1201     s.append(getValues(false));
1202     s.append('}');
1203     return s.toString();
1204   }
1205 
1206   private StringBuilder getValues(boolean printDefaults) {
1207     StringBuilder s = new StringBuilder();
1208 
1209     boolean hasConfigKeys = false;
1210 
1211     // print all reserved keys first
1212     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> entry : values.entrySet()) {
1213       ImmutableBytesWritable k = entry.getKey();
1214       if (!RESERVED_KEYWORDS.contains(k)) {
1215         hasConfigKeys = true;
1216         continue;
1217       }
1218       String key = Bytes.toString(k.get());
1219       String value = Bytes.toStringBinary(entry.getValue().get());
1220       if (printDefaults
1221           || !DEFAULT_VALUES.containsKey(key)
1222           || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
1223         s.append(", ");
1224         s.append(key);
1225         s.append(" => ");
1226         s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
1227       }
1228     }
1229 
1230     // print all non-reserved, advanced config keys as a separate subset
1231     if (hasConfigKeys) {
1232       s.append(", ");
1233       s.append(HConstants.METADATA).append(" => ");
1234       s.append('{');
1235       boolean printComma = false;
1236       for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> entry : values.entrySet()) {
1237         ImmutableBytesWritable k = entry.getKey();
1238         if (RESERVED_KEYWORDS.contains(k)) {
1239           continue;
1240         }
1241         String key = Bytes.toString(k.get());
1242         String value = Bytes.toStringBinary(entry.getValue().get());
1243         if (printComma) {
1244           s.append(", ");
1245         }
1246         printComma = true;
1247         s.append('\'').append(key).append('\'');
1248         s.append(" => ");
1249         s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
1250       }
1251       s.append('}');
1252     }
1253 
1254     if (!configuration.isEmpty()) {
1255       s.append(", ");
1256       s.append(HConstants.CONFIGURATION).append(" => ");
1257       s.append('{');
1258       boolean printCommaForConfiguration = false;
1259       for (Map.Entry<String, String> e : configuration.entrySet()) {
1260         if (printCommaForConfiguration) s.append(", ");
1261         printCommaForConfiguration = true;
1262         s.append('\'').append(e.getKey()).append('\'');
1263         s.append(" => ");
1264         s.append('\'').append(PrettyPrinter.format(e.getValue(), getUnit(e.getKey()))).append('\'');
1265       }
1266       s.append("}");
1267     }
1268     return s;
1269   }
1270 
1271   public static Unit getUnit(String key) {
1272     Unit unit;
1273       /* TTL for now, we can add more as we neeed */
1274     if (key.equals(HColumnDescriptor.TTL)) {
1275       unit = Unit.TIME_INTERVAL;
1276     } else {
1277       unit = Unit.NONE;
1278     }
1279     return unit;
1280   }
1281 
1282   public static Map<String, String> getDefaultValues() {
1283     return Collections.unmodifiableMap(DEFAULT_VALUES);
1284   }
1285 
1286   /**
1287    * @see java.lang.Object#equals(java.lang.Object)
1288    */
1289   @Override
1290   public boolean equals(Object obj) {
1291     if (this == obj) {
1292       return true;
1293     }
1294     if (obj == null) {
1295       return false;
1296     }
1297     if (!(obj instanceof HColumnDescriptor)) {
1298       return false;
1299     }
1300     return compareTo((HColumnDescriptor)obj) == 0;
1301   }
1302 
1303   /**
1304    * @see java.lang.Object#hashCode()
1305    */
1306   @Override
1307   public int hashCode() {
1308     int result = Bytes.hashCode(this.name);
1309     result ^= Byte.valueOf(COLUMN_DESCRIPTOR_VERSION).hashCode();
1310     result ^= values.hashCode();
1311     result ^= configuration.hashCode();
1312     return result;
1313   }
1314 
1315   /**
1316    * @deprecated Writables are going away.  Use pb {@link #parseFrom(byte[])} instead.
1317    */
1318   @Deprecated
1319   @Override
1320   public void readFields(DataInput in) throws IOException {
1321     int version = in.readByte();
1322     if (version < 6) {
1323       if (version <= 2) {
1324         Text t = new Text();
1325         t.readFields(in);
1326         this.name = t.getBytes();
1327 //        if(KeyValue.getFamilyDelimiterIndex(this.name, 0, this.name.length)
1328 //            > 0) {
1329 //          this.name = stripColon(this.name);
1330 //        }
1331       } else {
1332         this.name = Bytes.readByteArray(in);
1333       }
1334       this.values.clear();
1335       setMaxVersions(in.readInt());
1336       int ordinal = in.readInt();
1337       setCompressionType(Compression.Algorithm.values()[ordinal]);
1338       setInMemory(in.readBoolean());
1339       setBloomFilterType(in.readBoolean() ? BloomType.ROW : BloomType.NONE);
1340       if (getBloomFilterType() != BloomType.NONE && version < 5) {
1341         // If a bloomFilter is enabled and the column descriptor is less than
1342         // version 5, we need to skip over it to read the rest of the column
1343         // descriptor. There are no BloomFilterDescriptors written to disk for
1344         // column descriptors with a version number >= 5
1345         throw new UnsupportedClassVersionError(this.getClass().getName() +
1346             " does not support backward compatibility with versions older " +
1347             "than version 5");
1348       }
1349       if (version > 1) {
1350         setBlockCacheEnabled(in.readBoolean());
1351       }
1352       if (version > 2) {
1353        setTimeToLive(in.readInt());
1354       }
1355     } else {
1356       // version 6+
1357       this.name = Bytes.readByteArray(in);
1358       this.values.clear();
1359       int numValues = in.readInt();
1360       for (int i = 0; i < numValues; i++) {
1361         ImmutableBytesWritable key = new ImmutableBytesWritable();
1362         ImmutableBytesWritable value = new ImmutableBytesWritable();
1363         key.readFields(in);
1364         value.readFields(in);
1365 
1366         // in version 8, the BloomFilter setting changed from bool to enum
1367         if (version < 8 && Bytes.toString(key.get()).equals(BLOOMFILTER)) {
1368           value.set(Bytes.toBytes(
1369               Boolean.parseBoolean(Bytes.toString(value.get()))
1370                 ? BloomType.ROW.toString()
1371                 : BloomType.NONE.toString()));
1372         }
1373 
1374         values.put(key, value);
1375       }
1376       if (version == 6) {
1377         // Convert old values.
1378         setValue(COMPRESSION, Compression.Algorithm.NONE.getName());
1379       }
1380       String value = getValue(HConstants.VERSIONS);
1381       this.cachedMaxVersions = (value != null)?
1382           Integer.parseInt(value): DEFAULT_VERSIONS;
1383       if (version > 10) {
1384         configuration.clear();
1385         int numConfigs = in.readInt();
1386         for (int i = 0; i < numConfigs; i++) {
1387           ImmutableBytesWritable key = new ImmutableBytesWritable();
1388           ImmutableBytesWritable val = new ImmutableBytesWritable();
1389           key.readFields(in);
1390           val.readFields(in);
1391           configuration.put(
1392             Bytes.toString(key.get(), key.getOffset(), key.getLength()),
1393             Bytes.toString(val.get(), val.getOffset(), val.getLength()));
1394         }
1395       }
1396     }
1397   }
1398 
1399   /**
1400    * @deprecated Writables are going away.  Use {@link #toByteArray()} instead.
1401    */
1402   @Deprecated
1403   @Override
1404   public void write(DataOutput out) throws IOException {
1405     out.writeByte(COLUMN_DESCRIPTOR_VERSION);
1406     Bytes.writeByteArray(out, this.name);
1407     out.writeInt(values.size());
1408     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
1409         values.entrySet()) {
1410       e.getKey().write(out);
1411       e.getValue().write(out);
1412     }
1413     out.writeInt(configuration.size());
1414     for (Map.Entry<String, String> e : configuration.entrySet()) {
1415       new ImmutableBytesWritable(Bytes.toBytes(e.getKey())).write(out);
1416       new ImmutableBytesWritable(Bytes.toBytes(e.getValue())).write(out);
1417     }
1418   }
1419 
1420   // Comparable
1421   @Override
1422   public int compareTo(HColumnDescriptor o) {
1423     int result = Bytes.compareTo(this.name, o.getName());
1424     if (result == 0) {
1425       // punt on comparison for ordering, just calculate difference.
1426       result = this.values.hashCode() - o.values.hashCode();
1427       if (result < 0)
1428         result = -1;
1429       else if (result > 0)
1430         result = 1;
1431     }
1432     if (result == 0) {
1433       result = this.configuration.hashCode() - o.configuration.hashCode();
1434       if (result < 0)
1435         result = -1;
1436       else if (result > 0)
1437         result = 1;
1438     }
1439     return result;
1440   }
1441 
1442   /**
1443    * @return This instance serialized with pb with pb magic prefix
1444    * @see #parseFrom(byte[])
1445    */
1446   public byte [] toByteArray() {
1447     return ProtobufUtil.prependPBMagic(convert().toByteArray());
1448   }
1449 
1450   /**
1451    * @param bytes A pb serialized {@link HColumnDescriptor} instance with pb magic prefix
1452    * @return An instance of {@link HColumnDescriptor} made from <code>bytes</code>
1453    * @throws DeserializationException
1454    * @see #toByteArray()
1455    */
1456   public static HColumnDescriptor parseFrom(final byte [] bytes) throws DeserializationException {
1457     if (!ProtobufUtil.isPBMagicPrefix(bytes)) throw new DeserializationException("No magic");
1458     int pblen = ProtobufUtil.lengthOfPBMagic();
1459     ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1460     ColumnFamilySchema cfs = null;
1461     try {
1462       ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen);
1463       cfs = builder.build();
1464     } catch (IOException e) {
1465       throw new DeserializationException(e);
1466     }
1467     return convert(cfs);
1468   }
1469 
1470   /**
1471    * @param cfs
1472    * @return An {@link HColumnDescriptor} made from the passed in <code>cfs</code>
1473    */
1474   @Deprecated
1475   public static HColumnDescriptor convert(final ColumnFamilySchema cfs) {
1476     // Use the empty constructor so we preserve the initial values set on construction for things
1477     // like maxVersion.  Otherwise, we pick up wrong values on deserialization which makes for
1478     // unrelated-looking test failures that are hard to trace back to here.
1479     HColumnDescriptor hcd = new HColumnDescriptor();
1480     hcd.name = cfs.getName().toByteArray();
1481     for (BytesBytesPair a: cfs.getAttributesList()) {
1482       hcd.setValue(a.getFirst().toByteArray(), a.getSecond().toByteArray());
1483     }
1484     for (NameStringPair a: cfs.getConfigurationList()) {
1485       hcd.setConfiguration(a.getName(), a.getValue());
1486     }
1487     return hcd;
1488   }
1489 
1490   /**
1491    * @return Convert this instance to a the pb column family type
1492    */
1493   @Deprecated
1494   public ColumnFamilySchema convert() {
1495     ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1496     builder.setName(ByteStringer.wrap(getName()));
1497     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e: this.values.entrySet()) {
1498       BytesBytesPair.Builder aBuilder = BytesBytesPair.newBuilder();
1499       aBuilder.setFirst(ByteStringer.wrap(e.getKey().get()));
1500       aBuilder.setSecond(ByteStringer.wrap(e.getValue().get()));
1501       builder.addAttributes(aBuilder.build());
1502     }
1503     for (Map.Entry<String, String> e : this.configuration.entrySet()) {
1504       NameStringPair.Builder aBuilder = NameStringPair.newBuilder();
1505       aBuilder.setName(e.getKey());
1506       aBuilder.setValue(e.getValue());
1507       builder.addConfiguration(aBuilder.build());
1508     }
1509     return builder.build();
1510   }
1511 
1512   /**
1513    * Getter for accessing the configuration value by key.
1514    */
1515   public String getConfigurationValue(String key) {
1516     return configuration.get(key);
1517   }
1518 
1519   /**
1520    * Getter for fetching an unmodifiable {@link #configuration} map.
1521    */
1522   public Map<String, String> getConfiguration() {
1523     // shallow pointer copy
1524     return Collections.unmodifiableMap(configuration);
1525   }
1526 
1527   /**
1528    * Setter for storing a configuration setting in {@link #configuration} map.
1529    * @param key Config key. Same as XML config key e.g. hbase.something.or.other.
1530    * @param value String value. If null, removes the configuration.
1531    */
1532   public HColumnDescriptor setConfiguration(String key, String value) {
1533     if (value == null || value.length() == 0) {
1534       removeConfiguration(key);
1535     } else {
1536       configuration.put(key, value);
1537     }
1538     return this;
1539   }
1540 
1541   /**
1542    * Remove a configuration setting represented by the key from the {@link #configuration} map.
1543    */
1544   public void removeConfiguration(final String key) {
1545     configuration.remove(key);
1546   }
1547 
1548   /**
1549    * Return the encryption algorithm in use by this family
1550    */
1551   public String getEncryptionType() {
1552     return getValue(ENCRYPTION);
1553   }
1554 
1555   /**
1556    * Set the encryption algorithm for use with this family
1557    * @param algorithm
1558    */
1559   public HColumnDescriptor setEncryptionType(String algorithm) {
1560     setValue(ENCRYPTION, algorithm);
1561     return this;
1562   }
1563 
1564   /** Return the raw crypto key attribute for the family, or null if not set  */
1565   public byte[] getEncryptionKey() {
1566     return getValue(Bytes.toBytes(ENCRYPTION_KEY));
1567   }
1568 
1569   /** Set the raw crypto key attribute for the family */
1570   public HColumnDescriptor setEncryptionKey(byte[] keyBytes) {
1571     setValue(Bytes.toBytes(ENCRYPTION_KEY), keyBytes);
1572     return this;
1573   }
1574 
1575   /**
1576    * @return replication factor set for this CF or {@link #DEFAULT_DFS_REPLICATION} if not set.
1577    *         <p>
1578    *         {@link #DEFAULT_DFS_REPLICATION} value indicates that user has explicitly not set any
1579    *         block replication factor for this CF, hence use the default replication factor set in
1580    *         the file system.
1581    */
1582   public short getDFSReplication() {
1583     String rf = getValue(DFS_REPLICATION);
1584     return rf == null ? DEFAULT_DFS_REPLICATION : Short.parseShort(rf);
1585   }
1586 
1587   /**
1588    * Set the replication factor to hfile(s) belonging to this family
1589    * @param replication number of replicas the blocks(s) belonging to this CF should have, or
1590    *          {@link #DEFAULT_DFS_REPLICATION} for the default replication factor set in the
1591    *          filesystem
1592    * @return this (for chained invocation)
1593    */
1594   public HColumnDescriptor setDFSReplication(short replication) {
1595     if (replication < 1 && replication != DEFAULT_DFS_REPLICATION) {
1596       throw new IllegalArgumentException(
1597           "DFS replication factor cannot be less than 1 if explicitly set.");
1598     }
1599     setValue(DFS_REPLICATION, Short.toString(replication));
1600     return this;
1601   }
1602 
1603   /**
1604    * Return the storage policy in use by this family
1605    * <p/>
1606    * Not using {@code enum} here because HDFS is not using {@code enum} for storage policy, see
1607    * org.apache.hadoop.hdfs.server.blockmanagement.BlockStoragePolicySuite for more details
1608    */
1609   public String getStoragePolicy() {
1610     return getValue(STORAGE_POLICY);
1611   }
1612 
1613   /**
1614    * Set the storage policy for use with this family
1615    * @param policy the policy to set, valid setting includes: <i>"LAZY_PERSIST"</i>,
1616    *          <i>"ALL_SSD"</i>, <i>"ONE_SSD"</i>, <i>"HOT"</i>, <i>"WARM"</i>, <i>"COLD"</i>
1617    */
1618   public HColumnDescriptor setStoragePolicy(String policy) {
1619     setValue(STORAGE_POLICY, policy);
1620     return this;
1621   }
1622 }