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