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.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Objects; 030import java.util.Optional; 031import java.util.Set; 032import java.util.TreeMap; 033import java.util.TreeSet; 034import java.util.function.BiPredicate; 035import java.util.function.Function; 036import java.util.regex.Matcher; 037import java.util.regex.Pattern; 038import java.util.stream.Collectors; 039import org.apache.hadoop.hbase.Coprocessor; 040import org.apache.hadoop.hbase.HConstants; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.exceptions.DeserializationException; 043import org.apache.hadoop.hbase.exceptions.HBaseException; 044import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.PrettyPrinter; 047import org.apache.yetus.audience.InterfaceAudience; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 052import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 053 054/** 055 * Convenience class for composing an instance of {@link TableDescriptor}. 056 * @since 2.0.0 057 */ 058@InterfaceAudience.Public 059public class TableDescriptorBuilder { 060 public static final Logger LOG = LoggerFactory.getLogger(TableDescriptorBuilder.class); 061 @InterfaceAudience.Private 062 public static final String SPLIT_POLICY = "SPLIT_POLICY"; 063 private static final Bytes SPLIT_POLICY_KEY = new Bytes(Bytes.toBytes(SPLIT_POLICY)); 064 /** 065 * Used by HBase Shell interface to access this metadata attribute which denotes the maximum size 066 * of the store file after which a region split occurs. 067 */ 068 @InterfaceAudience.Private 069 public static final String MAX_FILESIZE = "MAX_FILESIZE"; 070 private static final Bytes MAX_FILESIZE_KEY = new Bytes(Bytes.toBytes(MAX_FILESIZE)); 071 072 /** 073 * Used by rest interface to access this metadata attribute which denotes if the table is Read 074 * Only. 075 */ 076 @InterfaceAudience.Private 077 public static final String READONLY = "READONLY"; 078 private static final Bytes READONLY_KEY = new Bytes(Bytes.toBytes(READONLY)); 079 080 /** 081 * Used by HBase Shell interface to access this metadata attribute which denotes if the table is 082 * compaction enabled. 083 */ 084 @InterfaceAudience.Private 085 public static final String COMPACTION_ENABLED = "COMPACTION_ENABLED"; 086 private static final Bytes COMPACTION_ENABLED_KEY = new Bytes(Bytes.toBytes(COMPACTION_ENABLED)); 087 088 /** 089 * Used by HBase Shell interface to access this metadata attribute which denotes if the table is 090 * split enabled. 091 */ 092 @InterfaceAudience.Private 093 public static final String SPLIT_ENABLED = "SPLIT_ENABLED"; 094 private static final Bytes SPLIT_ENABLED_KEY = new Bytes(Bytes.toBytes(SPLIT_ENABLED)); 095 096 /** 097 * Used by HBase Shell interface to access this metadata attribute which denotes if the table is 098 * merge enabled. 099 */ 100 @InterfaceAudience.Private 101 public static final String MERGE_ENABLED = "MERGE_ENABLED"; 102 private static final Bytes MERGE_ENABLED_KEY = new Bytes(Bytes.toBytes(MERGE_ENABLED)); 103 104 /** 105 * Used by HBase Shell interface to access this metadata attribute which represents the maximum 106 * size of the memstore after which its contents are flushed onto the disk. 107 */ 108 @InterfaceAudience.Private 109 public static final String MEMSTORE_FLUSHSIZE = "MEMSTORE_FLUSHSIZE"; 110 private static final Bytes MEMSTORE_FLUSHSIZE_KEY = new Bytes(Bytes.toBytes(MEMSTORE_FLUSHSIZE)); 111 112 @InterfaceAudience.Private 113 public static final String FLUSH_POLICY = "FLUSH_POLICY"; 114 private static final Bytes FLUSH_POLICY_KEY = new Bytes(Bytes.toBytes(FLUSH_POLICY)); 115 /** 116 * Used by rest interface to access this metadata attribute which denotes if it is a catalog 117 * table, either <code> hbase:meta </code>. 118 */ 119 @InterfaceAudience.Private 120 public static final String IS_META = "IS_META"; 121 private static final Bytes IS_META_KEY = new Bytes(Bytes.toBytes(IS_META)); 122 123 /** 124 * {@link Durability} setting for the table. 125 */ 126 @InterfaceAudience.Private 127 public static final String DURABILITY = "DURABILITY"; 128 private static final Bytes DURABILITY_KEY = new Bytes(Bytes.toBytes("DURABILITY")); 129 130 /** 131 * The number of region replicas for the table. 132 */ 133 @InterfaceAudience.Private 134 public static final String REGION_REPLICATION = "REGION_REPLICATION"; 135 private static final Bytes REGION_REPLICATION_KEY = new Bytes(Bytes.toBytes(REGION_REPLICATION)); 136 137 /** 138 * The flag to indicate whether or not the memstore should be replicated for read-replicas 139 * (CONSISTENCY => TIMELINE). 140 */ 141 @InterfaceAudience.Private 142 public static final String REGION_MEMSTORE_REPLICATION = "REGION_MEMSTORE_REPLICATION"; 143 private static final Bytes REGION_MEMSTORE_REPLICATION_KEY = 144 new Bytes(Bytes.toBytes(REGION_MEMSTORE_REPLICATION)); 145 146 /** 147 * Used by shell/rest interface to access this metadata attribute which denotes if the table 148 * should be treated by region normalizer. 149 */ 150 @InterfaceAudience.Private 151 public static final String NORMALIZATION_ENABLED = "NORMALIZATION_ENABLED"; 152 private static final Bytes NORMALIZATION_ENABLED_KEY = 153 new Bytes(Bytes.toBytes(NORMALIZATION_ENABLED)); 154 155 @InterfaceAudience.Private 156 public static final String NORMALIZER_TARGET_REGION_COUNT = "NORMALIZER_TARGET_REGION_COUNT"; 157 private static final Bytes NORMALIZER_TARGET_REGION_COUNT_KEY = 158 new Bytes(Bytes.toBytes(NORMALIZER_TARGET_REGION_COUNT)); 159 160 @InterfaceAudience.Private 161 public static final String NORMALIZER_TARGET_REGION_SIZE_MB = "NORMALIZER_TARGET_REGION_SIZE_MB"; 162 private static final Bytes NORMALIZER_TARGET_REGION_SIZE_MB_KEY = 163 new Bytes(Bytes.toBytes(NORMALIZER_TARGET_REGION_SIZE_MB)); 164 // TODO: Keeping backward compatability with HBASE-25651 change. Can be removed in later version 165 @InterfaceAudience.Private 166 @Deprecated 167 public static final String NORMALIZER_TARGET_REGION_SIZE = "NORMALIZER_TARGET_REGION_SIZE"; 168 @Deprecated 169 private static final Bytes NORMALIZER_TARGET_REGION_SIZE_KEY = 170 new Bytes(Bytes.toBytes(NORMALIZER_TARGET_REGION_SIZE)); 171 172 /** 173 * Default durability for HTD is USE_DEFAULT, which defaults to HBase-global default value 174 */ 175 private static final Durability DEFAULT_DURABLITY = Durability.USE_DEFAULT; 176 177 @InterfaceAudience.Private 178 public static final String PRIORITY = "PRIORITY"; 179 private static final Bytes PRIORITY_KEY = new Bytes(Bytes.toBytes(PRIORITY)); 180 181 private static final Bytes RSGROUP_KEY = 182 new Bytes(Bytes.toBytes(RSGroupInfo.TABLE_DESC_PROP_GROUP)); 183 184 /** 185 * Relative priority of the table used for rpc scheduling 186 */ 187 private static final int DEFAULT_PRIORITY = HConstants.NORMAL_QOS; 188 189 /** 190 * Constant that denotes whether the table is READONLY by default and is false 191 */ 192 public static final boolean DEFAULT_READONLY = false; 193 194 /** 195 * Constant that denotes whether the table is compaction enabled by default 196 */ 197 public static final boolean DEFAULT_COMPACTION_ENABLED = true; 198 199 /** 200 * Constant that denotes whether the table is split enabled by default 201 */ 202 public static final boolean DEFAULT_SPLIT_ENABLED = true; 203 204 /** 205 * Constant that denotes whether the table is merge enabled by default 206 */ 207 public static final boolean DEFAULT_MERGE_ENABLED = true; 208 209 /** 210 * Constant that denotes the maximum default size of the memstore in bytes after which the 211 * contents are flushed to the store files. 212 */ 213 public static final long DEFAULT_MEMSTORE_FLUSH_SIZE = 1024 * 1024 * 128L; 214 215 public static final int DEFAULT_REGION_REPLICATION = 1; 216 217 public static final boolean DEFAULT_REGION_MEMSTORE_REPLICATION = true; 218 219 private final static Map<String, String> DEFAULT_VALUES = new HashMap<>(); 220 private final static Set<Bytes> RESERVED_KEYWORDS = new HashSet<>(); 221 222 static { 223 DEFAULT_VALUES.put(MAX_FILESIZE, String.valueOf(HConstants.DEFAULT_MAX_FILE_SIZE)); 224 DEFAULT_VALUES.put(READONLY, String.valueOf(DEFAULT_READONLY)); 225 DEFAULT_VALUES.put(MEMSTORE_FLUSHSIZE, String.valueOf(DEFAULT_MEMSTORE_FLUSH_SIZE)); 226 DEFAULT_VALUES.put(DURABILITY, DEFAULT_DURABLITY.name()); // use the enum name 227 DEFAULT_VALUES.put(REGION_REPLICATION, String.valueOf(DEFAULT_REGION_REPLICATION)); 228 DEFAULT_VALUES.put(PRIORITY, String.valueOf(DEFAULT_PRIORITY)); 229 DEFAULT_VALUES.keySet().stream().map(s -> new Bytes(Bytes.toBytes(s))) 230 .forEach(RESERVED_KEYWORDS::add); 231 RESERVED_KEYWORDS.add(IS_META_KEY); 232 } 233 234 public static PrettyPrinter.Unit getUnit(String key) { 235 switch (key) { 236 case MAX_FILESIZE: 237 case MEMSTORE_FLUSHSIZE: 238 return PrettyPrinter.Unit.BYTE; 239 default: 240 return PrettyPrinter.Unit.NONE; 241 } 242 } 243 244 /** 245 * @deprecated namespace table has been folded into the ns family in meta table, do not use this 246 * any more. 247 */ 248 @InterfaceAudience.Private 249 @Deprecated 250 public final static String NAMESPACE_FAMILY_INFO = "info"; 251 252 /** 253 * @deprecated namespace table has been folded into the ns family in meta table, do not use this 254 * any more. 255 */ 256 @InterfaceAudience.Private 257 @Deprecated 258 public final static byte[] NAMESPACE_FAMILY_INFO_BYTES = Bytes.toBytes(NAMESPACE_FAMILY_INFO); 259 260 /** 261 * @deprecated namespace table has been folded into the ns family in meta table, do not use this 262 * any more. 263 */ 264 @InterfaceAudience.Private 265 @Deprecated 266 public final static byte[] NAMESPACE_COL_DESC_BYTES = Bytes.toBytes("d"); 267 268 /** 269 * <pre> 270 * Pattern that matches a coprocessor specification. Form is: 271 * {@code <coprocessor jar file location> '|' <class name> ['|' <priority> ['|' <arguments>]]} 272 * where arguments are {@code <KEY> '=' <VALUE> [,...]} 273 * For example: {@code hdfs:///foo.jar|com.foo.FooRegionObserver|1001|arg1=1,arg2=2} 274 * </pre> 275 */ 276 private static final Pattern CP_HTD_ATTR_VALUE_PATTERN = 277 Pattern.compile("(^[^\\|]*)\\|([^\\|]+)\\|[\\s]*([\\d]*)[\\s]*(\\|.*)?$"); 278 279 private static final String CP_HTD_ATTR_VALUE_PARAM_KEY_PATTERN = "[^=,]+"; 280 private static final String CP_HTD_ATTR_VALUE_PARAM_VALUE_PATTERN = "[^,]+"; 281 private static final Pattern CP_HTD_ATTR_VALUE_PARAM_PATTERN = Pattern.compile("(" 282 + CP_HTD_ATTR_VALUE_PARAM_KEY_PATTERN + ")=(" + CP_HTD_ATTR_VALUE_PARAM_VALUE_PATTERN + "),?"); 283 private static final Pattern CP_HTD_ATTR_KEY_PATTERN = 284 Pattern.compile("^coprocessor\\$([0-9]+)$", Pattern.CASE_INSENSITIVE); 285 286 /** 287 * Table descriptor for namespace table 288 * @deprecated since 3.0.0 and will be removed in 4.0.0. We have folded the data in namespace 289 * table into meta table, so do not use it any more. 290 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21154">HBASE-21154</a> 291 */ 292 @Deprecated 293 public static final TableDescriptor NAMESPACE_TABLEDESC = 294 TableDescriptorBuilder.newBuilder(TableName.NAMESPACE_TABLE_NAME) 295 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(NAMESPACE_FAMILY_INFO_BYTES) 296 // Ten is arbitrary number. Keep versions to help debugging. 297 .setMaxVersions(10).setInMemory(true).setBlocksize(8 * 1024) 298 .setScope(HConstants.REPLICATION_SCOPE_LOCAL).build()) 299 .build(); 300 301 private final ModifyableTableDescriptor desc; 302 303 /** Returns This instance serialized with pb with pb magic prefix */ 304 public static byte[] toByteArray(TableDescriptor desc) { 305 if (desc instanceof ModifyableTableDescriptor) { 306 return ((ModifyableTableDescriptor) desc).toByteArray(); 307 } 308 return new ModifyableTableDescriptor(desc).toByteArray(); 309 } 310 311 /** 312 * The input should be created by {@link #toByteArray}. 313 * @param pbBytes A pb serialized TableDescriptor instance with pb magic prefix 314 * @return This instance serialized with pb with pb magic prefix 315 * @throws org.apache.hadoop.hbase.exceptions.DeserializationException if an error occurred 316 */ 317 public static TableDescriptor parseFrom(byte[] pbBytes) throws DeserializationException { 318 return ModifyableTableDescriptor.parseFrom(pbBytes); 319 } 320 321 public static TableDescriptorBuilder newBuilder(final TableName name) { 322 return new TableDescriptorBuilder(name); 323 } 324 325 public static TableDescriptor copy(TableDescriptor desc) { 326 return new ModifyableTableDescriptor(desc); 327 } 328 329 public static TableDescriptor copy(TableName name, TableDescriptor desc) { 330 return new ModifyableTableDescriptor(name, desc); 331 } 332 333 /** 334 * Copy all values, families, and name from the input. 335 * @param desc The desciptor to copy 336 * @return A clone of input 337 */ 338 public static TableDescriptorBuilder newBuilder(final TableDescriptor desc) { 339 return new TableDescriptorBuilder(desc); 340 } 341 342 private TableDescriptorBuilder(final TableName name) { 343 this.desc = new ModifyableTableDescriptor(name); 344 } 345 346 private TableDescriptorBuilder(final TableDescriptor desc) { 347 this.desc = new ModifyableTableDescriptor(desc); 348 } 349 350 public TableDescriptorBuilder setCoprocessor(String className) throws IOException { 351 return setCoprocessor(CoprocessorDescriptorBuilder.of(className)); 352 } 353 354 public TableDescriptorBuilder setCoprocessor(CoprocessorDescriptor cpDesc) throws IOException { 355 desc.setCoprocessor(Objects.requireNonNull(cpDesc)); 356 return this; 357 } 358 359 public TableDescriptorBuilder setCoprocessors(Collection<CoprocessorDescriptor> cpDescs) 360 throws IOException { 361 for (CoprocessorDescriptor cpDesc : cpDescs) { 362 desc.setCoprocessor(cpDesc); 363 } 364 return this; 365 } 366 367 public boolean hasCoprocessor(String classNameToMatch) { 368 return desc.hasCoprocessor(classNameToMatch); 369 } 370 371 public TableDescriptorBuilder setColumnFamily(final ColumnFamilyDescriptor family) { 372 desc.setColumnFamily(Objects.requireNonNull(family)); 373 return this; 374 } 375 376 public TableDescriptorBuilder 377 setColumnFamilies(final Collection<ColumnFamilyDescriptor> families) { 378 families.forEach(desc::setColumnFamily); 379 return this; 380 } 381 382 public TableDescriptorBuilder modifyColumnFamily(final ColumnFamilyDescriptor family) { 383 desc.modifyColumnFamily(Objects.requireNonNull(family)); 384 return this; 385 } 386 387 public TableDescriptorBuilder removeValue(final String key) { 388 desc.removeValue(key); 389 return this; 390 } 391 392 public TableDescriptorBuilder removeValue(Bytes key) { 393 desc.removeValue(key); 394 return this; 395 } 396 397 public TableDescriptorBuilder removeValue(byte[] key) { 398 desc.removeValue(key); 399 return this; 400 } 401 402 public TableDescriptorBuilder removeValue(BiPredicate<Bytes, Bytes> predicate) { 403 List<Bytes> toRemove = 404 desc.getValues().entrySet().stream().filter(e -> predicate.test(e.getKey(), e.getValue())) 405 .map(Map.Entry::getKey).collect(Collectors.toList()); 406 for (Bytes key : toRemove) { 407 removeValue(key); 408 } 409 return this; 410 } 411 412 public TableDescriptorBuilder removeColumnFamily(final byte[] name) { 413 desc.removeColumnFamily(name); 414 return this; 415 } 416 417 public TableDescriptorBuilder removeCoprocessor(String className) { 418 desc.removeCoprocessor(className); 419 return this; 420 } 421 422 public TableDescriptorBuilder setCompactionEnabled(final boolean isEnable) { 423 desc.setCompactionEnabled(isEnable); 424 return this; 425 } 426 427 public TableDescriptorBuilder setSplitEnabled(final boolean isEnable) { 428 desc.setSplitEnabled(isEnable); 429 return this; 430 } 431 432 public TableDescriptorBuilder setMergeEnabled(final boolean isEnable) { 433 desc.setMergeEnabled(isEnable); 434 return this; 435 } 436 437 public TableDescriptorBuilder setDurability(Durability durability) { 438 desc.setDurability(durability); 439 return this; 440 } 441 442 public TableDescriptorBuilder setFlushPolicyClassName(String clazz) { 443 desc.setFlushPolicyClassName(clazz); 444 return this; 445 } 446 447 public TableDescriptorBuilder setMaxFileSize(long maxFileSize) { 448 desc.setMaxFileSize(maxFileSize); 449 return this; 450 } 451 452 public TableDescriptorBuilder setMaxFileSize(String maxFileSize) throws HBaseException { 453 desc.setMaxFileSize(maxFileSize); 454 return this; 455 } 456 457 public TableDescriptorBuilder setMemStoreFlushSize(long memstoreFlushSize) { 458 desc.setMemStoreFlushSize(memstoreFlushSize); 459 return this; 460 } 461 462 public TableDescriptorBuilder setMemStoreFlushSize(String memStoreFlushSize) 463 throws HBaseException { 464 desc.setMemStoreFlushSize(memStoreFlushSize); 465 return this; 466 } 467 468 public TableDescriptorBuilder setNormalizerTargetRegionCount(final int regionCount) { 469 desc.setNormalizerTargetRegionCount(regionCount); 470 return this; 471 } 472 473 public TableDescriptorBuilder setNormalizerTargetRegionSize(final long regionSize) { 474 desc.setNormalizerTargetRegionSize(regionSize); 475 return this; 476 } 477 478 public TableDescriptorBuilder setNormalizationEnabled(final boolean isEnable) { 479 desc.setNormalizationEnabled(isEnable); 480 return this; 481 } 482 483 public TableDescriptorBuilder setPriority(int priority) { 484 desc.setPriority(priority); 485 return this; 486 } 487 488 public TableDescriptorBuilder setReadOnly(final boolean readOnly) { 489 desc.setReadOnly(readOnly); 490 return this; 491 } 492 493 public TableDescriptorBuilder setRegionMemStoreReplication(boolean memstoreReplication) { 494 desc.setRegionMemStoreReplication(memstoreReplication); 495 return this; 496 } 497 498 public TableDescriptorBuilder setRegionReplication(int regionReplication) { 499 desc.setRegionReplication(regionReplication); 500 return this; 501 } 502 503 public TableDescriptorBuilder setRegionSplitPolicyClassName(String clazz) { 504 desc.setRegionSplitPolicyClassName(clazz); 505 return this; 506 } 507 508 public TableDescriptorBuilder setValue(final String key, final String value) { 509 desc.setValue(key, value); 510 return this; 511 } 512 513 public TableDescriptorBuilder setValue(final Bytes key, final Bytes value) { 514 desc.setValue(key, value); 515 return this; 516 } 517 518 public TableDescriptorBuilder setValue(final byte[] key, final byte[] value) { 519 desc.setValue(key, value); 520 return this; 521 } 522 523 public String getValue(String key) { 524 return desc.getValue(key); 525 } 526 527 /** 528 * Sets replication scope all & only the columns already in the builder. Columns added later won't 529 * be backfilled with replication scope. 530 * @param scope replication scope 531 * @return a TableDescriptorBuilder 532 */ 533 public TableDescriptorBuilder setReplicationScope(int scope) { 534 Map<byte[], ColumnFamilyDescriptor> newFamilies = new TreeMap<>(Bytes.BYTES_RAWCOMPARATOR); 535 newFamilies.putAll(desc.families); 536 newFamilies.forEach((cf, cfDesc) -> { 537 desc.removeColumnFamily(cf); 538 desc 539 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(cfDesc).setScope(scope).build()); 540 }); 541 return this; 542 } 543 544 public TableDescriptorBuilder setRegionServerGroup(String group) { 545 desc.setValue(RSGROUP_KEY, group); 546 return this; 547 } 548 549 public TableDescriptor build() { 550 return new ModifyableTableDescriptor(desc); 551 } 552 553 private static final class ModifyableTableDescriptor 554 implements TableDescriptor, Comparable<ModifyableTableDescriptor> { 555 556 private final TableName name; 557 558 /** 559 * A map which holds the metadata information of the table. This metadata includes values like 560 * IS_META, SPLIT_POLICY, MAX_FILE_SIZE, READONLY, MEMSTORE_FLUSHSIZE etc... 561 */ 562 private final Map<Bytes, Bytes> values = new HashMap<>(); 563 564 /** 565 * Maps column family name to the respective FamilyDescriptors 566 */ 567 private final Map<byte[], ColumnFamilyDescriptor> families = 568 new TreeMap<>(Bytes.BYTES_RAWCOMPARATOR); 569 570 /** 571 * Construct a table descriptor specifying a TableName object 572 * @param name Table name. 573 */ 574 private ModifyableTableDescriptor(final TableName name) { 575 this(name, Collections.emptyList(), Collections.emptyMap()); 576 } 577 578 private ModifyableTableDescriptor(final TableDescriptor desc) { 579 this(desc.getTableName(), Arrays.asList(desc.getColumnFamilies()), desc.getValues()); 580 } 581 582 /** 583 * Construct a table descriptor by cloning the descriptor passed as a parameter. 584 * <p> 585 * Makes a deep copy of the supplied descriptor. 586 * @param name The new name 587 * @param desc The descriptor. 588 */ 589 private ModifyableTableDescriptor(final TableName name, final TableDescriptor desc) { 590 this(name, Arrays.asList(desc.getColumnFamilies()), desc.getValues()); 591 } 592 593 private ModifyableTableDescriptor(final TableName name, 594 final Collection<ColumnFamilyDescriptor> families, Map<Bytes, Bytes> values) { 595 this.name = name; 596 families.forEach(c -> this.families.put(c.getName(), ColumnFamilyDescriptorBuilder.copy(c))); 597 this.values.putAll(values); 598 this.values.put(IS_META_KEY, 599 new Bytes(Bytes.toBytes(Boolean.toString(name.equals(TableName.META_TABLE_NAME))))); 600 } 601 602 /** 603 * Checks if this table is <code> hbase:meta </code> region. 604 * @return true if this table is <code> hbase:meta </code> region 605 */ 606 @Override 607 public boolean isMetaRegion() { 608 return getOrDefault(IS_META_KEY, Boolean::valueOf, false); 609 } 610 611 /** 612 * Checks if the table is a <code>hbase:meta</code> table 613 * @return true if table is <code> hbase:meta </code> region. 614 */ 615 @Override 616 public boolean isMetaTable() { 617 return isMetaRegion(); 618 } 619 620 @Override 621 public Bytes getValue(Bytes key) { 622 Bytes rval = values.get(key); 623 return rval == null ? null : new Bytes(rval.copyBytes()); 624 } 625 626 @Override 627 public String getValue(String key) { 628 Bytes rval = values.get(new Bytes(Bytes.toBytes(key))); 629 return rval == null ? null : Bytes.toString(rval.get(), rval.getOffset(), rval.getLength()); 630 } 631 632 @Override 633 public byte[] getValue(byte[] key) { 634 Bytes value = values.get(new Bytes(key)); 635 return value == null ? null : value.copyBytes(); 636 } 637 638 private <T> T getOrDefault(Bytes key, Function<String, T> function, T defaultValue) { 639 Bytes value = values.get(key); 640 if (value == null) { 641 return defaultValue; 642 } else { 643 return function.apply(Bytes.toString(value.get(), value.getOffset(), value.getLength())); 644 } 645 } 646 647 /** 648 * Getter for fetching an unmodifiable {@link #values} map. 649 * @return unmodifiable map {@link #values}. 650 * @see #values 651 */ 652 @Override 653 public Map<Bytes, Bytes> getValues() { 654 // shallow pointer copy 655 return Collections.unmodifiableMap(values); 656 } 657 658 /** 659 * Setter for storing metadata as a (key, value) pair in {@link #values} map 660 * @param key The key. 661 * @param value The value. If null, removes the setting. 662 * @return the modifyable TD 663 * @see #values 664 */ 665 public ModifyableTableDescriptor setValue(byte[] key, byte[] value) { 666 return setValue(toBytesOrNull(key, v -> v), toBytesOrNull(value, v -> v)); 667 } 668 669 public ModifyableTableDescriptor setValue(String key, String value) { 670 return setValue(toBytesOrNull(key, Bytes::toBytes), toBytesOrNull(value, Bytes::toBytes)); 671 } 672 673 /** 674 * @param key The key. 675 * @param value The value. If null, removes the setting. 676 */ 677 private ModifyableTableDescriptor setValue(final Bytes key, final String value) { 678 return setValue(key, toBytesOrNull(value, Bytes::toBytes)); 679 } 680 681 /** 682 * Setter for storing metadata as a (key, value) pair in {@link #values} map 683 * @param key The key. 684 * @param value The value. If null, removes the setting. 685 */ 686 public ModifyableTableDescriptor setValue(final Bytes key, final 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 private static <T> Bytes toBytesOrNull(T t, Function<T, byte[]> f) { 696 if (t == null) { 697 return null; 698 } else { 699 return new Bytes(f.apply(t)); 700 } 701 } 702 703 /** 704 * Remove metadata represented by the key from the {@link #values} map 705 * @param key Key whose key and value we're to remove from TableDescriptor parameters. 706 * @return the modifyable TD 707 */ 708 public ModifyableTableDescriptor removeValue(final String key) { 709 return setValue(key, (String) null); 710 } 711 712 /** 713 * Remove metadata represented by the key from the {@link #values} map 714 * @param key Key whose key and value we're to remove from TableDescriptor parameters. 715 * @return the modifyable TD 716 */ 717 public ModifyableTableDescriptor removeValue(Bytes key) { 718 return setValue(key, (Bytes) null); 719 } 720 721 /** 722 * Remove metadata represented by the key from the {@link #values} map 723 * @param key Key whose key and value we're to remove from TableDescriptor parameters. 724 * @return the modifyable TD 725 */ 726 public ModifyableTableDescriptor removeValue(final byte[] key) { 727 return removeValue(new Bytes(key)); 728 } 729 730 /** 731 * Check if the readOnly flag of the table is set. If the readOnly flag is set then the contents 732 * of the table can only be read from but not modified. 733 * @return true if all columns in the table should be read only 734 */ 735 @Override 736 public boolean isReadOnly() { 737 return getOrDefault(READONLY_KEY, Boolean::valueOf, DEFAULT_READONLY); 738 } 739 740 /** 741 * Setting the table as read only sets all the columns in the table as read only. By default all 742 * tables are modifiable, but if the readOnly flag is set to true then the contents of the table 743 * can only be read but not modified. 744 * @param readOnly True if all of the columns in the table should be read only. 745 * @return the modifyable TD 746 */ 747 public ModifyableTableDescriptor setReadOnly(final boolean readOnly) { 748 return setValue(READONLY_KEY, Boolean.toString(readOnly)); 749 } 750 751 /** 752 * Check if the compaction enable flag of the table is true. If flag is false then no 753 * minor/major compactions will be done in real. 754 * @return true if table compaction enabled 755 */ 756 @Override 757 public boolean isCompactionEnabled() { 758 return getOrDefault(COMPACTION_ENABLED_KEY, Boolean::valueOf, DEFAULT_COMPACTION_ENABLED); 759 } 760 761 /** 762 * Setting the table compaction enable flag. 763 * @param isEnable True if enable compaction. 764 * @return the modifyable TD 765 */ 766 public ModifyableTableDescriptor setCompactionEnabled(final boolean isEnable) { 767 return setValue(COMPACTION_ENABLED_KEY, Boolean.toString(isEnable)); 768 } 769 770 /** 771 * Check if the split enable flag of the table is true. If flag is false then no split will be 772 * done. 773 * @return true if table region split enabled 774 */ 775 @Override 776 public boolean isSplitEnabled() { 777 return getOrDefault(SPLIT_ENABLED_KEY, Boolean::valueOf, DEFAULT_SPLIT_ENABLED); 778 } 779 780 /** 781 * Setting the table region split enable flag. 782 * @param isEnable True if enable region split. 783 * @return the modifyable TD 784 */ 785 public ModifyableTableDescriptor setSplitEnabled(final boolean isEnable) { 786 return setValue(SPLIT_ENABLED_KEY, Boolean.toString(isEnable)); 787 } 788 789 /** 790 * Check if the region merge enable flag of the table is true. If flag is false then no merge 791 * will be done. 792 * @return true if table region merge enabled 793 */ 794 @Override 795 public boolean isMergeEnabled() { 796 return getOrDefault(MERGE_ENABLED_KEY, Boolean::valueOf, DEFAULT_MERGE_ENABLED); 797 } 798 799 /** 800 * Setting the table region merge enable flag. 801 * @param isEnable True if enable region merge. 802 * @return the modifyable TD 803 */ 804 public ModifyableTableDescriptor setMergeEnabled(final boolean isEnable) { 805 return setValue(MERGE_ENABLED_KEY, Boolean.toString(isEnable)); 806 } 807 808 /** 809 * Check if normalization enable flag of the table is true. If flag is false then no region 810 * normalizer won't attempt to normalize this table. 811 * @return true if region normalization is enabled for this table 812 **/ 813 @Override 814 public boolean isNormalizationEnabled() { 815 return getOrDefault(NORMALIZATION_ENABLED_KEY, Boolean::valueOf, false); 816 } 817 818 /** 819 * Check if there is the target region count. If so, the normalize plan will be calculated based 820 * on the target region count. 821 * @return target region count after normalize done 822 */ 823 @Override 824 public int getNormalizerTargetRegionCount() { 825 return getOrDefault(NORMALIZER_TARGET_REGION_COUNT_KEY, Integer::valueOf, 826 Integer.valueOf(-1)); 827 } 828 829 /** 830 * Check if there is the target region size. If so, the normalize plan will be calculated based 831 * on the target region size. 832 * @return target region size after normalize done 833 */ 834 @Override 835 public long getNormalizerTargetRegionSize() { 836 long target_region_size = 837 getOrDefault(NORMALIZER_TARGET_REGION_SIZE_MB_KEY, Long::valueOf, Long.valueOf(-1)); 838 return target_region_size == Long.valueOf(-1) 839 ? getOrDefault(NORMALIZER_TARGET_REGION_SIZE_KEY, Long::valueOf, Long.valueOf(-1)) 840 : target_region_size; 841 } 842 843 /** 844 * Setting the table normalization enable flag. 845 * @param isEnable True if enable normalization. 846 * @return the modifyable TD 847 */ 848 public ModifyableTableDescriptor setNormalizationEnabled(final boolean isEnable) { 849 return setValue(NORMALIZATION_ENABLED_KEY, Boolean.toString(isEnable)); 850 } 851 852 /** 853 * Setting the target region count of table normalization . 854 * @param regionCount the target region count. 855 * @return the modifyable TD 856 */ 857 public ModifyableTableDescriptor setNormalizerTargetRegionCount(final int regionCount) { 858 return setValue(NORMALIZER_TARGET_REGION_COUNT_KEY, Integer.toString(regionCount)); 859 } 860 861 /** 862 * Setting the target region size of table normalization. 863 * @param regionSize the target region size. 864 * @return the modifyable TD 865 */ 866 public ModifyableTableDescriptor setNormalizerTargetRegionSize(final long regionSize) { 867 return setValue(NORMALIZER_TARGET_REGION_SIZE_MB_KEY, Long.toString(regionSize)); 868 } 869 870 /** 871 * Sets the {@link Durability} setting for the table. This defaults to Durability.USE_DEFAULT. 872 * @param durability enum value 873 * @return the modifyable TD 874 */ 875 public ModifyableTableDescriptor setDurability(Durability durability) { 876 return setValue(DURABILITY_KEY, durability.name()); 877 } 878 879 /** 880 * Returns the durability setting for the table. 881 * @return durability setting for the table. 882 */ 883 @Override 884 public Durability getDurability() { 885 return getOrDefault(DURABILITY_KEY, Durability::valueOf, DEFAULT_DURABLITY); 886 } 887 888 /** 889 * Get the name of the table 890 */ 891 @Override 892 public TableName getTableName() { 893 return name; 894 } 895 896 /** 897 * This sets the class associated with the region split policy which determines when a region 898 * split should occur. The class used by default is defined in 899 * org.apache.hadoop.hbase.regionserver.RegionSplitPolicy 900 * @param clazz the class name 901 * @return the modifyable TD 902 */ 903 public ModifyableTableDescriptor setRegionSplitPolicyClassName(String clazz) { 904 return setValue(SPLIT_POLICY_KEY, clazz); 905 } 906 907 /** 908 * This gets the class associated with the region split policy which determines when a region 909 * split should occur. The class used by default is defined in 910 * org.apache.hadoop.hbase.regionserver.RegionSplitPolicy 911 * @return the class name of the region split policy for this table. If this returns null, the 912 * default split policy is used. 913 */ 914 @Override 915 public String getRegionSplitPolicyClassName() { 916 return getOrDefault(SPLIT_POLICY_KEY, Function.identity(), null); 917 } 918 919 /** 920 * Returns the maximum size upto which a region can grow to after which a region split is 921 * triggered. The region size is represented by the size of the biggest store file in that 922 * region. 923 * @return max hregion size for table, -1 if not set. 924 * @see #setMaxFileSize(long) 925 */ 926 @Override 927 public long getMaxFileSize() { 928 return getOrDefault(MAX_FILESIZE_KEY, Long::valueOf, (long) -1); 929 } 930 931 /** 932 * Sets the maximum size upto which a region can grow to after which a region split is 933 * triggered. The region size is represented by the size of the biggest store file in that 934 * region, i.e. If the biggest store file grows beyond the maxFileSize, then the region split is 935 * triggered. This defaults to a value of 256 MB. 936 * <p> 937 * This is not an absolute value and might vary. Assume that a single row exceeds the 938 * maxFileSize then the storeFileSize will be greater than maxFileSize since a single row cannot 939 * be split across multiple regions 940 * </p> 941 * @param maxFileSize The maximum file size that a store file can grow to before a split is 942 * triggered. 943 * @return the modifyable TD 944 */ 945 public ModifyableTableDescriptor setMaxFileSize(long maxFileSize) { 946 return setValue(MAX_FILESIZE_KEY, Long.toString(maxFileSize)); 947 } 948 949 public ModifyableTableDescriptor setMaxFileSize(String maxFileSize) throws HBaseException { 950 return setMaxFileSize( 951 Long.parseLong(PrettyPrinter.valueOf(maxFileSize, PrettyPrinter.Unit.BYTE))); 952 } 953 954 /** 955 * Returns the size of the memstore after which a flush to filesystem is triggered. 956 * @return memory cache flush size for each hregion, -1 if not set. 957 * @see #setMemStoreFlushSize(long) 958 */ 959 @Override 960 public long getMemStoreFlushSize() { 961 return getOrDefault(MEMSTORE_FLUSHSIZE_KEY, Long::valueOf, (long) -1); 962 } 963 964 /** 965 * Represents the maximum size of the memstore after which the contents of the memstore are 966 * flushed to the filesystem. This defaults to a size of 64 MB. 967 * @param memstoreFlushSize memory cache flush size for each hregion 968 * @return the modifyable TD 969 */ 970 public ModifyableTableDescriptor setMemStoreFlushSize(long memstoreFlushSize) { 971 return setValue(MEMSTORE_FLUSHSIZE_KEY, Long.toString(memstoreFlushSize)); 972 } 973 974 public ModifyableTableDescriptor setMemStoreFlushSize(String memStoreFlushSize) 975 throws HBaseException { 976 return setMemStoreFlushSize( 977 Long.parseLong(PrettyPrinter.valueOf(memStoreFlushSize, PrettyPrinter.Unit.BYTE))); 978 } 979 980 /** 981 * This sets the class associated with the flush policy which determines determines the stores 982 * need to be flushed when flushing a region. The class used by default is defined in 983 * org.apache.hadoop.hbase.regionserver.FlushPolicy. 984 * @param clazz the class name 985 * @return the modifyable TD 986 */ 987 public ModifyableTableDescriptor setFlushPolicyClassName(String clazz) { 988 return setValue(FLUSH_POLICY_KEY, clazz); 989 } 990 991 /** 992 * This gets the class associated with the flush policy which determines the stores need to be 993 * flushed when flushing a region. The class used by default is defined in 994 * org.apache.hadoop.hbase.regionserver.FlushPolicy. 995 * @return the class name of the flush policy for this table. If this returns null, the default 996 * flush policy is used. 997 */ 998 @Override 999 public String getFlushPolicyClassName() { 1000 return getOrDefault(FLUSH_POLICY_KEY, Function.identity(), null); 1001 } 1002 1003 /** 1004 * Adds a column family. For the updating purpose please use 1005 * {@link #modifyColumnFamily(ColumnFamilyDescriptor)} instead. 1006 * @param family to add. 1007 * @return the modifyable TD 1008 */ 1009 public ModifyableTableDescriptor setColumnFamily(final ColumnFamilyDescriptor family) { 1010 if (family.getName() == null || family.getName().length <= 0) { 1011 throw new IllegalArgumentException("Family name cannot be null or empty"); 1012 } 1013 int flength = family.getName() == null ? 0 : family.getName().length; 1014 if (flength > Byte.MAX_VALUE) { 1015 throw new IllegalArgumentException( 1016 "The length of family name is bigger than " + Byte.MAX_VALUE); 1017 } 1018 if (hasColumnFamily(family.getName())) { 1019 throw new IllegalArgumentException( 1020 "Family '" + family.getNameAsString() + "' already exists so cannot be added"); 1021 } 1022 return putColumnFamily(family); 1023 } 1024 1025 /** 1026 * Modifies the existing column family. 1027 * @param family to update 1028 * @return this (for chained invocation) 1029 */ 1030 public ModifyableTableDescriptor modifyColumnFamily(final ColumnFamilyDescriptor family) { 1031 if (family.getName() == null || family.getName().length <= 0) { 1032 throw new IllegalArgumentException("Family name cannot be null or empty"); 1033 } 1034 if (!hasColumnFamily(family.getName())) { 1035 throw new IllegalArgumentException( 1036 "Column family '" + family.getNameAsString() + "' does not exist"); 1037 } 1038 return putColumnFamily(family); 1039 } 1040 1041 private ModifyableTableDescriptor putColumnFamily(ColumnFamilyDescriptor family) { 1042 families.put(family.getName(), family); 1043 return this; 1044 } 1045 1046 /** 1047 * Checks to see if this table contains the given column family 1048 * @param familyName Family name or column name. 1049 * @return true if the table contains the specified family name 1050 */ 1051 @Override 1052 public boolean hasColumnFamily(final byte[] familyName) { 1053 return families.containsKey(familyName); 1054 } 1055 1056 /** Returns Name of this table and then a map of all of the column family descriptors. */ 1057 @Override 1058 public String toString() { 1059 StringBuilder s = new StringBuilder(); 1060 s.append('\'').append(Bytes.toString(name.getName())).append('\''); 1061 s.append(getValues(true)); 1062 families.values().forEach(f -> s.append(", ").append(f)); 1063 return s.toString(); 1064 } 1065 1066 /** 1067 * @return Name of this table and then a map of all of the column family descriptors (with only 1068 * the non-default column family attributes) 1069 */ 1070 @Override 1071 public String toStringCustomizedValues() { 1072 StringBuilder s = new StringBuilder(); 1073 s.append('\'').append(Bytes.toString(name.getName())).append('\''); 1074 s.append(getValues(false)); 1075 families.values().forEach(hcd -> s.append(", ").append(hcd.toStringCustomizedValues())); 1076 return s.toString(); 1077 } 1078 1079 /** Returns map of all table attributes formatted into string. */ 1080 public String toStringTableAttributes() { 1081 return getValues(true).toString(); 1082 } 1083 1084 private StringBuilder getValues(boolean printDefaults) { 1085 StringBuilder s = new StringBuilder(); 1086 1087 // step 1: set partitioning and pruning 1088 Set<Bytes> reservedKeys = new TreeSet<>(); 1089 Set<Bytes> userKeys = new TreeSet<>(); 1090 for (Map.Entry<Bytes, Bytes> entry : values.entrySet()) { 1091 if (entry.getKey() == null || entry.getKey().get() == null) { 1092 continue; 1093 } 1094 String key = Bytes.toString(entry.getKey().get()); 1095 // in this section, print out reserved keywords + coprocessor info 1096 if (!RESERVED_KEYWORDS.contains(entry.getKey()) && !key.startsWith("coprocessor$")) { 1097 userKeys.add(entry.getKey()); 1098 continue; 1099 } 1100 // only print out IS_META if true 1101 String value = Bytes.toString(entry.getValue().get()); 1102 if (key.equalsIgnoreCase(IS_META)) { 1103 if (Boolean.valueOf(value) == false) { 1104 continue; 1105 } 1106 } 1107 // see if a reserved key is a default value. may not want to print it out 1108 if ( 1109 printDefaults || !DEFAULT_VALUES.containsKey(key) 1110 || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value) 1111 ) { 1112 reservedKeys.add(entry.getKey()); 1113 } 1114 } 1115 1116 // early exit optimization 1117 boolean hasAttributes = !reservedKeys.isEmpty() || !userKeys.isEmpty(); 1118 if (!hasAttributes) { 1119 return s; 1120 } 1121 1122 s.append(", {"); 1123 // step 2: printing attributes 1124 if (hasAttributes) { 1125 s.append("TABLE_ATTRIBUTES => {"); 1126 1127 // print all reserved keys first 1128 boolean printCommaForAttr = false; 1129 for (Bytes k : reservedKeys) { 1130 String key = Bytes.toString(k.get()); 1131 String value = Bytes.toStringBinary(values.get(k).get()); 1132 if (printCommaForAttr) { 1133 s.append(", "); 1134 } 1135 printCommaForAttr = true; 1136 s.append(key); 1137 s.append(" => "); 1138 s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\''); 1139 } 1140 1141 if (!userKeys.isEmpty()) { 1142 // print all non-reserved as a separate subset 1143 if (printCommaForAttr) { 1144 s.append(", "); 1145 } 1146 s.append(HConstants.METADATA).append(" => "); 1147 s.append("{"); 1148 boolean printCommaForCfg = false; 1149 for (Bytes k : userKeys) { 1150 String key = Bytes.toString(k.get()); 1151 String value = Bytes.toStringBinary(values.get(k).get()); 1152 if (printCommaForCfg) { 1153 s.append(", "); 1154 } 1155 printCommaForCfg = true; 1156 s.append('\'').append(key).append('\''); 1157 s.append(" => "); 1158 s.append('\'').append(PrettyPrinter.format(value, getUnit(key))).append('\''); 1159 } 1160 s.append("}"); 1161 } 1162 1163 s.append("}"); 1164 } 1165 1166 s.append("}"); // end METHOD 1167 return s; 1168 } 1169 1170 /** 1171 * Compare the contents of the descriptor with another one passed as a parameter. Checks if the 1172 * obj passed is an instance of ModifyableTableDescriptor, if yes then the contents of the 1173 * descriptors are compared. 1174 * @param obj The object to compare 1175 * @return true if the contents of the the two descriptors exactly match 1176 * @see java.lang.Object#equals(java.lang.Object) 1177 */ 1178 @Override 1179 public boolean equals(Object obj) { 1180 if (this == obj) { 1181 return true; 1182 } 1183 if (obj instanceof ModifyableTableDescriptor) { 1184 return TableDescriptor.COMPARATOR.compare(this, (ModifyableTableDescriptor) obj) == 0; 1185 } 1186 return false; 1187 } 1188 1189 /** Returns hash code */ 1190 @Override 1191 public int hashCode() { 1192 int result = this.name.hashCode(); 1193 if (this.families.size() > 0) { 1194 for (ColumnFamilyDescriptor e : this.families.values()) { 1195 result ^= e.hashCode(); 1196 } 1197 } 1198 result ^= values.hashCode(); 1199 return result; 1200 } 1201 1202 // Comparable 1203 /** 1204 * Compares the descriptor with another descriptor which is passed as a parameter. This compares 1205 * the content of the two descriptors and not the reference. 1206 * @param other The MTD to compare 1207 * @return 0 if the contents of the descriptors are exactly matching, 1 if there is a mismatch 1208 * in the contents 1209 */ 1210 @Override 1211 public int compareTo(final ModifyableTableDescriptor other) { 1212 return TableDescriptor.COMPARATOR.compare(this, other); 1213 } 1214 1215 @Override 1216 public ColumnFamilyDescriptor[] getColumnFamilies() { 1217 return families.values().toArray(new ColumnFamilyDescriptor[families.size()]); 1218 } 1219 1220 /** 1221 * Returns the configured replicas per region 1222 */ 1223 @Override 1224 public int getRegionReplication() { 1225 return getOrDefault(REGION_REPLICATION_KEY, Integer::valueOf, DEFAULT_REGION_REPLICATION); 1226 } 1227 1228 /** 1229 * Sets the number of replicas per region. 1230 * @param regionReplication the replication factor per region 1231 * @return the modifyable TD 1232 */ 1233 public ModifyableTableDescriptor setRegionReplication(int regionReplication) { 1234 return setValue(REGION_REPLICATION_KEY, Integer.toString(regionReplication)); 1235 } 1236 1237 /** Returns true if the read-replicas memstore replication is enabled. */ 1238 @Override 1239 public boolean hasRegionMemStoreReplication() { 1240 return getOrDefault(REGION_MEMSTORE_REPLICATION_KEY, Boolean::valueOf, 1241 DEFAULT_REGION_MEMSTORE_REPLICATION); 1242 } 1243 1244 /** 1245 * Enable or Disable the memstore replication from the primary region to the replicas. The 1246 * replication will be used only for meta operations (e.g. flush, compaction, ...) 1247 * @param memstoreReplication true if the new data written to the primary region should be 1248 * replicated. false if the secondaries can tollerate to have new 1249 * data only when the primary flushes the memstore. 1250 * @return the modifyable TD 1251 */ 1252 public ModifyableTableDescriptor setRegionMemStoreReplication(boolean memstoreReplication) { 1253 return setValue(REGION_MEMSTORE_REPLICATION_KEY, Boolean.toString(memstoreReplication)); 1254 } 1255 1256 public ModifyableTableDescriptor setPriority(int priority) { 1257 return setValue(PRIORITY_KEY, Integer.toString(priority)); 1258 } 1259 1260 @Override 1261 public int getPriority() { 1262 return getOrDefault(PRIORITY_KEY, Integer::valueOf, DEFAULT_PRIORITY); 1263 } 1264 1265 /** 1266 * Returns all the column family names of the current table. The map of TableDescriptor contains 1267 * mapping of family name to ColumnFamilyDescriptor. This returns all the keys of the family map 1268 * which represents the column family names of the table. 1269 * @return Immutable sorted set of the keys of the families. 1270 */ 1271 @Override 1272 public Set<byte[]> getColumnFamilyNames() { 1273 return Collections.unmodifiableSet(this.families.keySet()); 1274 } 1275 1276 /** 1277 * Returns the ColumnFamilyDescriptor for a specific column family with name as specified by the 1278 * parameter column. 1279 * @param column Column family name 1280 * @return Column descriptor for the passed family name or the family on passed in column. 1281 */ 1282 @Override 1283 public ColumnFamilyDescriptor getColumnFamily(final byte[] column) { 1284 return this.families.get(column); 1285 } 1286 1287 /** 1288 * Removes the ColumnFamilyDescriptor with name specified by the parameter column from the table 1289 * descriptor 1290 * @param column Name of the column family to be removed. 1291 * @return Column descriptor for the passed family name or the family on passed in column. 1292 */ 1293 public ColumnFamilyDescriptor removeColumnFamily(final byte[] column) { 1294 return this.families.remove(column); 1295 } 1296 1297 /** 1298 * Add a table coprocessor to this table. The coprocessor type must be 1299 * org.apache.hadoop.hbase.coprocessor.RegionObserver or Endpoint. It won't check if the class 1300 * can be loaded or not. Whether a coprocessor is loadable or not will be determined when a 1301 * region is opened. 1302 * @param className Full class name. 1303 * @return the modifyable TD 1304 */ 1305 public ModifyableTableDescriptor setCoprocessor(String className) throws IOException { 1306 return setCoprocessor(CoprocessorDescriptorBuilder.newBuilder(className) 1307 .setPriority(Coprocessor.PRIORITY_USER).build()); 1308 } 1309 1310 /** 1311 * Add a table coprocessor to this table. The coprocessor type must be 1312 * org.apache.hadoop.hbase.coprocessor.RegionObserver or Endpoint. It won't check if the class 1313 * can be loaded or not. Whether a coprocessor is loadable or not will be determined when a 1314 * region is opened. 1315 * @throws IOException any illegal parameter key/value 1316 * @return the modifyable TD 1317 */ 1318 public ModifyableTableDescriptor setCoprocessor(CoprocessorDescriptor cp) throws IOException { 1319 checkHasCoprocessor(cp.getClassName()); 1320 if (cp.getPriority() < 0) { 1321 throw new IOException( 1322 "Priority must be bigger than or equal with zero, current:" + cp.getPriority()); 1323 } 1324 // Validate parameter kvs and then add key/values to kvString. 1325 StringBuilder kvString = new StringBuilder(); 1326 for (Map.Entry<String, String> e : cp.getProperties().entrySet()) { 1327 if (!e.getKey().matches(CP_HTD_ATTR_VALUE_PARAM_KEY_PATTERN)) { 1328 throw new IOException("Illegal parameter key = " + e.getKey()); 1329 } 1330 if (!e.getValue().matches(CP_HTD_ATTR_VALUE_PARAM_VALUE_PATTERN)) { 1331 throw new IOException("Illegal parameter (" + e.getKey() + ") value = " + e.getValue()); 1332 } 1333 if (kvString.length() != 0) { 1334 kvString.append(','); 1335 } 1336 kvString.append(e.getKey()); 1337 kvString.append('='); 1338 kvString.append(e.getValue()); 1339 } 1340 1341 String value = cp.getJarPath().orElse("") + "|" + cp.getClassName() + "|" 1342 + Integer.toString(cp.getPriority()) + "|" + kvString.toString(); 1343 return setCoprocessorToMap(value); 1344 } 1345 1346 /** 1347 * Add a table coprocessor to this table. The coprocessor type must be 1348 * org.apache.hadoop.hbase.coprocessor.RegionObserver or Endpoint. It won't check if the class 1349 * can be loaded or not. Whether a coprocessor is loadable or not will be determined when a 1350 * region is opened. 1351 * @param specStr The Coprocessor specification all in in one String 1352 * @return the modifyable TD 1353 * @deprecated used by HTableDescriptor and admin.rb. As of release 2.0.0, this will be removed 1354 * in HBase 3.0.0. 1355 */ 1356 @Deprecated 1357 public ModifyableTableDescriptor setCoprocessorWithSpec(final String specStr) 1358 throws IOException { 1359 CoprocessorDescriptor cpDesc = 1360 toCoprocessorDescriptor(specStr).orElseThrow(() -> new IllegalArgumentException( 1361 "Format does not match " + CP_HTD_ATTR_VALUE_PATTERN + ": " + specStr)); 1362 checkHasCoprocessor(cpDesc.getClassName()); 1363 return setCoprocessorToMap(specStr); 1364 } 1365 1366 private void checkHasCoprocessor(final String className) throws IOException { 1367 if (hasCoprocessor(className)) { 1368 throw new IOException("Coprocessor " + className + " already exists."); 1369 } 1370 } 1371 1372 /** 1373 * Add coprocessor to values Map 1374 * @param specStr The Coprocessor specification all in in one String 1375 * @return Returns <code>this</code> 1376 */ 1377 private ModifyableTableDescriptor setCoprocessorToMap(final String specStr) { 1378 if (specStr == null) { 1379 return this; 1380 } 1381 // generate a coprocessor key 1382 int maxCoprocessorNumber = 0; 1383 Matcher keyMatcher; 1384 for (Map.Entry<Bytes, Bytes> e : this.values.entrySet()) { 1385 keyMatcher = CP_HTD_ATTR_KEY_PATTERN.matcher(Bytes.toString(e.getKey().get())); 1386 if (!keyMatcher.matches()) { 1387 continue; 1388 } 1389 maxCoprocessorNumber = 1390 Math.max(Integer.parseInt(keyMatcher.group(1)), maxCoprocessorNumber); 1391 } 1392 maxCoprocessorNumber++; 1393 String key = "coprocessor$" + Integer.toString(maxCoprocessorNumber); 1394 return setValue(new Bytes(Bytes.toBytes(key)), new Bytes(Bytes.toBytes(specStr))); 1395 } 1396 1397 /** 1398 * Check if the table has an attached co-processor represented by the name className 1399 * @param classNameToMatch - Class name of the co-processor 1400 * @return true of the table has a co-processor className 1401 */ 1402 @Override 1403 public boolean hasCoprocessor(String classNameToMatch) { 1404 return getCoprocessorDescriptors().stream() 1405 .anyMatch(cp -> cp.getClassName().equals(classNameToMatch)); 1406 } 1407 1408 /** 1409 * Return the list of attached co-processor represented by their name className 1410 * @return The list of co-processors classNames 1411 */ 1412 @Override 1413 public List<CoprocessorDescriptor> getCoprocessorDescriptors() { 1414 List<CoprocessorDescriptor> result = new ArrayList<>(); 1415 for (Map.Entry<Bytes, Bytes> e : getValues().entrySet()) { 1416 String key = Bytes.toString(e.getKey().get()).trim(); 1417 if (CP_HTD_ATTR_KEY_PATTERN.matcher(key).matches()) { 1418 toCoprocessorDescriptor(Bytes.toString(e.getValue().get()).trim()).ifPresent(result::add); 1419 } 1420 } 1421 return result; 1422 } 1423 1424 /** 1425 * Remove a coprocessor from those set on the table 1426 * @param className Class name of the co-processor 1427 */ 1428 public void removeCoprocessor(String className) { 1429 Bytes match = null; 1430 Matcher keyMatcher; 1431 Matcher valueMatcher; 1432 for (Map.Entry<Bytes, Bytes> e : this.values.entrySet()) { 1433 keyMatcher = CP_HTD_ATTR_KEY_PATTERN.matcher(Bytes.toString(e.getKey().get())); 1434 if (!keyMatcher.matches()) { 1435 continue; 1436 } 1437 valueMatcher = CP_HTD_ATTR_VALUE_PATTERN.matcher(Bytes.toString(e.getValue().get())); 1438 if (!valueMatcher.matches()) { 1439 continue; 1440 } 1441 // get className and compare 1442 String clazz = valueMatcher.group(2).trim(); // classname is the 2nd field 1443 // remove the CP if it is present 1444 if (clazz.equals(className.trim())) { 1445 match = e.getKey(); 1446 break; 1447 } 1448 } 1449 // if we found a match, remove it 1450 if (match != null) { 1451 ModifyableTableDescriptor.this.removeValue(match); 1452 } else { 1453 throw new IllegalArgumentException(String.format( 1454 "coprocessor with class name %s was not found in the table attribute", className)); 1455 } 1456 } 1457 1458 /** Returns the bytes in pb format */ 1459 private byte[] toByteArray() { 1460 return ProtobufUtil.prependPBMagic(ProtobufUtil.toTableSchema(this).toByteArray()); 1461 } 1462 1463 /** 1464 * @param bytes A pb serialized {@link ModifyableTableDescriptor} instance with pb magic prefix 1465 * @return An instance of {@link ModifyableTableDescriptor} made from <code>bytes</code> 1466 * @see #toByteArray() 1467 */ 1468 private static TableDescriptor parseFrom(final byte[] bytes) throws DeserializationException { 1469 if (!ProtobufUtil.isPBMagicPrefix(bytes)) { 1470 throw new DeserializationException("Expected PB encoded ModifyableTableDescriptor"); 1471 } 1472 int pblen = ProtobufUtil.lengthOfPBMagic(); 1473 HBaseProtos.TableSchema.Builder builder = HBaseProtos.TableSchema.newBuilder(); 1474 try { 1475 ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen); 1476 return ProtobufUtil.toTableDescriptor(builder.build()); 1477 } catch (IOException e) { 1478 throw new DeserializationException(e); 1479 } 1480 } 1481 1482 @Override 1483 public int getColumnFamilyCount() { 1484 return families.size(); 1485 } 1486 1487 @Override 1488 public Optional<String> getRegionServerGroup() { 1489 Bytes value = values.get(RSGROUP_KEY); 1490 if (value != null) { 1491 return Optional.of(Bytes.toString(value.get(), value.getOffset(), value.getLength())); 1492 } else { 1493 return Optional.empty(); 1494 } 1495 } 1496 } 1497 1498 /** 1499 * This method is mostly intended for internal use. However, it it also relied on by hbase-shell 1500 * for backwards compatibility. 1501 */ 1502 private static Optional<CoprocessorDescriptor> toCoprocessorDescriptor(String spec) { 1503 Matcher matcher = CP_HTD_ATTR_VALUE_PATTERN.matcher(spec); 1504 if (matcher.matches()) { 1505 // jar file path can be empty if the cp class can be loaded 1506 // from class loader. 1507 String path = matcher.group(1).trim().isEmpty() ? null : matcher.group(1).trim(); 1508 String className = matcher.group(2).trim(); 1509 if (className.isEmpty()) { 1510 return Optional.empty(); 1511 } 1512 String priorityStr = matcher.group(3).trim(); 1513 int priority = 1514 priorityStr.isEmpty() ? Coprocessor.PRIORITY_USER : Integer.parseInt(priorityStr); 1515 String cfgSpec = null; 1516 try { 1517 cfgSpec = matcher.group(4); 1518 } catch (IndexOutOfBoundsException ex) { 1519 // ignore 1520 } 1521 Map<String, String> ourConf = new TreeMap<>(); 1522 if (cfgSpec != null && !cfgSpec.trim().equals("|")) { 1523 cfgSpec = cfgSpec.substring(cfgSpec.indexOf('|') + 1); 1524 Matcher m = CP_HTD_ATTR_VALUE_PARAM_PATTERN.matcher(cfgSpec); 1525 while (m.find()) { 1526 ourConf.put(m.group(1), m.group(2)); 1527 } 1528 } 1529 return Optional.of(CoprocessorDescriptorBuilder.newBuilder(className).setJarPath(path) 1530 .setPriority(priority).setProperties(ourConf).build()); 1531 } 1532 return Optional.empty(); 1533 } 1534}