View Javadoc

1   /**
2    * Copyright 2007 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase;
21  
22  import java.io.DataInput;
23  import java.io.DataOutput;
24  import java.io.IOException;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Map;
28  
29  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
30  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
31  import org.apache.hadoop.hbase.io.hfile.Compression;
32  import org.apache.hadoop.hbase.io.hfile.HFile;
33  import org.apache.hadoop.hbase.regionserver.StoreFile;
34  import org.apache.hadoop.hbase.regionserver.StoreFile.BloomType;
35  import org.apache.hadoop.hbase.regionserver.wal.HLog;
36  import org.apache.hadoop.hbase.util.Bytes;
37  import org.apache.hadoop.io.Text;
38  import org.apache.hadoop.io.WritableComparable;
39  
40  /**
41   * An HColumnDescriptor contains information about a column family such as the
42   * number of versions, compression settings, etc.
43   *
44   * It is used as input when creating a table or adding a column. Once set, the
45   * parameters that specify a column cannot be changed without deleting the
46   * column and recreating it. If there is data stored in the column, it will be
47   * deleted when the column is deleted.
48   */
49  public class HColumnDescriptor implements WritableComparable<HColumnDescriptor> {
50    // For future backward compatibility
51  
52    // Version 3 was when column names become byte arrays and when we picked up
53    // Time-to-live feature.  Version 4 was when we moved to byte arrays, HBASE-82.
54    // Version 5 was when bloom filter descriptors were removed.
55    // Version 6 adds metadata as a map where keys and values are byte[].
56    // Version 7 -- add new compression and hfile blocksize to HColumnDescriptor (HBASE-1217)
57    // Version 8 -- reintroduction of bloom filters, changed from boolean to enum
58    // Version 9 -- add data block encoding
59    private static final byte COLUMN_DESCRIPTOR_VERSION = (byte) 9;
60  
61    // These constants are used as FileInfo keys
62    public static final String COMPRESSION = "COMPRESSION";
63    public static final String COMPRESSION_COMPACT = "COMPRESSION_COMPACT";
64    public static final String ENCODE_ON_DISK =
65        "ENCODE_ON_DISK";
66    public static final String DATA_BLOCK_ENCODING =
67        "DATA_BLOCK_ENCODING";
68    public static final String BLOCKCACHE = "BLOCKCACHE";
69    public static final String CACHE_DATA_ON_WRITE = "CACHE_DATA_ON_WRITE";
70    public static final String CACHE_INDEX_ON_WRITE = "CACHE_INDEX_ON_WRITE";
71    public static final String CACHE_BLOOMS_ON_WRITE = "CACHE_BLOOMS_ON_WRITE";
72    public static final String EVICT_BLOCKS_ON_CLOSE = "EVICT_BLOCKS_ON_CLOSE";
73  
74    /**
75     * Size of storefile/hfile 'blocks'.  Default is {@link #DEFAULT_BLOCKSIZE}.
76     * Use smaller block sizes for faster random-access at expense of larger
77     * indices (more memory consumption).
78     */
79    public static final String BLOCKSIZE = "BLOCKSIZE";
80  
81    public static final String LENGTH = "LENGTH";
82    public static final String TTL = "TTL";
83    public static final String BLOOMFILTER = "BLOOMFILTER";
84    public static final String FOREVER = "FOREVER";
85    public static final String REPLICATION_SCOPE = "REPLICATION_SCOPE";
86    public static final String MIN_VERSIONS = "MIN_VERSIONS";
87    public static final String KEEP_DELETED_CELLS = "KEEP_DELETED_CELLS";
88  
89    /**
90     * Default compression type.
91     */
92    public static final String DEFAULT_COMPRESSION =
93      Compression.Algorithm.NONE.getName();
94    
95    /**
96     * Default value of the flag that enables data block encoding on disk, as
97     * opposed to encoding in cache only. We encode blocks everywhere by default,
98     * as long as {@link #DATA_BLOCK_ENCODING} is not NONE.
99     */
100   public static final boolean DEFAULT_ENCODE_ON_DISK = true;
101 
102   /** Default data block encoding algorithm. */
103   public static final String DEFAULT_DATA_BLOCK_ENCODING =
104       DataBlockEncoding.NONE.toString();
105 
106   /**
107    * Default number of versions of a record to keep.
108    */
109   public static final int DEFAULT_VERSIONS = 3;
110 
111   /**
112    * Default is not to keep a minimum of versions.
113    */
114   public static final int DEFAULT_MIN_VERSIONS = 0;
115 
116   /*
117    * Cache here the HCD value.
118    * Question: its OK to cache since when we're reenable, we create a new HCD?
119    */
120   private volatile Integer blocksize = null;
121 
122   /**
123    * Default setting for whether to serve from memory or not.
124    */
125   public static final boolean DEFAULT_IN_MEMORY = false;
126 
127   /**
128    * Default setting for preventing deleted from being collected immediately.
129    */
130   public static final boolean DEFAULT_KEEP_DELETED = false;
131 
132   /**
133    * Default setting for whether to use a block cache or not.
134    */
135   public static final boolean DEFAULT_BLOCKCACHE = true;
136 
137   /**
138    * Default setting for whether to cache data blocks on write if block caching
139    * is enabled.
140    */
141   public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
142   
143   /**
144    * Default setting for whether to cache index blocks on write if block
145    * caching is enabled.
146    */
147   public static final boolean DEFAULT_CACHE_INDEX_ON_WRITE = false;
148 
149   /**
150    * Default size of blocks in files stored to the filesytem (hfiles).
151    */
152   public static final int DEFAULT_BLOCKSIZE = HFile.DEFAULT_BLOCKSIZE;
153 
154   /**
155    * Default setting for whether or not to use bloomfilters.
156    */
157   public static final String DEFAULT_BLOOMFILTER = StoreFile.BloomType.NONE.toString();
158 
159   /**
160    * Default setting for whether to cache bloom filter blocks on write if block
161    * caching is enabled.
162    */
163   public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
164 
165   /**
166    * Default time to live of cell contents.
167    */
168   public static final int DEFAULT_TTL = HConstants.FOREVER;
169 
170   /**
171    * Default scope.
172    */
173   public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
174 
175   /**
176    * Default setting for whether to evict cached blocks from the blockcache on
177    * close.
178    */
179   public static final boolean DEFAULT_EVICT_BLOCKS_ON_CLOSE = false;
180 
181   private final static Map<String, String> DEFAULT_VALUES = new HashMap<String, String>();
182   static {
183       DEFAULT_VALUES.put(BLOOMFILTER, DEFAULT_BLOOMFILTER);
184       DEFAULT_VALUES.put(REPLICATION_SCOPE, String.valueOf(DEFAULT_REPLICATION_SCOPE));
185       DEFAULT_VALUES.put(HConstants.VERSIONS, String.valueOf(DEFAULT_VERSIONS));
186       DEFAULT_VALUES.put(MIN_VERSIONS, String.valueOf(DEFAULT_MIN_VERSIONS));
187       DEFAULT_VALUES.put(COMPRESSION, DEFAULT_COMPRESSION);
188       DEFAULT_VALUES.put(TTL, String.valueOf(DEFAULT_TTL));
189       DEFAULT_VALUES.put(BLOCKSIZE, String.valueOf(DEFAULT_BLOCKSIZE));
190       DEFAULT_VALUES.put(HConstants.IN_MEMORY, String.valueOf(DEFAULT_IN_MEMORY));
191       DEFAULT_VALUES.put(BLOCKCACHE, String.valueOf(DEFAULT_BLOCKCACHE));
192       DEFAULT_VALUES.put(KEEP_DELETED_CELLS, String.valueOf(DEFAULT_KEEP_DELETED));
193       DEFAULT_VALUES.put(ENCODE_ON_DISK,
194           String.valueOf(DEFAULT_ENCODE_ON_DISK));
195       DEFAULT_VALUES.put(DATA_BLOCK_ENCODING,
196           String.valueOf(DEFAULT_DATA_BLOCK_ENCODING));
197       DEFAULT_VALUES.put(CACHE_DATA_ON_WRITE,
198           String.valueOf(DEFAULT_CACHE_DATA_ON_WRITE));
199       DEFAULT_VALUES.put(CACHE_INDEX_ON_WRITE,
200           String.valueOf(DEFAULT_CACHE_INDEX_ON_WRITE));
201       DEFAULT_VALUES.put(CACHE_BLOOMS_ON_WRITE,
202           String.valueOf(DEFAULT_CACHE_BLOOMS_ON_WRITE));
203       DEFAULT_VALUES.put(EVICT_BLOCKS_ON_CLOSE,
204           String.valueOf(DEFAULT_EVICT_BLOCKS_ON_CLOSE));
205   }
206 
207   // Column family name
208   private byte [] name;
209 
210   // Column metadata
211   protected Map<ImmutableBytesWritable,ImmutableBytesWritable> values =
212     new HashMap<ImmutableBytesWritable,ImmutableBytesWritable>();
213 
214   /*
215    * Cache the max versions rather than calculate it every time.
216    */
217   private int cachedMaxVersions = -1;
218 
219   /**
220    * Default constructor. Must be present for Writable.
221    */
222   public HColumnDescriptor() {
223     this.name = null;
224   }
225 
226   /**
227    * Construct a column descriptor specifying only the family name
228    * The other attributes are defaulted.
229    *
230    * @param familyName Column family name. Must be 'printable' -- digit or
231    * letter -- and may not contain a <code>:<code>
232    */
233   public HColumnDescriptor(final String familyName) {
234     this(Bytes.toBytes(familyName));
235   }
236 
237   /**
238    * Construct a column descriptor specifying only the family name
239    * The other attributes are defaulted.
240    *
241    * @param familyName Column family name. Must be 'printable' -- digit or
242    * letter -- and may not contain a <code>:<code>
243    */
244   public HColumnDescriptor(final byte [] familyName) {
245     this (familyName == null || familyName.length <= 0?
246       HConstants.EMPTY_BYTE_ARRAY: familyName, DEFAULT_VERSIONS,
247       DEFAULT_COMPRESSION, DEFAULT_IN_MEMORY, DEFAULT_BLOCKCACHE,
248       DEFAULT_TTL, DEFAULT_BLOOMFILTER);
249   }
250 
251   /**
252    * Constructor.
253    * Makes a deep copy of the supplied descriptor.
254    * Can make a modifiable descriptor from an UnmodifyableHColumnDescriptor.
255    * @param desc The descriptor.
256    */
257   public HColumnDescriptor(HColumnDescriptor desc) {
258     super();
259     this.name = desc.name.clone();
260     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
261         desc.values.entrySet()) {
262       this.values.put(e.getKey(), e.getValue());
263     }
264     setMaxVersions(desc.getMaxVersions());
265   }
266 
267   /**
268    * Constructor
269    * @param familyName Column family name. Must be 'printable' -- digit or
270    * letter -- and may not contain a <code>:<code>
271    * @param maxVersions Maximum number of versions to keep
272    * @param compression Compression type
273    * @param inMemory If true, column data should be kept in an HRegionServer's
274    * cache
275    * @param blockCacheEnabled If true, MapFile blocks should be cached
276    * @param timeToLive Time-to-live of cell contents, in seconds
277    * (use HConstants.FOREVER for unlimited TTL)
278    * @param bloomFilter Bloom filter type for this column
279    *
280    * @throws IllegalArgumentException if passed a family name that is made of
281    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
282    * a <code>:</code>
283    * @throws IllegalArgumentException if the number of versions is &lt;= 0
284    * @deprecated use {@link #HColumnDescriptor(String)} and setters
285    */
286   @Deprecated
287   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
288       final String compression, final boolean inMemory,
289       final boolean blockCacheEnabled,
290       final int timeToLive, final String bloomFilter) {
291     this(familyName, maxVersions, compression, inMemory, blockCacheEnabled,
292       DEFAULT_BLOCKSIZE, timeToLive, bloomFilter, DEFAULT_REPLICATION_SCOPE);
293   }
294 
295   /**
296    * Constructor
297    * @param familyName Column family name. Must be 'printable' -- digit or
298    * letter -- and may not contain a <code>:<code>
299    * @param maxVersions Maximum number of versions to keep
300    * @param compression Compression type
301    * @param inMemory If true, column data should be kept in an HRegionServer's
302    * cache
303    * @param blockCacheEnabled If true, MapFile blocks should be cached
304    * @param blocksize Block size to use when writing out storefiles.  Use
305    * smaller block sizes for faster random-access at expense of larger indices
306    * (more memory consumption).  Default is usually 64k.
307    * @param timeToLive Time-to-live of cell contents, in seconds
308    * (use HConstants.FOREVER for unlimited TTL)
309    * @param bloomFilter Bloom filter type for this column
310    * @param scope The scope tag for this column
311    *
312    * @throws IllegalArgumentException if passed a family name that is made of
313    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
314    * a <code>:</code>
315    * @throws IllegalArgumentException if the number of versions is &lt;= 0
316    * @deprecated use {@link #HColumnDescriptor(String)} and setters
317    */
318   @Deprecated
319   public HColumnDescriptor(final byte [] familyName, final int maxVersions,
320       final String compression, final boolean inMemory,
321       final boolean blockCacheEnabled, final int blocksize,
322       final int timeToLive, final String bloomFilter, final int scope) {
323     this(familyName, DEFAULT_MIN_VERSIONS, maxVersions, DEFAULT_KEEP_DELETED,
324         compression, DEFAULT_ENCODE_ON_DISK, DEFAULT_DATA_BLOCK_ENCODING,
325         inMemory, blockCacheEnabled, blocksize, timeToLive, bloomFilter,
326         scope);
327   }
328 
329   /**
330    * Constructor
331    * @param familyName Column family name. Must be 'printable' -- digit or
332    * letter -- and may not contain a <code>:<code>
333    * @param minVersions Minimum number of versions to keep
334    * @param maxVersions Maximum number of versions to keep
335    * @param keepDeletedCells Whether to retain deleted cells until they expire
336    *        up to maxVersions versions.
337    * @param compression Compression type
338    * @param encodeOnDisk whether to use the specified data block encoding
339    *        on disk. If false, the encoding will be used in cache only.
340    * @param dataBlockEncoding data block encoding
341    * @param inMemory If true, column data should be kept in an HRegionServer's
342    * cache
343    * @param blockCacheEnabled If true, MapFile blocks should be cached
344    * @param blocksize Block size to use when writing out storefiles.  Use
345    * smaller blocksizes for faster random-access at expense of larger indices
346    * (more memory consumption).  Default is usually 64k.
347    * @param timeToLive Time-to-live of cell contents, in seconds
348    * (use HConstants.FOREVER for unlimited TTL)
349    * @param bloomFilter Bloom filter type for this column
350    * @param scope The scope tag for this column
351    *
352    * @throws IllegalArgumentException if passed a family name that is made of
353    * other than 'word' characters: i.e. <code>[a-zA-Z_0-9]</code> or contains
354    * a <code>:</code>
355    * @throws IllegalArgumentException if the number of versions is &lt;= 0
356    * @deprecated use {@link #HColumnDescriptor(String)} and setters
357    */
358   @Deprecated
359   public HColumnDescriptor(final byte[] familyName, final int minVersions,
360       final int maxVersions, final boolean keepDeletedCells,
361       final String compression, final boolean encodeOnDisk,
362       final String dataBlockEncoding, final boolean inMemory,
363       final boolean blockCacheEnabled, final int blocksize,
364       final int timeToLive, final String bloomFilter, final int scope) {
365     isLegalFamilyName(familyName);
366     this.name = familyName;
367 
368     if (maxVersions <= 0) {
369       // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
370       // Until there is support, consider 0 or < 0 -- a configuration error.
371       throw new IllegalArgumentException("Maximum versions must be positive");
372     }
373 
374     if (minVersions > 0) {
375       if (timeToLive == HConstants.FOREVER) {
376         throw new IllegalArgumentException("Minimum versions requires TTL.");
377       }
378       if (minVersions >= maxVersions) {
379         throw new IllegalArgumentException("Minimum versions must be < "
380             + "maximum versions.");
381       }
382     }
383 
384     setMaxVersions(maxVersions);
385     setMinVersions(minVersions);
386     setKeepDeletedCells(keepDeletedCells);
387     setInMemory(inMemory);
388     setBlockCacheEnabled(blockCacheEnabled);
389     setTimeToLive(timeToLive);
390     setCompressionType(Compression.Algorithm.
391       valueOf(compression.toUpperCase()));
392     setEncodeOnDisk(encodeOnDisk);
393     setDataBlockEncoding(DataBlockEncoding.
394         valueOf(dataBlockEncoding.toUpperCase()));
395     setBloomFilterType(StoreFile.BloomType.
396       valueOf(bloomFilter.toUpperCase()));
397     setBlocksize(blocksize);
398     setScope(scope);
399   }
400 
401   /**
402    * @param b Family name.
403    * @return <code>b</code>
404    * @throws IllegalArgumentException If not null and not a legitimate family
405    * name: i.e. 'printable' and ends in a ':' (Null passes are allowed because
406    * <code>b</code> can be null when deserializing).  Cannot start with a '.'
407    * either. Also Family can not be an empty value or equal "recovered.edits".
408    */
409   public static byte [] isLegalFamilyName(final byte [] b) {
410     if (b == null) {
411       return b;
412     }
413     if (b[0] == '.') {
414       throw new IllegalArgumentException("Family names cannot start with a " +
415         "period: " + Bytes.toString(b));
416     }
417     for (int i = 0; i < b.length; i++) {
418       if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
419         throw new IllegalArgumentException("Illegal character <" + b[i] +
420           ">. Family names cannot contain control characters or colons: " +
421           Bytes.toString(b));
422       }
423     }
424     byte[] recoveredEdit = Bytes.toBytes(HLog.RECOVERED_EDITS_DIR);
425     if (Bytes.equals(recoveredEdit, b)) {
426       throw new IllegalArgumentException("Family name cannot be: " +
427           HLog.RECOVERED_EDITS_DIR);
428     }
429     return b;
430   }
431 
432   /**
433    * @return Name of this column family
434    */
435   public byte [] getName() {
436     return name;
437   }
438 
439   /**
440    * @return Name of this column family
441    */
442   public String getNameAsString() {
443     return Bytes.toString(this.name);
444   }
445 
446   /**
447    * @param key The key.
448    * @return The value.
449    */
450   public byte[] getValue(byte[] key) {
451     ImmutableBytesWritable ibw = values.get(new ImmutableBytesWritable(key));
452     if (ibw == null)
453       return null;
454     return ibw.get();
455   }
456 
457   /**
458    * @param key The key.
459    * @return The value as a string.
460    */
461   public String getValue(String key) {
462     byte[] value = getValue(Bytes.toBytes(key));
463     if (value == null)
464       return null;
465     return Bytes.toString(value);
466   }
467 
468   /**
469    * @return All values.
470    */
471   public Map<ImmutableBytesWritable,ImmutableBytesWritable> getValues() {
472     return Collections.unmodifiableMap(values);
473   }
474 
475   /**
476    * @param key The key.
477    * @param value The value.
478    * @return this (for chained invocation)
479    */
480   public HColumnDescriptor setValue(byte[] key, byte[] value) {
481     values.put(new ImmutableBytesWritable(key),
482       new ImmutableBytesWritable(value));
483     return this;
484   }
485 
486   /**
487    * @param key Key whose key and value we're to remove from HCD parameters.
488    */
489   public void remove(final byte [] key) {
490     values.remove(new ImmutableBytesWritable(key));
491   }
492 
493   /**
494    * @param key The key.
495    * @param value The value.
496    * @return this (for chained invocation)
497    */
498   public HColumnDescriptor setValue(String key, String value) {
499     setValue(Bytes.toBytes(key), Bytes.toBytes(value));
500     return this;
501   }
502 
503   /** @return compression type being used for the column family */
504   public Compression.Algorithm getCompression() {
505     String n = getValue(COMPRESSION);
506     if (n == null) {
507       return Compression.Algorithm.NONE;
508     }
509     return Compression.Algorithm.valueOf(n.toUpperCase());
510   }
511 
512   /** @return compression type being used for the column family for major 
513       compression */
514   public Compression.Algorithm getCompactionCompression() {
515     String n = getValue(COMPRESSION_COMPACT);
516     if (n == null) {
517       return getCompression();
518     }
519     return Compression.Algorithm.valueOf(n.toUpperCase());
520   }
521 
522   /** @return maximum number of versions */
523   public int getMaxVersions() {
524     return this.cachedMaxVersions;
525   }
526 
527   /**
528    * @param maxVersions maximum number of versions
529    * @return this (for chained invocation)
530    */
531   public HColumnDescriptor setMaxVersions(int maxVersions) {
532     setValue(HConstants.VERSIONS, Integer.toString(maxVersions));
533     cachedMaxVersions = maxVersions;
534     return this;
535   }
536 
537   /**
538    * @return The storefile/hfile blocksize for this column family.
539    */
540   public synchronized int getBlocksize() {
541     if (this.blocksize == null) {
542       String value = getValue(BLOCKSIZE);
543       this.blocksize = (value != null)?
544         Integer.decode(value): Integer.valueOf(DEFAULT_BLOCKSIZE);
545     }
546     return this.blocksize.intValue();
547   }
548 
549   /**
550    * @param s Blocksize to use when writing out storefiles/hfiles on this
551    * column family.
552    * @return this (for chained invocation)
553    */
554   public HColumnDescriptor setBlocksize(int s) {
555     setValue(BLOCKSIZE, Integer.toString(s));
556     this.blocksize = null;
557     return this;
558   }
559 
560   /**
561    * @return Compression type setting.
562    */
563   public Compression.Algorithm getCompressionType() {
564     return getCompression();
565   }
566 
567   /**
568    * Compression types supported in hbase.
569    * LZO is not bundled as part of the hbase distribution.
570    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
571    * for how to enable it.
572    * @param type Compression type setting.
573    * @return this (for chained invocation)
574    */
575   public HColumnDescriptor setCompressionType(Compression.Algorithm type) {
576     return setValue(COMPRESSION, type.getName().toUpperCase());
577   }
578 
579   /** @return data block encoding algorithm used on disk */
580   public DataBlockEncoding getDataBlockEncodingOnDisk() {
581     String encodeOnDiskStr = getValue(ENCODE_ON_DISK);
582     boolean encodeOnDisk;
583     if (encodeOnDiskStr == null) {
584       encodeOnDisk = DEFAULT_ENCODE_ON_DISK;
585     } else {
586       encodeOnDisk = Boolean.valueOf(encodeOnDiskStr);
587     }
588 
589     if (!encodeOnDisk) {
590       // No encoding on disk.
591       return DataBlockEncoding.NONE;
592     }
593     return getDataBlockEncoding();
594   }
595 
596   /**
597    * Set the flag indicating that we only want to encode data block in cache
598    * but not on disk.
599    * @return this (for chained invocation)
600    */
601   public HColumnDescriptor setEncodeOnDisk(boolean encodeOnDisk) {
602     return setValue(ENCODE_ON_DISK, String.valueOf(encodeOnDisk));
603   }
604 
605   /**
606    * @return the data block encoding algorithm used in block cache and
607    *         optionally on disk
608    */
609   public DataBlockEncoding getDataBlockEncoding() {
610     String type = getValue(DATA_BLOCK_ENCODING);
611     if (type == null) {
612       type = DEFAULT_DATA_BLOCK_ENCODING;
613     }
614     return DataBlockEncoding.valueOf(type);
615   }
616 
617   /**
618    * Set data block encoding algorithm used in block cache.
619    * @param type What kind of data block encoding will be used.
620    * @return this (for chained invocation)
621    */
622   public HColumnDescriptor setDataBlockEncoding(DataBlockEncoding type) {
623     String name;
624     if (type != null) {
625       name = type.toString();
626     } else {
627       name = DataBlockEncoding.NONE.toString();
628     }
629     return setValue(DATA_BLOCK_ENCODING, name);
630   }
631 
632   /**
633    * @return Compression type setting.
634    */
635   public Compression.Algorithm getCompactionCompressionType() {
636     return getCompactionCompression();
637   }
638 
639   /**
640    * Compression types supported in hbase.
641    * LZO is not bundled as part of the hbase distribution.
642    * See <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO Compression</a>
643    * for how to enable it.
644    * @param type Compression type setting.
645    * @return this (for chained invocation)
646    */
647   public HColumnDescriptor setCompactionCompressionType(
648       Compression.Algorithm type) {
649     return setValue(COMPRESSION_COMPACT, type.getName().toUpperCase());
650   }
651 
652   /**
653    * @return True if we are to keep all in use HRegionServer cache.
654    */
655   public boolean isInMemory() {
656     String value = getValue(HConstants.IN_MEMORY);
657     if (value != null)
658       return Boolean.valueOf(value).booleanValue();
659     return DEFAULT_IN_MEMORY;
660   }
661 
662   /**
663    * @param inMemory True if we are to keep all values in the HRegionServer
664    * cache
665    * @return this (for chained invocation)
666    */
667   public HColumnDescriptor setInMemory(boolean inMemory) {
668     return setValue(HConstants.IN_MEMORY, Boolean.toString(inMemory));
669   }
670 
671   public boolean getKeepDeletedCells() {
672     String value = getValue(KEEP_DELETED_CELLS);
673     if (value != null) {
674       return Boolean.valueOf(value).booleanValue();
675     }
676     return DEFAULT_KEEP_DELETED;
677   }
678 
679   /**
680    * @param keepDeletedCells True if deleted rows should not be collected
681    * immediately.
682    * @return this (for chained invocation)
683    */
684   public HColumnDescriptor setKeepDeletedCells(boolean keepDeletedCells) {
685     return setValue(KEEP_DELETED_CELLS, Boolean.toString(keepDeletedCells));
686   }
687 
688   /**
689    * @return Time-to-live of cell contents, in seconds.
690    */
691   public int getTimeToLive() {
692     String value = getValue(TTL);
693     return (value != null)? Integer.valueOf(value).intValue(): DEFAULT_TTL;
694   }
695 
696   /**
697    * @param timeToLive Time-to-live of cell contents, in seconds.
698    * @return this (for chained invocation)
699    */
700   public HColumnDescriptor setTimeToLive(int timeToLive) {
701     return setValue(TTL, Integer.toString(timeToLive));
702   }
703 
704   /**
705    * @return The minimum number of versions to keep.
706    */
707   public int getMinVersions() {
708     String value = getValue(MIN_VERSIONS);
709     return (value != null)? Integer.valueOf(value).intValue(): 0;
710   }
711 
712   /**
713    * @param minVersions The minimum number of versions to keep.
714    * (used when timeToLive is set)
715    * @return this (for chained invocation)
716    */
717   public HColumnDescriptor setMinVersions(int minVersions) {
718     return setValue(MIN_VERSIONS, Integer.toString(minVersions));
719   }
720 
721   /**
722    * @return True if MapFile blocks should be cached.
723    */
724   public boolean isBlockCacheEnabled() {
725     String value = getValue(BLOCKCACHE);
726     if (value != null)
727       return Boolean.valueOf(value).booleanValue();
728     return DEFAULT_BLOCKCACHE;
729   }
730 
731   /**
732    * @param blockCacheEnabled True if MapFile blocks should be cached.
733    * @return this (for chained invocation)
734    */
735   public HColumnDescriptor setBlockCacheEnabled(boolean blockCacheEnabled) {
736     return setValue(BLOCKCACHE, Boolean.toString(blockCacheEnabled));
737   }
738 
739   /**
740    * @return bloom filter type used for new StoreFiles in ColumnFamily
741    */
742   public StoreFile.BloomType getBloomFilterType() {
743     String n = getValue(BLOOMFILTER);
744     if (n == null) {
745       n = DEFAULT_BLOOMFILTER;
746     }
747     return StoreFile.BloomType.valueOf(n.toUpperCase());
748   }
749 
750   /**
751    * @param bt bloom filter type
752    * @return this (for chained invocation)
753    */
754   public HColumnDescriptor setBloomFilterType(final StoreFile.BloomType bt) {
755     return setValue(BLOOMFILTER, bt.toString());
756   }
757 
758    /**
759     * @return the scope tag
760     */
761   public int getScope() {
762     String value = getValue(REPLICATION_SCOPE);
763     if (value != null) {
764       return Integer.valueOf(value).intValue();
765     }
766     return DEFAULT_REPLICATION_SCOPE;
767   }
768 
769  /**
770   * @param scope the scope tag
771   * @return this (for chained invocation)
772   */
773   public HColumnDescriptor setScope(int scope) {
774     return setValue(REPLICATION_SCOPE, Integer.toString(scope));
775   }
776 
777   /**
778    * @return true if we should cache data blocks on write
779    */
780   public boolean shouldCacheDataOnWrite() {
781     String value = getValue(CACHE_DATA_ON_WRITE);
782     if (value != null) {
783       return Boolean.valueOf(value).booleanValue();
784     }
785     return DEFAULT_CACHE_DATA_ON_WRITE;
786   }
787 
788   /**
789    * @param value true if we should cache data blocks on write
790    * @return this (for chained invocation)
791    */
792   public HColumnDescriptor setCacheDataOnWrite(boolean value) {
793     return setValue(CACHE_DATA_ON_WRITE, Boolean.toString(value));
794   }
795 
796   /**
797    * @return true if we should cache index blocks on write
798    */
799   public boolean shouldCacheIndexesOnWrite() {
800     String value = getValue(CACHE_INDEX_ON_WRITE);
801     if (value != null) {
802       return Boolean.valueOf(value).booleanValue();
803     }
804     return DEFAULT_CACHE_INDEX_ON_WRITE;
805   }
806 
807   /**
808    * @param value true if we should cache index blocks on write
809    * @return this (for chained invocation)
810    */
811   public HColumnDescriptor setCacheIndexesOnWrite(boolean value) {
812     return setValue(CACHE_INDEX_ON_WRITE, Boolean.toString(value));
813   }
814 
815   /**
816    * @return true if we should cache bloomfilter blocks on write
817    */
818   public boolean shouldCacheBloomsOnWrite() {
819     String value = getValue(CACHE_BLOOMS_ON_WRITE);
820     if (value != null) {
821       return Boolean.valueOf(value).booleanValue();
822     }
823     return DEFAULT_CACHE_BLOOMS_ON_WRITE;
824   }
825 
826   /**
827    * @param value true if we should cache bloomfilter blocks on write
828    * @return this (for chained invocation)
829    */
830   public HColumnDescriptor setCacheBloomsOnWrite(boolean value) {
831     return setValue(CACHE_BLOOMS_ON_WRITE, Boolean.toString(value));
832   }
833 
834   /**
835    * @return true if we should evict cached blocks from the blockcache on
836    * close
837    */
838   public boolean shouldEvictBlocksOnClose() {
839     String value = getValue(EVICT_BLOCKS_ON_CLOSE);
840     if (value != null) {
841       return Boolean.valueOf(value).booleanValue();
842     }
843     return DEFAULT_EVICT_BLOCKS_ON_CLOSE;
844   }
845 
846   /**
847    * @param value true if we should evict cached blocks from the blockcache on
848    * close
849    * @return this (for chained invocation)
850    */
851   public HColumnDescriptor setEvictBlocksOnClose(boolean value) {
852     return setValue(EVICT_BLOCKS_ON_CLOSE, Boolean.toString(value));
853   }
854 
855   /**
856    * @see java.lang.Object#toString()
857    */
858   @Override
859   public String toString() {
860     StringBuilder s = new StringBuilder();
861     s.append('{');
862     s.append(HConstants.NAME);
863     s.append(" => '");
864     s.append(Bytes.toString(name));
865     s.append("'");
866     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
867         values.entrySet()) {
868       String key = Bytes.toString(e.getKey().get());
869       String value = Bytes.toString(e.getValue().get());
870       s.append(", ");
871       s.append(key);
872       s.append(" => '");
873       s.append(value);
874       s.append("'");
875     }
876     s.append('}');
877     return s.toString();
878   }
879 
880   /**
881    * @return Column family descriptor with only the customized attributes.
882    */
883   public String toStringCustomizedValues() {
884     StringBuilder s = new StringBuilder();
885     s.append('{');
886     s.append(HConstants.NAME);
887     s.append(" => '");
888     s.append(Bytes.toString(name));
889     s.append("'");
890     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
891         values.entrySet()) {
892       String key = Bytes.toString(e.getKey().get());
893       String value = Bytes.toString(e.getValue().get());
894       if(DEFAULT_VALUES.get(key) == null || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
895         s.append(", ");
896         s.append(key);
897         s.append(" => '");
898         s.append(value);
899         s.append("'");
900       }
901     }
902     s.append('}');
903     return s.toString();
904   }
905 
906   /**
907    * @see java.lang.Object#equals(java.lang.Object)
908    */
909   @Override
910   public boolean equals(Object obj) {
911     if (this == obj) {
912       return true;
913     }
914     if (obj == null) {
915       return false;
916     }
917     if (!(obj instanceof HColumnDescriptor)) {
918       return false;
919     }
920     return compareTo((HColumnDescriptor)obj) == 0;
921   }
922 
923   /**
924    * @see java.lang.Object#hashCode()
925    */
926   @Override
927   public int hashCode() {
928     int result = Bytes.hashCode(this.name);
929     result ^= Byte.valueOf(COLUMN_DESCRIPTOR_VERSION).hashCode();
930     result ^= values.hashCode();
931     return result;
932   }
933 
934   // Writable
935 
936   public void readFields(DataInput in) throws IOException {
937     int version = in.readByte();
938     if (version < 6) {
939       if (version <= 2) {
940         Text t = new Text();
941         t.readFields(in);
942         this.name = t.getBytes();
943 //        if(KeyValue.getFamilyDelimiterIndex(this.name, 0, this.name.length)
944 //            > 0) {
945 //          this.name = stripColon(this.name);
946 //        }
947       } else {
948         this.name = Bytes.readByteArray(in);
949       }
950       this.values.clear();
951       setMaxVersions(in.readInt());
952       int ordinal = in.readInt();
953       setCompressionType(Compression.Algorithm.values()[ordinal]);
954       setInMemory(in.readBoolean());
955       setBloomFilterType(in.readBoolean() ? BloomType.ROW : BloomType.NONE);
956       if (getBloomFilterType() != BloomType.NONE && version < 5) {
957         // If a bloomFilter is enabled and the column descriptor is less than
958         // version 5, we need to skip over it to read the rest of the column
959         // descriptor. There are no BloomFilterDescriptors written to disk for
960         // column descriptors with a version number >= 5
961         throw new UnsupportedClassVersionError(this.getClass().getName() +
962             " does not support backward compatibility with versions older " +
963             "than version 5");
964       }
965       if (version > 1) {
966         setBlockCacheEnabled(in.readBoolean());
967       }
968       if (version > 2) {
969        setTimeToLive(in.readInt());
970       }
971     } else {
972       // version 6+
973       this.name = Bytes.readByteArray(in);
974       this.values.clear();
975       int numValues = in.readInt();
976       for (int i = 0; i < numValues; i++) {
977         ImmutableBytesWritable key = new ImmutableBytesWritable();
978         ImmutableBytesWritable value = new ImmutableBytesWritable();
979         key.readFields(in);
980         value.readFields(in);
981 
982         // in version 8, the BloomFilter setting changed from bool to enum
983         if (version < 8 && Bytes.toString(key.get()).equals(BLOOMFILTER)) {
984           value.set(Bytes.toBytes(
985               Boolean.getBoolean(Bytes.toString(value.get()))
986                 ? BloomType.ROW.toString()
987                 : BloomType.NONE.toString()));
988         }
989 
990         values.put(key, value);
991       }
992       if (version == 6) {
993         // Convert old values.
994         setValue(COMPRESSION, Compression.Algorithm.NONE.getName());
995       }
996       String value = getValue(HConstants.VERSIONS);
997       this.cachedMaxVersions = (value != null)?
998           Integer.valueOf(value).intValue(): DEFAULT_VERSIONS;
999     }
1000   }
1001 
1002   public void write(DataOutput out) throws IOException {
1003     out.writeByte(COLUMN_DESCRIPTOR_VERSION);
1004     Bytes.writeByteArray(out, this.name);
1005     out.writeInt(values.size());
1006     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
1007         values.entrySet()) {
1008       e.getKey().write(out);
1009       e.getValue().write(out);
1010     }
1011   }
1012 
1013   // Comparable
1014 
1015   public int compareTo(HColumnDescriptor o) {
1016     int result = Bytes.compareTo(this.name, o.getName());
1017     if (result == 0) {
1018       // punt on comparison for ordering, just calculate difference
1019       result = this.values.hashCode() - o.values.hashCode();
1020       if (result < 0)
1021         result = -1;
1022       else if (result > 0)
1023         result = 1;
1024     }
1025     return result;
1026   }
1027 }