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