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