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