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