001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
021import java.io.IOException;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Map;
026import java.util.Set;
027import java.util.function.Function;
028
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.KeepDeletedCells;
031import org.apache.hadoop.hbase.MemoryCompactionPolicy;
032import org.apache.yetus.audience.InterfaceAudience;
033import org.apache.hadoop.hbase.exceptions.DeserializationException;
034import org.apache.hadoop.hbase.exceptions.HBaseException;
035import org.apache.hadoop.hbase.io.compress.Compression;
036import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
037import org.apache.hadoop.hbase.regionserver.BloomType;
038import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
039import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ColumnFamilySchema;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.apache.hadoop.hbase.util.PrettyPrinter;
042import org.apache.hadoop.hbase.util.PrettyPrinter.Unit;
043
044/**
045 * @since 2.0.0
046 */
047@InterfaceAudience.Public
048public class ColumnFamilyDescriptorBuilder {
049  // For future backward compatibility
050
051  // Version  3 was when column names become byte arrays and when we picked up
052  // Time-to-live feature.  Version 4 was when we moved to byte arrays, HBASE-82.
053  // Version  5 was when bloom filter descriptors were removed.
054  // Version  6 adds metadata as a map where keys and values are byte[].
055  // Version  7 -- add new compression and hfile blocksize to HColumnDescriptor (HBASE-1217)
056  // Version  8 -- reintroduction of bloom filters, changed from boolean to enum
057  // Version  9 -- add data block encoding
058  // Version 10 -- change metadata to standard type.
059  // Version 11 -- add column family level configuration.
060  private static final byte COLUMN_DESCRIPTOR_VERSION = (byte) 11;
061
062  @InterfaceAudience.Private
063  public static final String IN_MEMORY_COMPACTION = "IN_MEMORY_COMPACTION";
064  private static final Bytes IN_MEMORY_COMPACTION_BYTES = new Bytes(Bytes.toBytes(IN_MEMORY_COMPACTION));
065
066  @InterfaceAudience.Private
067  public static final String IN_MEMORY = HConstants.IN_MEMORY;
068  private static final Bytes IN_MEMORY_BYTES = new Bytes(Bytes.toBytes(IN_MEMORY));
069
070  // These constants are used as FileInfo keys
071  @InterfaceAudience.Private
072  public static final String COMPRESSION = "COMPRESSION";
073  private static final Bytes COMPRESSION_BYTES = new Bytes(Bytes.toBytes(COMPRESSION));
074  @InterfaceAudience.Private
075  public static final String COMPRESSION_COMPACT = "COMPRESSION_COMPACT";
076  private static final Bytes COMPRESSION_COMPACT_BYTES = new Bytes(Bytes.toBytes(COMPRESSION_COMPACT));
077  @InterfaceAudience.Private
078  public static final String DATA_BLOCK_ENCODING = "DATA_BLOCK_ENCODING";
079  private static final Bytes DATA_BLOCK_ENCODING_BYTES = new Bytes(Bytes.toBytes(DATA_BLOCK_ENCODING));
080  /**
081   * Key for the BLOCKCACHE attribute. A more exact name would be
082   * CACHE_DATA_ON_READ because this flag sets whether or not we cache DATA
083   * blocks. We always cache INDEX and BLOOM blocks; caching these blocks cannot
084   * be disabled.
085   */
086  @InterfaceAudience.Private
087  public static final String BLOCKCACHE = "BLOCKCACHE";
088  private static final Bytes BLOCKCACHE_BYTES = new Bytes(Bytes.toBytes(BLOCKCACHE));
089  @InterfaceAudience.Private
090  public static final String CACHE_DATA_ON_WRITE = "CACHE_DATA_ON_WRITE";
091  private static final Bytes CACHE_DATA_ON_WRITE_BYTES = new Bytes(Bytes.toBytes(CACHE_DATA_ON_WRITE));
092  @InterfaceAudience.Private
093  public static final String CACHE_INDEX_ON_WRITE = "CACHE_INDEX_ON_WRITE";
094  private static final Bytes CACHE_INDEX_ON_WRITE_BYTES = new Bytes(Bytes.toBytes(CACHE_INDEX_ON_WRITE));
095  @InterfaceAudience.Private
096  public static final String CACHE_BLOOMS_ON_WRITE = "CACHE_BLOOMS_ON_WRITE";
097  private static final Bytes CACHE_BLOOMS_ON_WRITE_BYTES = new Bytes(Bytes.toBytes(CACHE_BLOOMS_ON_WRITE));
098  @InterfaceAudience.Private
099  public static final String EVICT_BLOCKS_ON_CLOSE = "EVICT_BLOCKS_ON_CLOSE";
100  private static final Bytes EVICT_BLOCKS_ON_CLOSE_BYTES = new Bytes(Bytes.toBytes(EVICT_BLOCKS_ON_CLOSE));
101
102  /**
103   * Key for the PREFETCH_BLOCKS_ON_OPEN attribute. If set, all INDEX, BLOOM,
104   * and DATA blocks of HFiles belonging to this family will be loaded into the
105   * cache as soon as the file is opened. These loads will not count as cache
106   * misses.
107   */
108  @InterfaceAudience.Private
109  public static final String PREFETCH_BLOCKS_ON_OPEN = "PREFETCH_BLOCKS_ON_OPEN";
110  private static final Bytes PREFETCH_BLOCKS_ON_OPEN_BYTES = new Bytes(Bytes.toBytes(PREFETCH_BLOCKS_ON_OPEN));
111
112  /**
113   * Size of storefile/hfile 'blocks'. Default is {@link #DEFAULT_BLOCKSIZE}.
114   * Use smaller block sizes for faster random-access at expense of larger
115   * indices (more memory consumption). Note that this is a soft limit and that
116   * blocks have overhead (metadata, CRCs) so blocks will tend to be the size
117   * specified here and then some; i.e. don't expect that setting BLOCKSIZE=4k
118   * means hbase data will align with an SSDs 4k page accesses (TODO).
119   */
120  @InterfaceAudience.Private
121  public static final String BLOCKSIZE = "BLOCKSIZE";
122  private static final Bytes BLOCKSIZE_BYTES = new Bytes(Bytes.toBytes(BLOCKSIZE));
123
124  @InterfaceAudience.Private
125  public static final String TTL = "TTL";
126  private static final Bytes TTL_BYTES = new Bytes(Bytes.toBytes(TTL));
127  @InterfaceAudience.Private
128  public static final String BLOOMFILTER = "BLOOMFILTER";
129  private static final Bytes BLOOMFILTER_BYTES = new Bytes(Bytes.toBytes(BLOOMFILTER));
130  @InterfaceAudience.Private
131  public static final String REPLICATION_SCOPE = "REPLICATION_SCOPE";
132  private static final Bytes REPLICATION_SCOPE_BYTES = new Bytes(Bytes.toBytes(REPLICATION_SCOPE));
133  @InterfaceAudience.Private
134  public static final String MAX_VERSIONS = HConstants.VERSIONS;
135  private static final Bytes MAX_VERSIONS_BYTES = new Bytes(Bytes.toBytes(MAX_VERSIONS));
136  @InterfaceAudience.Private
137  public static final String MIN_VERSIONS = "MIN_VERSIONS";
138  private static final Bytes MIN_VERSIONS_BYTES = new Bytes(Bytes.toBytes(MIN_VERSIONS));
139  /**
140   * Retain all cells across flushes and compactions even if they fall behind a
141   * delete tombstone. To see all retained cells, do a 'raw' scan; see
142   * Scan#setRaw or pass RAW => true attribute in the shell.
143   */
144  @InterfaceAudience.Private
145  public static final String KEEP_DELETED_CELLS = "KEEP_DELETED_CELLS";
146  private static final Bytes KEEP_DELETED_CELLS_BYTES = new Bytes(Bytes.toBytes(KEEP_DELETED_CELLS));
147  @InterfaceAudience.Private
148  public static final String COMPRESS_TAGS = "COMPRESS_TAGS";
149  private static final Bytes COMPRESS_TAGS_BYTES = new Bytes(Bytes.toBytes(COMPRESS_TAGS));
150  @InterfaceAudience.Private
151  public static final String ENCRYPTION = "ENCRYPTION";
152  private static final Bytes ENCRYPTION_BYTES = new Bytes(Bytes.toBytes(ENCRYPTION));
153  @InterfaceAudience.Private
154  public static final String ENCRYPTION_KEY = "ENCRYPTION_KEY";
155  private static final Bytes ENCRYPTION_KEY_BYTES = new Bytes(Bytes.toBytes(ENCRYPTION_KEY));
156
157  private static final boolean DEFAULT_MOB = false;
158  @InterfaceAudience.Private
159  public static final String IS_MOB = "IS_MOB";
160  private static final Bytes IS_MOB_BYTES = new Bytes(Bytes.toBytes(IS_MOB));
161  @InterfaceAudience.Private
162  public static final String MOB_THRESHOLD = "MOB_THRESHOLD";
163  private static final Bytes MOB_THRESHOLD_BYTES = new Bytes(Bytes.toBytes(MOB_THRESHOLD));
164  public static final long DEFAULT_MOB_THRESHOLD = 100 * 1024; // 100k
165  @InterfaceAudience.Private
166  public static final String MOB_COMPACT_PARTITION_POLICY = "MOB_COMPACT_PARTITION_POLICY";
167  private static final Bytes MOB_COMPACT_PARTITION_POLICY_BYTES = new Bytes(Bytes.toBytes(MOB_COMPACT_PARTITION_POLICY));
168  public static final MobCompactPartitionPolicy DEFAULT_MOB_COMPACT_PARTITION_POLICY
169          = MobCompactPartitionPolicy.DAILY;
170  @InterfaceAudience.Private
171  public static final String DFS_REPLICATION = "DFS_REPLICATION";
172  private static final Bytes DFS_REPLICATION_BYTES = new Bytes(Bytes.toBytes(DFS_REPLICATION));
173  public static final short DEFAULT_DFS_REPLICATION = 0;
174  @InterfaceAudience.Private
175  public static final String STORAGE_POLICY = "STORAGE_POLICY";
176  private static final Bytes STORAGE_POLICY_BYTES = new Bytes(Bytes.toBytes(STORAGE_POLICY));
177
178  public static final String NEW_VERSION_BEHAVIOR = "NEW_VERSION_BEHAVIOR";
179  private static final Bytes NEW_VERSION_BEHAVIOR_BYTES = new Bytes(Bytes.toBytes(NEW_VERSION_BEHAVIOR));
180  public static final boolean DEFAULT_NEW_VERSION_BEHAVIOR = false;
181  /**
182   * Default compression type.
183   */
184  public static final Compression.Algorithm DEFAULT_COMPRESSION = Compression.Algorithm.NONE;
185
186  /**
187   * Default data block encoding algorithm.
188   */
189  public static final DataBlockEncoding DEFAULT_DATA_BLOCK_ENCODING = DataBlockEncoding.NONE;
190
191  /**
192   * Default number of versions of a record to keep.
193   */
194  public static final int DEFAULT_MAX_VERSIONS = 1;
195
196  /**
197   * Default is not to keep a minimum of versions.
198   */
199  public static final int DEFAULT_MIN_VERSIONS = 0;
200
201  /**
202   * Default setting for whether to try and serve this column family from memory
203   * or not.
204   */
205  public static final boolean DEFAULT_IN_MEMORY = false;
206
207  /**
208   * Default setting for preventing deleted from being collected immediately.
209   */
210  public static final KeepDeletedCells DEFAULT_KEEP_DELETED = KeepDeletedCells.FALSE;
211
212  /**
213   * Default setting for whether to use a block cache or not.
214   */
215  public static final boolean DEFAULT_BLOCKCACHE = true;
216
217  /**
218   * Default setting for whether to cache data blocks on write if block caching
219   * is enabled.
220   */
221  public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
222
223  /**
224   * Default setting for whether to cache index blocks on write if block caching
225   * is enabled.
226   */
227  public static final boolean DEFAULT_CACHE_INDEX_ON_WRITE = false;
228
229  /**
230   * Default size of blocks in files stored to the filesytem (hfiles).
231   */
232  public static final int DEFAULT_BLOCKSIZE = HConstants.DEFAULT_BLOCKSIZE;
233
234  /**
235   * Default setting for whether or not to use bloomfilters.
236   */
237  public static final BloomType DEFAULT_BLOOMFILTER = BloomType.ROW;
238
239  /**
240   * Default setting for whether to cache bloom filter blocks on write if block
241   * caching is enabled.
242   */
243  public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
244
245  /**
246   * Default time to live of cell contents.
247   */
248  public static final int DEFAULT_TTL = HConstants.FOREVER;
249
250  /**
251   * Default scope.
252   */
253  public static final int DEFAULT_REPLICATION_SCOPE = HConstants.REPLICATION_SCOPE_LOCAL;
254
255  /**
256   * Default setting for whether to evict cached blocks from the blockcache on
257   * close.
258   */
259  public static final boolean DEFAULT_EVICT_BLOCKS_ON_CLOSE = false;
260
261  /**
262   * Default compress tags along with any type of DataBlockEncoding.
263   */
264  public static final boolean DEFAULT_COMPRESS_TAGS = true;
265
266  /*
267   * Default setting for whether to prefetch blocks into the blockcache on open.
268   */
269  public static final boolean DEFAULT_PREFETCH_BLOCKS_ON_OPEN = false;
270
271  private final static Map<String, String> DEFAULT_VALUES = new HashMap<>();
272
273  private static Map<Bytes, Bytes> getDefaultValuesBytes() {
274    Map<Bytes, Bytes> values = new HashMap<>();
275    DEFAULT_VALUES.forEach((k, v) -> values.put(new Bytes(Bytes.toBytes(k)), new Bytes(Bytes.toBytes(v))));
276    return values;
277  }
278
279  public static Map<String, String> getDefaultValues() {
280    return Collections.unmodifiableMap(DEFAULT_VALUES);
281  }
282
283  private final static Set<Bytes> RESERVED_KEYWORDS = new HashSet<>();
284
285  static {
286    DEFAULT_VALUES.put(BLOOMFILTER, DEFAULT_BLOOMFILTER.name());
287    DEFAULT_VALUES.put(REPLICATION_SCOPE, String.valueOf(DEFAULT_REPLICATION_SCOPE));
288    DEFAULT_VALUES.put(MAX_VERSIONS, String.valueOf(DEFAULT_MAX_VERSIONS));
289    DEFAULT_VALUES.put(MIN_VERSIONS, String.valueOf(DEFAULT_MIN_VERSIONS));
290    DEFAULT_VALUES.put(COMPRESSION, DEFAULT_COMPRESSION.name());
291    DEFAULT_VALUES.put(TTL, String.valueOf(DEFAULT_TTL));
292    DEFAULT_VALUES.put(BLOCKSIZE, String.valueOf(DEFAULT_BLOCKSIZE));
293    DEFAULT_VALUES.put(IN_MEMORY, String.valueOf(DEFAULT_IN_MEMORY));
294    DEFAULT_VALUES.put(BLOCKCACHE, String.valueOf(DEFAULT_BLOCKCACHE));
295    DEFAULT_VALUES.put(KEEP_DELETED_CELLS, String.valueOf(DEFAULT_KEEP_DELETED));
296    DEFAULT_VALUES.put(DATA_BLOCK_ENCODING, String.valueOf(DEFAULT_DATA_BLOCK_ENCODING));
297    DEFAULT_VALUES.put(CACHE_DATA_ON_WRITE, String.valueOf(DEFAULT_CACHE_DATA_ON_WRITE));
298    DEFAULT_VALUES.put(CACHE_INDEX_ON_WRITE, String.valueOf(DEFAULT_CACHE_INDEX_ON_WRITE));
299    DEFAULT_VALUES.put(CACHE_BLOOMS_ON_WRITE, String.valueOf(DEFAULT_CACHE_BLOOMS_ON_WRITE));
300    DEFAULT_VALUES.put(EVICT_BLOCKS_ON_CLOSE, String.valueOf(DEFAULT_EVICT_BLOCKS_ON_CLOSE));
301    DEFAULT_VALUES.put(PREFETCH_BLOCKS_ON_OPEN, String.valueOf(DEFAULT_PREFETCH_BLOCKS_ON_OPEN));
302    DEFAULT_VALUES.put(NEW_VERSION_BEHAVIOR, String.valueOf(DEFAULT_NEW_VERSION_BEHAVIOR));
303    DEFAULT_VALUES.keySet().forEach(s -> RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(s))));
304    RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(ENCRYPTION)));
305    RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(ENCRYPTION_KEY)));
306    RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(IS_MOB)));
307    RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(MOB_THRESHOLD)));
308    RESERVED_KEYWORDS.add(new Bytes(Bytes.toBytes(MOB_COMPACT_PARTITION_POLICY)));
309  }
310
311  public static Unit getUnit(String key) {
312    /* TTL for now, we can add more as we need */
313    switch (key) {
314      case TTL:
315        return Unit.TIME_INTERVAL;
316      default:
317        return Unit.NONE;
318    }
319  }
320
321  /**
322   * @param b Family name.
323   * @return <code>b</code>
324   * @throws IllegalArgumentException If not null and not a legitimate family
325   * name: i.e. 'printable' and ends in a ':' (Null passes are allowed because
326   * <code>b</code> can be null when deserializing). Cannot start with a '.'
327   * either. Also Family can not be an empty value or equal "recovered.edits".
328   */
329  public static byte[] isLegalColumnFamilyName(final byte[] b) {
330    if (b == null) {
331      return null;
332    }
333    Preconditions.checkArgument(b.length != 0, "Column Family name can not be empty");
334    if (b[0] == '.') {
335      throw new IllegalArgumentException("Column Family names cannot start with a "
336              + "period: " + Bytes.toString(b));
337    }
338    for (int i = 0; i < b.length; i++) {
339      if (Character.isISOControl(b[i]) || b[i] == ':' || b[i] == '\\' || b[i] == '/') {
340        throw new IllegalArgumentException("Illegal character <" + b[i]
341                + ">. Column Family names cannot contain control characters or colons: "
342                + Bytes.toString(b));
343      }
344    }
345    byte[] recoveredEdit = Bytes.toBytes(HConstants.RECOVERED_EDITS_DIR);
346    if (Bytes.equals(recoveredEdit, b)) {
347      throw new IllegalArgumentException("Column Family name cannot be: "
348              + HConstants.RECOVERED_EDITS_DIR);
349    }
350    return b;
351  }
352
353  private final ModifyableColumnFamilyDescriptor desc;
354
355  public static ColumnFamilyDescriptor parseFrom(final byte[] pbBytes) throws DeserializationException {
356    return ModifyableColumnFamilyDescriptor.parseFrom(pbBytes);
357  }
358
359  public static ColumnFamilyDescriptorBuilder newBuilder(final byte[] name) {
360    return new ColumnFamilyDescriptorBuilder(name);
361  }
362
363  public static ColumnFamilyDescriptorBuilder newBuilder(final ColumnFamilyDescriptor desc) {
364    return new ColumnFamilyDescriptorBuilder(desc);
365  }
366
367  public static ColumnFamilyDescriptor copy(ColumnFamilyDescriptor desc) {
368    return new ModifyableColumnFamilyDescriptor(desc);
369  }
370
371  public static ColumnFamilyDescriptor of(String name) {
372    return of(Bytes.toBytes(name));
373  }
374
375  public static ColumnFamilyDescriptor of(byte[] name) {
376    return newBuilder(name).build();
377  }
378
379  private ColumnFamilyDescriptorBuilder(final byte[] name) {
380    this.desc = new ModifyableColumnFamilyDescriptor(name);
381  }
382
383  private ColumnFamilyDescriptorBuilder(final ColumnFamilyDescriptor desc) {
384    this.desc = new ModifyableColumnFamilyDescriptor(desc);
385  }
386
387  /**
388   * @param desc The table descriptor to serialize
389   * @return This instance serialized with pb with pb magic prefix
390   */
391  public static byte[] toByteArray(ColumnFamilyDescriptor desc) {
392    if (desc instanceof ModifyableColumnFamilyDescriptor) {
393      return ((ModifyableColumnFamilyDescriptor) desc).toByteArray();
394    }
395    return new ModifyableColumnFamilyDescriptor(desc).toByteArray();
396  }
397
398  public ColumnFamilyDescriptor build() {
399    return new ModifyableColumnFamilyDescriptor(desc);
400  }
401
402  public ColumnFamilyDescriptorBuilder removeConfiguration(String key) {
403    desc.removeConfiguration(key);
404    return this;
405  }
406
407  public String getNameAsString() {
408    return desc.getNameAsString();
409  }
410
411  public ColumnFamilyDescriptorBuilder setBlockCacheEnabled(boolean value) {
412    desc.setBlockCacheEnabled(value);
413    return this;
414  }
415
416  public ColumnFamilyDescriptorBuilder setBlocksize(int value) {
417    desc.setBlocksize(value);
418    return this;
419  }
420
421  public ColumnFamilyDescriptorBuilder setBloomFilterType(final BloomType value) {
422    desc.setBloomFilterType(value);
423    return this;
424  }
425
426  public ColumnFamilyDescriptorBuilder setCacheBloomsOnWrite(boolean value) {
427    desc.setCacheBloomsOnWrite(value);
428    return this;
429  }
430
431  public ColumnFamilyDescriptorBuilder setCacheDataOnWrite(boolean value) {
432    desc.setCacheDataOnWrite(value);
433    return this;
434  }
435
436  public ColumnFamilyDescriptorBuilder setCacheIndexesOnWrite(final boolean value) {
437    desc.setCacheIndexesOnWrite(value);
438    return this;
439  }
440
441  public ColumnFamilyDescriptorBuilder setCompactionCompressionType(Compression.Algorithm value) {
442    desc.setCompactionCompressionType(value);
443    return this;
444  }
445
446  public ColumnFamilyDescriptorBuilder setCompressTags(boolean value) {
447    desc.setCompressTags(value);
448    return this;
449  }
450
451  public ColumnFamilyDescriptorBuilder setCompressionType(Compression.Algorithm value) {
452    desc.setCompressionType(value);
453    return this;
454  }
455
456  public Compression.Algorithm getCompressionType() {
457    return desc.getCompressionType();
458  }
459
460  public ColumnFamilyDescriptorBuilder setConfiguration(final String key, final String value) {
461    desc.setConfiguration(key, value);
462    return this;
463  }
464
465  public ColumnFamilyDescriptorBuilder setDFSReplication(short value) {
466    desc.setDFSReplication(value);
467    return this;
468  }
469
470  public ColumnFamilyDescriptorBuilder setDataBlockEncoding(DataBlockEncoding value) {
471    desc.setDataBlockEncoding(value);
472    return this;
473  }
474
475  public ColumnFamilyDescriptorBuilder setEncryptionKey(final byte[] value) {
476    desc.setEncryptionKey(value);
477    return this;
478  }
479
480  public ColumnFamilyDescriptorBuilder setEncryptionType(String value) {
481    desc.setEncryptionType(value);
482    return this;
483  }
484
485  public ColumnFamilyDescriptorBuilder setEvictBlocksOnClose(boolean value) {
486    desc.setEvictBlocksOnClose(value);
487    return this;
488  }
489
490  public ColumnFamilyDescriptorBuilder setInMemory(final boolean value) {
491    desc.setInMemory(value);
492    return this;
493  }
494
495  public ColumnFamilyDescriptorBuilder setInMemoryCompaction(final MemoryCompactionPolicy value) {
496    desc.setInMemoryCompaction(value);
497    return this;
498  }
499
500  public ColumnFamilyDescriptorBuilder setKeepDeletedCells(KeepDeletedCells value) {
501    desc.setKeepDeletedCells(value);
502    return this;
503  }
504
505  public ColumnFamilyDescriptorBuilder setMaxVersions(final int value) {
506    desc.setMaxVersions(value);
507    return this;
508  }
509
510  public ColumnFamilyDescriptorBuilder setMinVersions(final int value) {
511    desc.setMinVersions(value);
512    return this;
513  }
514
515  public ColumnFamilyDescriptorBuilder setMobCompactPartitionPolicy(final MobCompactPartitionPolicy value) {
516    desc.setMobCompactPartitionPolicy(value);
517    return this;
518  }
519
520  public ColumnFamilyDescriptorBuilder setMobEnabled(final boolean value) {
521    desc.setMobEnabled(value);
522    return this;
523  }
524
525  public ColumnFamilyDescriptorBuilder setMobThreshold(final long value) {
526    desc.setMobThreshold(value);
527    return this;
528  }
529
530  public ColumnFamilyDescriptorBuilder setPrefetchBlocksOnOpen(final boolean value) {
531    desc.setPrefetchBlocksOnOpen(value);
532    return this;
533  }
534
535  public ColumnFamilyDescriptorBuilder setScope(final int value) {
536    desc.setScope(value);
537    return this;
538  }
539
540  public ColumnFamilyDescriptorBuilder setStoragePolicy(final String value) {
541    desc.setStoragePolicy(value);
542    return this;
543  }
544
545  public ColumnFamilyDescriptorBuilder setTimeToLive(final int value) {
546    desc.setTimeToLive(value);
547    return this;
548  }
549
550  public ColumnFamilyDescriptorBuilder setTimeToLive(final String value) throws HBaseException {
551    desc.setTimeToLive(value);
552    return this;
553  }
554
555  public ColumnFamilyDescriptorBuilder setNewVersionBehavior(final boolean value) {
556    desc.setNewVersionBehavior(value);
557    return this;
558  }
559
560  public ColumnFamilyDescriptorBuilder setValue(final Bytes key, final Bytes value) {
561    desc.setValue(key, value);
562    return this;
563  }
564
565  public ColumnFamilyDescriptorBuilder setValue(final byte[] key, final byte[] value) {
566    desc.setValue(key, value);
567    return this;
568  }
569
570  public ColumnFamilyDescriptorBuilder setValue(final String key, final String value) {
571    desc.setValue(key, value);
572    return this;
573  }
574
575  /**
576   * An ModifyableFamilyDescriptor contains information about a column family such as the
577   * number of versions, compression settings, etc.
578   *
579   * It is used as input when creating a table or adding a column.
580   * TODO: make this package-private after removing the HColumnDescriptor
581   */
582  @InterfaceAudience.Private
583  public static class ModifyableColumnFamilyDescriptor
584      implements ColumnFamilyDescriptor, Comparable<ModifyableColumnFamilyDescriptor> {
585
586    // Column family name
587    private final byte[] name;
588
589    // Column metadata
590    private final Map<Bytes, Bytes> values = new HashMap<>();
591
592    /**
593     * A map which holds the configuration specific to the column family. The
594     * keys of the map have the same names as config keys and override the
595     * defaults with cf-specific settings. Example usage may be for compactions,
596     * etc.
597     */
598    private final Map<String, String> configuration = new HashMap<>();
599
600    /**
601     * Construct a column descriptor specifying only the family name The other
602     * attributes are defaulted.
603     *
604     * @param name Column family name. Must be 'printable' -- digit or
605     * letter -- and may not contain a <code>:</code>
606     * TODO: make this private after the HCD is removed.
607     */
608    @InterfaceAudience.Private
609    public ModifyableColumnFamilyDescriptor(final byte[] name) {
610      this(isLegalColumnFamilyName(name), getDefaultValuesBytes(), Collections.emptyMap());
611    }
612
613    /**
614     * Constructor. Makes a deep copy of the supplied descriptor.
615     * TODO: make this private after the HCD is removed.
616     * @param desc The descriptor.
617     */
618    @InterfaceAudience.Private
619    public ModifyableColumnFamilyDescriptor(ColumnFamilyDescriptor desc) {
620      this(desc.getName(), desc.getValues(), desc.getConfiguration());
621    }
622
623    private ModifyableColumnFamilyDescriptor(byte[] name, Map<Bytes, Bytes> values, Map<String, String> config) {
624      this.name = name;
625      this.values.putAll(values);
626      this.configuration.putAll(config);
627    }
628
629    @Override
630    public byte[] getName() {
631      return Bytes.copy(name);
632    }
633
634    @Override
635    public String getNameAsString() {
636      return Bytes.toString(name);
637    }
638
639    @Override
640    public Bytes getValue(Bytes key) {
641      return values.get(key);
642    }
643
644    @Override
645    public byte[] getValue(byte[] key) {
646      Bytes value = values.get(new Bytes(key));
647      return value == null ? null : value.get();
648    }
649
650    @Override
651    public Map<Bytes, Bytes> getValues() {
652      return Collections.unmodifiableMap(values);
653    }
654
655    /**
656     * @param key The key.
657     * @param value The value.
658     * @return this (for chained invocation)
659     */
660    public ModifyableColumnFamilyDescriptor setValue(byte[] key, byte[] value) {
661      return setValue(toBytesOrNull(key, Function.identity()), toBytesOrNull(value, Function.identity()));
662    }
663
664    public ModifyableColumnFamilyDescriptor setValue(String key, String value) {
665      return setValue(toBytesOrNull(key, Bytes::toBytes), toBytesOrNull(value, Bytes::toBytes));
666    }
667
668    private ModifyableColumnFamilyDescriptor setValue(Bytes key, String value) {
669      return setValue(key, toBytesOrNull(value, Bytes::toBytes));
670    }
671    /**
672     * @param key The key.
673     * @param value The value.
674     * @return this (for chained invocation)
675     */
676    private ModifyableColumnFamilyDescriptor setValue(Bytes key, Bytes value) {
677      if (value == null) {
678        values.remove(key);
679      } else {
680        values.put(key, value);
681      }
682      return this;
683    }
684
685    /**
686     *
687     * @param key Key whose key and value we're to remove from HCD parameters.
688     * @return this (for chained invocation)
689     */
690    public ModifyableColumnFamilyDescriptor removeValue(final Bytes key) {
691      return setValue(key, (Bytes) null);
692    }
693
694    private static <T> Bytes toBytesOrNull(T t, Function<T, byte[]> f) {
695      if (t == null) {
696        return null;
697      } else {
698        return new Bytes(f.apply(t));
699      }
700    }
701
702    private <T> T getStringOrDefault(Bytes key, Function<String, T> function, T defaultValue) {
703      return getOrDefault(key, b -> function.apply(Bytes.toString(b)), defaultValue);
704    }
705
706    private <T> T getOrDefault(Bytes key, Function<byte[], T> function, T defaultValue) {
707      Bytes value = values.get(key);
708      if (value == null) {
709        return defaultValue;
710      } else {
711        return function.apply(value.get());
712      }
713    }
714
715    @Override
716    public int getMaxVersions() {
717      return getStringOrDefault(MAX_VERSIONS_BYTES, Integer::parseInt, DEFAULT_MAX_VERSIONS);
718    }
719
720    /**
721     * @param maxVersions maximum number of versions
722     * @return this (for chained invocation)
723     */
724    public ModifyableColumnFamilyDescriptor setMaxVersions(int maxVersions) {
725      if (maxVersions <= 0) {
726        // TODO: Allow maxVersion of 0 to be the way you say "Keep all versions".
727        // Until there is support, consider 0 or < 0 -- a configuration error.
728        throw new IllegalArgumentException("Maximum versions must be positive");
729      }
730      if (maxVersions < this.getMinVersions()) {
731        throw new IllegalArgumentException("Set MaxVersion to " + maxVersions
732                + " while minVersion is " + this.getMinVersions()
733                + ". Maximum versions must be >= minimum versions ");
734      }
735      setValue(MAX_VERSIONS_BYTES, Integer.toString(maxVersions));
736      return this;
737    }
738
739    /**
740     * Set minimum and maximum versions to keep
741     *
742     * @param minVersions minimal number of versions
743     * @param maxVersions maximum number of versions
744     * @return this (for chained invocation)
745     */
746    public ModifyableColumnFamilyDescriptor setVersions(int minVersions, int maxVersions) {
747      if (minVersions <= 0) {
748        // TODO: Allow minVersion and maxVersion of 0 to be the way you say "Keep all versions".
749        // Until there is support, consider 0 or < 0 -- a configuration error.
750        throw new IllegalArgumentException("Minimum versions must be positive");
751      }
752
753      if (maxVersions < minVersions) {
754        throw new IllegalArgumentException("Unable to set MaxVersion to " + maxVersions
755                + " and set MinVersion to " + minVersions
756                + ", as maximum versions must be >= minimum versions.");
757      }
758      setMinVersions(minVersions);
759      setMaxVersions(maxVersions);
760      return this;
761    }
762
763
764    @Override
765    public int getBlocksize() {
766      return getStringOrDefault(BLOCKSIZE_BYTES, Integer::valueOf, DEFAULT_BLOCKSIZE);
767    }
768
769    /**
770     * @param s Blocksize to use when writing out storefiles/hfiles on this
771     * column family.
772     * @return this (for chained invocation)
773     */
774    public ModifyableColumnFamilyDescriptor setBlocksize(int s) {
775      return setValue(BLOCKSIZE_BYTES, Integer.toString(s));
776    }
777
778    @Override
779    public Compression.Algorithm getCompressionType() {
780      return getStringOrDefault(COMPRESSION_BYTES,
781              Compression.Algorithm::valueOf,
782              DEFAULT_COMPRESSION);
783    }
784
785    /**
786     * Compression types supported in hbase. LZO is not bundled as part of the
787     * hbase distribution. See
788     * <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO
789     * Compression</a>
790     * for how to enable it.
791     *
792     * @param type Compression type setting.
793     * @return this (for chained invocation)
794     */
795    public ModifyableColumnFamilyDescriptor setCompressionType(Compression.Algorithm type) {
796      return setValue(COMPRESSION_BYTES, type.name());
797    }
798
799    @Override
800    public DataBlockEncoding getDataBlockEncoding() {
801      return getStringOrDefault(DATA_BLOCK_ENCODING_BYTES,
802              DataBlockEncoding::valueOf,
803              DataBlockEncoding.NONE);
804    }
805
806    /**
807     * Set data block encoding algorithm used in block cache.
808     *
809     * @param type What kind of data block encoding will be used.
810     * @return this (for chained invocation)
811     */
812    public ModifyableColumnFamilyDescriptor setDataBlockEncoding(DataBlockEncoding type) {
813      return setValue(DATA_BLOCK_ENCODING_BYTES, type == null ? DataBlockEncoding.NONE.name() : type.name());
814    }
815
816    /**
817     * Set whether the tags should be compressed along with DataBlockEncoding.
818     * When no DataBlockEncoding is been used, this is having no effect.
819     *
820     * @param compressTags
821     * @return this (for chained invocation)
822     */
823    public ModifyableColumnFamilyDescriptor setCompressTags(boolean compressTags) {
824      return setValue(COMPRESS_TAGS_BYTES, String.valueOf(compressTags));
825    }
826
827    @Override
828    public boolean isCompressTags() {
829      return getStringOrDefault(COMPRESS_TAGS_BYTES, Boolean::valueOf,
830              DEFAULT_COMPRESS_TAGS);
831    }
832
833    @Override
834    public Compression.Algorithm getCompactionCompressionType() {
835      return getStringOrDefault(COMPRESSION_COMPACT_BYTES,
836              Compression.Algorithm::valueOf,
837              getCompressionType());
838    }
839
840    /**
841     * Compression types supported in hbase. LZO is not bundled as part of the
842     * hbase distribution. See
843     * <a href="http://wiki.apache.org/hadoop/UsingLzoCompression">LZO
844     * Compression</a>
845     * for how to enable it.
846     *
847     * @param type Compression type setting.
848     * @return this (for chained invocation)
849     */
850    public ModifyableColumnFamilyDescriptor setCompactionCompressionType(
851            Compression.Algorithm type) {
852      return setValue(COMPRESSION_COMPACT_BYTES, type.name());
853    }
854
855    @Override
856    public boolean isInMemory() {
857      return getStringOrDefault(IN_MEMORY_BYTES, Boolean::valueOf, DEFAULT_IN_MEMORY);
858    }
859
860    /**
861     * @param inMemory True if we are to favor keeping all values for this
862     * column family in the HRegionServer cache
863     * @return this (for chained invocation)
864     */
865    public ModifyableColumnFamilyDescriptor setInMemory(boolean inMemory) {
866      return setValue(IN_MEMORY_BYTES, Boolean.toString(inMemory));
867    }
868
869    @Override
870    public MemoryCompactionPolicy getInMemoryCompaction() {
871      return getStringOrDefault(IN_MEMORY_COMPACTION_BYTES, MemoryCompactionPolicy::valueOf, null);
872    }
873
874    /**
875     * @param inMemoryCompaction the prefered in-memory compaction policy for
876     * this column family
877     * @return this (for chained invocation)
878     */
879    public ModifyableColumnFamilyDescriptor setInMemoryCompaction(MemoryCompactionPolicy inMemoryCompaction) {
880      return setValue(IN_MEMORY_COMPACTION_BYTES, inMemoryCompaction.name());
881    }
882
883    @Override
884    public KeepDeletedCells getKeepDeletedCells() {
885      return getStringOrDefault(KEEP_DELETED_CELLS_BYTES,
886          KeepDeletedCells::getValue, DEFAULT_KEEP_DELETED);
887    }
888
889    /**
890     * @param keepDeletedCells True if deleted rows should not be collected
891     * immediately.
892     * @return this (for chained invocation)
893     */
894    public ModifyableColumnFamilyDescriptor setKeepDeletedCells(KeepDeletedCells keepDeletedCells) {
895      return setValue(KEEP_DELETED_CELLS_BYTES, keepDeletedCells.name());
896    }
897
898    /**
899     * By default, HBase only consider timestamp in versions. So a previous Delete with higher ts
900     * will mask a later Put with lower ts. Set this to true to enable new semantics of versions.
901     * We will also consider mvcc in versions. See HBASE-15968 for details.
902     */
903    @Override
904    public boolean isNewVersionBehavior() {
905      return getStringOrDefault(NEW_VERSION_BEHAVIOR_BYTES,
906          Boolean::parseBoolean, DEFAULT_NEW_VERSION_BEHAVIOR);
907    }
908
909    public ModifyableColumnFamilyDescriptor setNewVersionBehavior(boolean newVersionBehavior) {
910      return setValue(NEW_VERSION_BEHAVIOR_BYTES, Boolean.toString(newVersionBehavior));
911    }
912
913    @Override
914    public int getTimeToLive() {
915      return getStringOrDefault(TTL_BYTES, Integer::parseInt, DEFAULT_TTL);
916    }
917
918    /**
919     * @param timeToLive Time-to-live of cell contents, in seconds.
920     * @return this (for chained invocation)
921     */
922    public ModifyableColumnFamilyDescriptor setTimeToLive(int timeToLive) {
923      return setValue(TTL_BYTES, Integer.toString(timeToLive));
924    }
925
926    /**
927     * @param timeToLive Time-to-live of cell contents, in seconds.
928     * @return this (for chained invocation)
929     * @throws org.apache.hadoop.hbase.exceptions.HBaseException
930     */
931    public ModifyableColumnFamilyDescriptor setTimeToLive(String timeToLive) throws HBaseException {
932      return setTimeToLive(Integer.parseInt(PrettyPrinter.valueOf(timeToLive, Unit.TIME_INTERVAL)));
933    }
934
935    @Override
936    public int getMinVersions() {
937      return getStringOrDefault(MIN_VERSIONS_BYTES, Integer::valueOf, DEFAULT_MIN_VERSIONS);
938    }
939
940    /**
941     * @param minVersions The minimum number of versions to keep. (used when
942     * timeToLive is set)
943     * @return this (for chained invocation)
944     */
945    public ModifyableColumnFamilyDescriptor setMinVersions(int minVersions) {
946      return setValue(MIN_VERSIONS_BYTES, Integer.toString(minVersions));
947    }
948
949    @Override
950    public boolean isBlockCacheEnabled() {
951      return getStringOrDefault(BLOCKCACHE_BYTES, Boolean::valueOf, DEFAULT_BLOCKCACHE);
952    }
953
954    /**
955     * @param blockCacheEnabled True if hfile DATA type blocks should be cached
956     * (We always cache INDEX and BLOOM blocks; you cannot turn this off).
957     * @return this (for chained invocation)
958     */
959    public ModifyableColumnFamilyDescriptor setBlockCacheEnabled(boolean blockCacheEnabled) {
960      return setValue(BLOCKCACHE_BYTES, Boolean.toString(blockCacheEnabled));
961    }
962
963    @Override
964    public BloomType getBloomFilterType() {
965      return getStringOrDefault(BLOOMFILTER_BYTES, BloomType::valueOf, DEFAULT_BLOOMFILTER);
966    }
967
968    public ModifyableColumnFamilyDescriptor setBloomFilterType(final BloomType bt) {
969      return setValue(BLOOMFILTER_BYTES, bt.name());
970    }
971
972    @Override
973    public int getScope() {
974      return getStringOrDefault(REPLICATION_SCOPE_BYTES, Integer::valueOf, DEFAULT_REPLICATION_SCOPE);
975    }
976
977    /**
978     * @param scope the scope tag
979     * @return this (for chained invocation)
980     */
981    public ModifyableColumnFamilyDescriptor setScope(int scope) {
982      return setValue(REPLICATION_SCOPE_BYTES, Integer.toString(scope));
983    }
984
985    @Override
986    public boolean isCacheDataOnWrite() {
987      return getStringOrDefault(CACHE_DATA_ON_WRITE_BYTES, Boolean::valueOf, DEFAULT_CACHE_DATA_ON_WRITE);
988    }
989
990    /**
991     * @param value true if we should cache data blocks on write
992     * @return this (for chained invocation)
993     */
994    public ModifyableColumnFamilyDescriptor setCacheDataOnWrite(boolean value) {
995      return setValue(CACHE_DATA_ON_WRITE_BYTES, Boolean.toString(value));
996    }
997
998    @Override
999    public boolean isCacheIndexesOnWrite() {
1000      return getStringOrDefault(CACHE_INDEX_ON_WRITE_BYTES, Boolean::valueOf, DEFAULT_CACHE_INDEX_ON_WRITE);
1001    }
1002
1003    /**
1004     * @param value true if we should cache index blocks on write
1005     * @return this (for chained invocation)
1006     */
1007    public ModifyableColumnFamilyDescriptor setCacheIndexesOnWrite(boolean value) {
1008      return setValue(CACHE_INDEX_ON_WRITE_BYTES, Boolean.toString(value));
1009    }
1010
1011    @Override
1012    public boolean isCacheBloomsOnWrite() {
1013      return getStringOrDefault(CACHE_BLOOMS_ON_WRITE_BYTES, Boolean::valueOf, DEFAULT_CACHE_BLOOMS_ON_WRITE);
1014    }
1015
1016    /**
1017     * @param value true if we should cache bloomfilter blocks on write
1018     * @return this (for chained invocation)
1019     */
1020    public ModifyableColumnFamilyDescriptor setCacheBloomsOnWrite(boolean value) {
1021      return setValue(CACHE_BLOOMS_ON_WRITE_BYTES, Boolean.toString(value));
1022    }
1023
1024    @Override
1025    public boolean isEvictBlocksOnClose() {
1026      return getStringOrDefault(EVICT_BLOCKS_ON_CLOSE_BYTES, Boolean::valueOf, DEFAULT_EVICT_BLOCKS_ON_CLOSE);
1027    }
1028
1029    /**
1030     * @param value true if we should evict cached blocks from the blockcache on
1031     * close
1032     * @return this (for chained invocation)
1033     */
1034    public ModifyableColumnFamilyDescriptor setEvictBlocksOnClose(boolean value) {
1035      return setValue(EVICT_BLOCKS_ON_CLOSE_BYTES, Boolean.toString(value));
1036    }
1037
1038    @Override
1039    public boolean isPrefetchBlocksOnOpen() {
1040      return getStringOrDefault(PREFETCH_BLOCKS_ON_OPEN_BYTES, Boolean::valueOf, DEFAULT_PREFETCH_BLOCKS_ON_OPEN);
1041    }
1042
1043    /**
1044     * @param value true if we should prefetch blocks into the blockcache on
1045     * open
1046     * @return this (for chained invocation)
1047     */
1048    public ModifyableColumnFamilyDescriptor setPrefetchBlocksOnOpen(boolean value) {
1049      return setValue(PREFETCH_BLOCKS_ON_OPEN_BYTES, Boolean.toString(value));
1050    }
1051
1052    @Override
1053    public String toString() {
1054      StringBuilder s = new StringBuilder();
1055      s.append('{');
1056      s.append(HConstants.NAME);
1057      s.append(" => '");
1058      s.append(getNameAsString());
1059      s.append("'");
1060      s.append(getValues(true));
1061      s.append('}');
1062      return s.toString();
1063    }
1064
1065
1066    @Override
1067    public String toStringCustomizedValues() {
1068      StringBuilder s = new StringBuilder();
1069      s.append('{');
1070      s.append(HConstants.NAME);
1071      s.append(" => '");
1072      s.append(getNameAsString());
1073      s.append("'");
1074      s.append(getValues(false));
1075      s.append('}');
1076      return s.toString();
1077    }
1078
1079    private StringBuilder getValues(boolean printDefaults) {
1080      StringBuilder s = new StringBuilder();
1081
1082      boolean hasConfigKeys = false;
1083
1084      // print all reserved keys first
1085      for (Map.Entry<Bytes, Bytes> entry : values.entrySet()) {
1086        if (!RESERVED_KEYWORDS.contains(entry.getKey())) {
1087          hasConfigKeys = true;
1088          continue;
1089        }
1090        String key = Bytes.toString(entry.getKey().get());
1091        String value = Bytes.toStringBinary(entry.getValue().get());
1092        if (printDefaults
1093                || !DEFAULT_VALUES.containsKey(key)
1094                || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
1095          s.append(", ");
1096          s.append(key);
1097          s.append(" => ");
1098          s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
1099        }
1100      }
1101
1102      // print all non-reserved, advanced config keys as a separate subset
1103      if (hasConfigKeys) {
1104        s.append(", ");
1105        s.append(HConstants.METADATA).append(" => ");
1106        s.append('{');
1107        boolean printComma = false;
1108        for (Map.Entry<Bytes, Bytes> entry : values.entrySet()) {
1109          Bytes k = entry.getKey();
1110          if (RESERVED_KEYWORDS.contains(k)) {
1111            continue;
1112          }
1113          String key = Bytes.toString(k.get());
1114          String value = Bytes.toStringBinary(entry.getValue().get());
1115          if (printComma) {
1116            s.append(", ");
1117          }
1118          printComma = true;
1119          s.append('\'').append(key).append('\'');
1120          s.append(" => ");
1121          s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\'');
1122        }
1123        s.append('}');
1124      }
1125
1126      if (!configuration.isEmpty()) {
1127        s.append(", ");
1128        s.append(HConstants.CONFIGURATION).append(" => ");
1129        s.append('{');
1130        boolean printCommaForConfiguration = false;
1131        for (Map.Entry<String, String> e : configuration.entrySet()) {
1132          if (printCommaForConfiguration) {
1133            s.append(", ");
1134          }
1135          printCommaForConfiguration = true;
1136          s.append('\'').append(e.getKey()).append('\'');
1137          s.append(" => ");
1138          s.append('\'').append(PrettyPrinter.format(e.getValue(), getUnit(e.getKey()))).append('\'');
1139        }
1140        s.append("}");
1141      }
1142      return s;
1143    }
1144
1145    @Override
1146    public boolean equals(Object obj) {
1147      if (this == obj) {
1148        return true;
1149      }
1150      if (obj instanceof ModifyableColumnFamilyDescriptor) {
1151        return ColumnFamilyDescriptor.COMPARATOR.compare(this, (ModifyableColumnFamilyDescriptor) obj) == 0;
1152      }
1153      return false;
1154    }
1155
1156    @Override
1157    public int hashCode() {
1158      int result = Bytes.hashCode(name);
1159      result ^= (int) COLUMN_DESCRIPTOR_VERSION;
1160      result ^= values.hashCode();
1161      result ^= configuration.hashCode();
1162      return result;
1163    }
1164
1165    @Override
1166    public int compareTo(ModifyableColumnFamilyDescriptor other) {
1167      return COMPARATOR.compare(this, other);
1168    }
1169
1170    /**
1171     * @return This instance serialized with pb with pb magic prefix
1172     * @see #parseFrom(byte[])
1173     */
1174    private byte[] toByteArray() {
1175      return ProtobufUtil.prependPBMagic(ProtobufUtil.toColumnFamilySchema(this)
1176                      .toByteArray());
1177    }
1178
1179    /**
1180     * @param bytes A pb serialized {@link ModifyableColumnFamilyDescriptor} instance with pb
1181     * magic prefix
1182     * @return An instance of {@link ModifyableColumnFamilyDescriptor} made from
1183     * <code>bytes</code>
1184     * @throws DeserializationException
1185     * @see #toByteArray()
1186     */
1187    private static ColumnFamilyDescriptor parseFrom(final byte[] bytes) throws DeserializationException {
1188      if (!ProtobufUtil.isPBMagicPrefix(bytes)) {
1189        throw new DeserializationException("No magic");
1190      }
1191      int pblen = ProtobufUtil.lengthOfPBMagic();
1192      ColumnFamilySchema.Builder builder = ColumnFamilySchema.newBuilder();
1193      ColumnFamilySchema cfs = null;
1194      try {
1195        ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen);
1196        cfs = builder.build();
1197      } catch (IOException e) {
1198        throw new DeserializationException(e);
1199      }
1200      return ProtobufUtil.toColumnFamilyDescriptor(cfs);
1201    }
1202
1203    @Override
1204    public String getConfigurationValue(String key) {
1205      return configuration.get(key);
1206    }
1207
1208    @Override
1209    public Map<String, String> getConfiguration() {
1210      // shallow pointer copy
1211      return Collections.unmodifiableMap(configuration);
1212    }
1213
1214    /**
1215     * Setter for storing a configuration setting in {@link #configuration} map.
1216     *
1217     * @param key Config key. Same as XML config key e.g.
1218     * hbase.something.or.other.
1219     * @param value String value. If null, removes the configuration.
1220     * @return this (for chained invocation)
1221     */
1222    public ModifyableColumnFamilyDescriptor setConfiguration(String key, String value) {
1223      if (value == null) {
1224        configuration.remove(key);
1225      } else {
1226        configuration.put(key, value);
1227      }
1228      return this;
1229    }
1230
1231    /**
1232     * Remove a configuration setting represented by the key from the
1233     * {@link #configuration} map.
1234     *
1235     * @param key
1236     * @return this (for chained invocation)
1237     */
1238    public ModifyableColumnFamilyDescriptor removeConfiguration(final String key) {
1239      return setConfiguration(key, null);
1240    }
1241
1242    @Override
1243    public String getEncryptionType() {
1244      return getStringOrDefault(ENCRYPTION_BYTES, Function.identity(), null);
1245    }
1246
1247    /**
1248     * Set the encryption algorithm for use with this family
1249     *
1250     * @param algorithm
1251     * @return this (for chained invocation)
1252     */
1253    public ModifyableColumnFamilyDescriptor setEncryptionType(String algorithm) {
1254      return setValue(ENCRYPTION_BYTES, algorithm);
1255    }
1256
1257    @Override
1258    public byte[] getEncryptionKey() {
1259      return getOrDefault(ENCRYPTION_KEY_BYTES, Bytes::copy, null);
1260    }
1261
1262    /**
1263     * Set the raw crypto key attribute for the family
1264     *
1265     * @param keyBytes
1266     * @return this (for chained invocation)
1267     */
1268    public ModifyableColumnFamilyDescriptor setEncryptionKey(byte[] keyBytes) {
1269      return setValue(ENCRYPTION_KEY_BYTES, new Bytes(keyBytes));
1270    }
1271
1272    @Override
1273    public long getMobThreshold() {
1274      return getStringOrDefault(MOB_THRESHOLD_BYTES, Long::valueOf, DEFAULT_MOB_THRESHOLD);
1275    }
1276
1277    /**
1278     * Sets the mob threshold of the family.
1279     *
1280     * @param threshold The mob threshold.
1281     * @return this (for chained invocation)
1282     */
1283    public ModifyableColumnFamilyDescriptor setMobThreshold(long threshold) {
1284      return setValue(MOB_THRESHOLD_BYTES, String.valueOf(threshold));
1285    }
1286
1287    @Override
1288    public boolean isMobEnabled() {
1289      return getStringOrDefault(IS_MOB_BYTES, Boolean::valueOf, DEFAULT_MOB);
1290    }
1291
1292    /**
1293     * Enables the mob for the family.
1294     *
1295     * @param isMobEnabled Whether to enable the mob for the family.
1296     * @return this (for chained invocation)
1297     */
1298    public ModifyableColumnFamilyDescriptor setMobEnabled(boolean isMobEnabled) {
1299      return setValue(IS_MOB_BYTES, String.valueOf(isMobEnabled));
1300    }
1301
1302    @Override
1303    public MobCompactPartitionPolicy getMobCompactPartitionPolicy() {
1304      return getStringOrDefault(MOB_COMPACT_PARTITION_POLICY_BYTES,
1305              MobCompactPartitionPolicy::valueOf, DEFAULT_MOB_COMPACT_PARTITION_POLICY);
1306    }
1307
1308    /**
1309     * Set the mob compact partition policy for the family.
1310     *
1311     * @param policy policy type
1312     * @return this (for chained invocation)
1313     */
1314    public ModifyableColumnFamilyDescriptor setMobCompactPartitionPolicy(MobCompactPartitionPolicy policy) {
1315      return setValue(MOB_COMPACT_PARTITION_POLICY_BYTES, policy.name());
1316    }
1317
1318    @Override
1319    public short getDFSReplication() {
1320      return getStringOrDefault(DFS_REPLICATION_BYTES,
1321              Short::valueOf, DEFAULT_DFS_REPLICATION);
1322    }
1323
1324    /**
1325     * Set the replication factor to hfile(s) belonging to this family
1326     *
1327     * @param replication number of replicas the blocks(s) belonging to this CF
1328     * should have, or {@link #DEFAULT_DFS_REPLICATION} for the default
1329     * replication factor set in the filesystem
1330     * @return this (for chained invocation)
1331     */
1332    public ModifyableColumnFamilyDescriptor setDFSReplication(short replication) {
1333      if (replication < 1 && replication != DEFAULT_DFS_REPLICATION) {
1334        throw new IllegalArgumentException(
1335                "DFS replication factor cannot be less than 1 if explicitly set.");
1336      }
1337      return setValue(DFS_REPLICATION_BYTES, Short.toString(replication));
1338    }
1339
1340    @Override
1341    public String getStoragePolicy() {
1342      return getStringOrDefault(STORAGE_POLICY_BYTES, Function.identity(), null);
1343    }
1344
1345    /**
1346     * Set the storage policy for use with this family
1347     *
1348     * @param policy the policy to set, valid setting includes:
1349     * <i>"LAZY_PERSIST"</i>,
1350     * <i>"ALL_SSD"</i>, <i>"ONE_SSD"</i>, <i>"HOT"</i>, <i>"WARM"</i>,
1351     * <i>"COLD"</i>
1352     * @return this (for chained invocation)
1353     */
1354    public ModifyableColumnFamilyDescriptor setStoragePolicy(String policy) {
1355      return setValue(STORAGE_POLICY_BYTES, policy);
1356    }
1357
1358  }
1359}