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