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