HBASE-23892 SecureTestCluster should allow its subclasses to pass their Class referen...
[hbase.git] / hbase-common / src / main / java / org / apache / hadoop / hbase / TableName.java
blobf096ef9a7ab61c226e31511cac6cf9183c49dd29
1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package org.apache.hadoop.hbase;
21 import java.nio.ByteBuffer;
22 import java.nio.charset.StandardCharsets;
23 import java.util.Arrays;
24 import java.util.Set;
25 import java.util.concurrent.CopyOnWriteArraySet;
27 import org.apache.hadoop.hbase.util.Bytes;
28 import org.apache.yetus.audience.InterfaceAudience;
30 /**
31 * Immutable POJO class for representing a table name.
32 * Which is of the form:
33 * <table namespace>:<table qualifier>
35 * Two special namespaces:
37 * 1. hbase - system namespace, used to contain hbase internal tables
38 * 2. default - tables with no explicit specified namespace will
39 * automatically fall into this namespace.
41 * ie
43 * a) foo:bar, means namespace=foo and qualifier=bar
44 * b) bar, means namespace=default and qualifier=bar
45 * c) default:bar, means namespace=default and qualifier=bar
47 * <p>
48 * Internally, in this class, we cache the instances to limit the number of objects and
49 * make the "equals" faster. We try to minimize the number of objects created of
50 * the number of array copy to check if we already have an instance of this TableName. The code
51 * is not optimize for a new instance creation but is optimized to check for existence.
52 * </p>
54 @InterfaceAudience.Public
55 public final class TableName implements Comparable<TableName> {
57 /** See {@link #createTableNameIfNecessary(ByteBuffer, ByteBuffer)} */
58 private static final Set<TableName> tableCache = new CopyOnWriteArraySet<>();
60 /** Namespace delimiter */
61 //this should always be only 1 byte long
62 public final static char NAMESPACE_DELIM = ':';
64 // A non-capture group so that this can be embedded.
65 // regex is a bit more complicated to support nuance of tables
66 // in default namespace
67 //Allows only letters, digits and '_'
68 public static final String VALID_NAMESPACE_REGEX =
69 "(?:[_\\p{Digit}\\p{IsAlphabetic}]+)";
70 //Allows only letters, digits, '_', '-' and '.'
71 public static final String VALID_TABLE_QUALIFIER_REGEX =
72 "(?:[_\\p{Digit}\\p{IsAlphabetic}][-_.\\p{Digit}\\p{IsAlphabetic}]*)";
73 //Concatenation of NAMESPACE_REGEX and TABLE_QUALIFIER_REGEX,
74 //with NAMESPACE_DELIM as delimiter
75 public static final String VALID_USER_TABLE_REGEX =
76 "(?:(?:(?:"+VALID_NAMESPACE_REGEX+"\\"+NAMESPACE_DELIM+")?)" +
77 "(?:"+VALID_TABLE_QUALIFIER_REGEX+"))";
79 /** The hbase:meta table's name. */
80 public static final TableName META_TABLE_NAME =
81 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta");
83 /**
84 * The Namespace table's name.
85 * @deprecated since 3.0.0 and will be removed in 4.0.0. We have folded the data in namespace
86 * table into meta table, so do not use it any more.
87 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21154">HBASE-21154</a>
89 @Deprecated
90 public static final TableName NAMESPACE_TABLE_NAME =
91 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "namespace");
93 public static final String OLD_META_STR = ".META.";
94 public static final String OLD_ROOT_STR = "-ROOT-";
96 /** One globally disallowed name */
97 public static final String DISALLOWED_TABLE_NAME = "zookeeper";
99 /**
100 * @return True if <code>tn</code> is the hbase:meta table name.
102 public static boolean isMetaTableName(final TableName tn) {
103 return tn.equals(TableName.META_TABLE_NAME);
107 * TableName for old -ROOT- table. It is used to read/process old WALs which have
108 * ROOT edits.
110 public static final TableName OLD_ROOT_TABLE_NAME = getADummyTableName(OLD_ROOT_STR);
112 * TableName for old .META. table. Used in testing.
114 public static final TableName OLD_META_TABLE_NAME = getADummyTableName(OLD_META_STR);
116 private final byte[] name;
117 private final String nameAsString;
118 private final byte[] namespace;
119 private final String namespaceAsString;
120 private final byte[] qualifier;
121 private final String qualifierAsString;
122 private final boolean systemTable;
123 private final int hashCode;
126 * Check passed byte array, "tableName", is legal user-space table name.
127 * @return Returns passed <code>tableName</code> param
128 * @throws IllegalArgumentException if passed a tableName is null or
129 * is made of other than 'word' characters or underscores: i.e.
130 * <code>[\p{IsAlphabetic}\p{Digit}.-:]</code>. The ':' is used to delimit the namespace
131 * from the table name and can be used for nothing else.
133 * Namespace names can only contain 'word' characters
134 * <code>[\p{IsAlphabetic}\p{Digit}]</code> or '_'
136 * Qualifier names can only contain 'word' characters
137 * <code>[\p{IsAlphabetic}\p{Digit}]</code> or '_', '.' or '-'.
138 * The name may not start with '.' or '-'.
140 * Valid fully qualified table names:
141 * foo:bar, namespace=&gt;foo, table=&gt;bar
142 * org:foo.bar, namespace=org, table=&gt;foo.bar
144 public static byte [] isLegalFullyQualifiedTableName(final byte[] tableName) {
145 if (tableName == null || tableName.length <= 0) {
146 throw new IllegalArgumentException("Name is null or empty");
149 int namespaceDelimIndex =
150 org.apache.hbase.thirdparty.com.google.common.primitives.Bytes.lastIndexOf(tableName,
151 (byte) NAMESPACE_DELIM);
152 if (namespaceDelimIndex < 0){
153 isLegalTableQualifierName(tableName);
154 } else {
155 isLegalNamespaceName(tableName, 0, namespaceDelimIndex);
156 isLegalTableQualifierName(tableName, namespaceDelimIndex + 1, tableName.length);
158 return tableName;
161 public static byte [] isLegalTableQualifierName(final byte[] qualifierName) {
162 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, false);
163 return qualifierName;
166 public static byte [] isLegalTableQualifierName(final byte[] qualifierName, boolean isSnapshot) {
167 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, isSnapshot);
168 return qualifierName;
173 * Qualifier names can only contain 'word' characters
174 * <code>[\p{IsAlphabetic}\p{Digit}]</code> or '_', '.' or '-'.
175 * The name may not start with '.' or '-'.
177 * @param qualifierName byte array containing the qualifier name
178 * @param start start index
179 * @param end end index (exclusive)
181 public static void isLegalTableQualifierName(final byte[] qualifierName,
182 int start,
183 int end) {
184 isLegalTableQualifierName(qualifierName, start, end, false);
187 public static void isLegalTableQualifierName(final byte[] qualifierName,
188 int start,
189 int end,
190 boolean isSnapshot) {
191 if(end - start < 1) {
192 throw new IllegalArgumentException(isSnapshot ? "Snapshot" : "Table" + " qualifier must not be empty");
194 if (qualifierName[start] == '.' || qualifierName[start] == '-') {
195 throw new IllegalArgumentException("Illegal first character <" + qualifierName[start] +
196 "> at 0. " + (isSnapshot ? "Snapshot" : "User-space table") +
197 " qualifiers can only start with 'alphanumeric " +
198 "characters' from any language: " +
199 Bytes.toString(qualifierName, start, end));
201 // Treat the bytes as UTF-8
202 String qualifierString = new String(
203 qualifierName, start, (end - start), StandardCharsets.UTF_8);
204 if (qualifierString.equals(DISALLOWED_TABLE_NAME)) {
205 // Per https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel
206 // A znode named "zookeeper" is disallowed by zookeeper.
207 throw new IllegalArgumentException("Tables may not be named '" + DISALLOWED_TABLE_NAME + "'");
209 for (int i = 0; i < qualifierString.length(); i++) {
210 // Treat the string as a char-array as some characters may be multi-byte
211 char c = qualifierString.charAt(i);
212 // Check for letter, digit, underscore, hyphen, or period, and allowed by ZK.
213 // ZooKeeper also has limitations, but Character.isAlphabetic omits those all
214 // See https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel
215 if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '_' || c == '-' || c == '.') {
216 continue;
218 throw new IllegalArgumentException("Illegal character code:" + (int) c + ", <" + c + "> at " +
219 i + ". " + (isSnapshot ? "Snapshot" : "User-space table") +
220 " qualifiers may only contain 'alphanumeric characters' and digits: " +
221 qualifierString);
225 public static void isLegalNamespaceName(byte[] namespaceName) {
226 isLegalNamespaceName(namespaceName, 0, namespaceName.length);
230 * Valid namespace characters are alphabetic characters, numbers, and underscores.
232 public static void isLegalNamespaceName(final byte[] namespaceName,
233 final int start,
234 final int end) {
235 if(end - start < 1) {
236 throw new IllegalArgumentException("Namespace name must not be empty");
238 String nsString = new String(namespaceName, start, (end - start), StandardCharsets.UTF_8);
239 if (nsString.equals(DISALLOWED_TABLE_NAME)) {
240 // Per https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel
241 // A znode named "zookeeper" is disallowed by zookeeper.
242 throw new IllegalArgumentException("Tables may not be named '" + DISALLOWED_TABLE_NAME + "'");
244 for (int i = 0; i < nsString.length(); i++) {
245 // Treat the string as a char-array as some characters may be multi-byte
246 char c = nsString.charAt(i);
247 // ZooKeeper also has limitations, but Character.isAlphabetic omits those all
248 // See https://zookeeper.apache.org/doc/r3.4.10/zookeeperProgrammers.html#ch_zkDataModel
249 if (Character.isAlphabetic(c) || Character.isDigit(c)|| c == '_') {
250 continue;
252 throw new IllegalArgumentException("Illegal character <" + c +
253 "> at " + i + ". Namespaces may only contain " +
254 "'alphanumeric characters' from any language and digits: " + nsString);
258 public byte[] getName() {
259 return name;
262 public String getNameAsString() {
263 return nameAsString;
266 public byte[] getNamespace() {
267 return namespace;
270 public String getNamespaceAsString() {
271 return namespaceAsString;
275 * Ideally, getNameAsString should contain namespace within it,
276 * but if the namespace is default, it just returns the name. This method
277 * takes care of this corner case.
279 public String getNameWithNamespaceInclAsString() {
280 if(getNamespaceAsString().equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR)) {
281 return NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR +
282 TableName.NAMESPACE_DELIM + getNameAsString();
284 return getNameAsString();
287 public byte[] getQualifier() {
288 return qualifier;
291 public String getQualifierAsString() {
292 return qualifierAsString;
296 * @return A pointer to TableName as String bytes.
298 public byte[] toBytes() {
299 return name;
302 public boolean isSystemTable() {
303 return systemTable;
306 @Override
307 public String toString() {
308 return nameAsString;
313 * @throws IllegalArgumentException See {@link #valueOf(byte[])}
315 private TableName(ByteBuffer namespace, ByteBuffer qualifier) throws IllegalArgumentException {
316 this.qualifier = new byte[qualifier.remaining()];
317 qualifier.duplicate().get(this.qualifier);
318 this.qualifierAsString = Bytes.toString(this.qualifier);
320 if (qualifierAsString.equals(OLD_ROOT_STR)) {
321 throw new IllegalArgumentException(OLD_ROOT_STR + " has been deprecated.");
323 if (qualifierAsString.equals(OLD_META_STR)) {
324 throw new IllegalArgumentException(OLD_META_STR + " no longer exists. The table has been " +
325 "renamed to " + META_TABLE_NAME);
328 if (Bytes.equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME, namespace)) {
329 // Using the same objects: this will make the comparison faster later
330 this.namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
331 this.namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
332 this.systemTable = false;
334 // The name does not include the namespace when it's the default one.
335 this.nameAsString = qualifierAsString;
336 this.name = this.qualifier;
337 } else {
338 if (Bytes.equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME, namespace)) {
339 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
340 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
341 this.systemTable = true;
342 } else {
343 this.namespace = new byte[namespace.remaining()];
344 namespace.duplicate().get(this.namespace);
345 this.namespaceAsString = Bytes.toString(this.namespace);
346 this.systemTable = false;
348 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
349 this.name = Bytes.toBytes(nameAsString);
352 this.hashCode = nameAsString.hashCode();
354 isLegalNamespaceName(this.namespace);
355 isLegalTableQualifierName(this.qualifier);
359 * This is only for the old and meta tables.
361 private TableName(String qualifier) {
362 this.qualifier = Bytes.toBytes(qualifier);
363 this.qualifierAsString = qualifier;
365 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
366 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
367 this.systemTable = true;
369 // WARNING: nameAsString is different than name for old meta & root!
370 // This is by design.
371 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
372 this.name = this.qualifier;
374 this.hashCode = nameAsString.hashCode();
379 * Check that the object does not exist already. There are two reasons for creating the objects
380 * only once:
381 * 1) With 100K regions, the table names take ~20MB.
382 * 2) Equals becomes much faster as it's resolved with a reference and an int comparison.
384 private static TableName createTableNameIfNecessary(ByteBuffer bns, ByteBuffer qns) {
385 for (TableName tn : tableCache) {
386 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
387 return tn;
391 TableName newTable = new TableName(bns, qns);
392 if (tableCache.add(newTable)) { // Adds the specified element if it is not already present
393 return newTable;
396 // Someone else added it. Let's find it.
397 for (TableName tn : tableCache) {
398 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
399 return tn;
402 // this should never happen.
403 throw new IllegalStateException(newTable + " was supposed to be in the cache");
408 * It is used to create table names for old META, and ROOT table.
409 * These tables are not really legal tables. They are not added into the cache.
410 * @return a dummy TableName instance (with no validation) for the passed qualifier
412 private static TableName getADummyTableName(String qualifier) {
413 return new TableName(qualifier);
417 public static TableName valueOf(String namespaceAsString, String qualifierAsString) {
418 if (namespaceAsString == null || namespaceAsString.length() < 1) {
419 namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
422 for (TableName tn : tableCache) {
423 if (qualifierAsString.equals(tn.getQualifierAsString()) &&
424 namespaceAsString.equals(tn.getNamespaceAsString())) {
425 return tn;
429 return createTableNameIfNecessary(
430 ByteBuffer.wrap(Bytes.toBytes(namespaceAsString)),
431 ByteBuffer.wrap(Bytes.toBytes(qualifierAsString)));
436 * @throws IllegalArgumentException if fullName equals old root or old meta. Some code
437 * depends on this. The test is buried in the table creation to save on array comparison
438 * when we're creating a standard table object that will be in the cache.
440 public static TableName valueOf(byte[] fullName) throws IllegalArgumentException{
441 for (TableName tn : tableCache) {
442 if (Arrays.equals(tn.getName(), fullName)) {
443 return tn;
447 int namespaceDelimIndex =
448 org.apache.hbase.thirdparty.com.google.common.primitives.Bytes.lastIndexOf(fullName,
449 (byte) NAMESPACE_DELIM);
451 if (namespaceDelimIndex < 0) {
452 return createTableNameIfNecessary(
453 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
454 ByteBuffer.wrap(fullName));
455 } else {
456 return createTableNameIfNecessary(
457 ByteBuffer.wrap(fullName, 0, namespaceDelimIndex),
458 ByteBuffer.wrap(fullName, namespaceDelimIndex + 1,
459 fullName.length - (namespaceDelimIndex + 1)));
465 * @throws IllegalArgumentException if fullName equals old root or old meta. Some code
466 * depends on this.
468 public static TableName valueOf(String name) {
469 for (TableName tn : tableCache) {
470 if (name.equals(tn.getNameAsString())) {
471 return tn;
475 final int namespaceDelimIndex = name.indexOf(NAMESPACE_DELIM);
477 if (namespaceDelimIndex < 0) {
478 return createTableNameIfNecessary(
479 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
480 ByteBuffer.wrap(Bytes.toBytes(name)));
481 } else {
482 // indexOf is by character, not byte (consider multi-byte characters)
483 String ns = name.substring(0, namespaceDelimIndex);
484 String qualifier = name.substring(namespaceDelimIndex + 1);
485 return createTableNameIfNecessary(
486 ByteBuffer.wrap(Bytes.toBytes(ns)),
487 ByteBuffer.wrap(Bytes.toBytes(qualifier)));
492 public static TableName valueOf(byte[] namespace, byte[] qualifier) {
493 if (namespace == null || namespace.length < 1) {
494 namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
497 for (TableName tn : tableCache) {
498 if (Arrays.equals(tn.getQualifier(), qualifier) &&
499 Arrays.equals(tn.getNamespace(), namespace)) {
500 return tn;
504 return createTableNameIfNecessary(
505 ByteBuffer.wrap(namespace), ByteBuffer.wrap(qualifier));
508 public static TableName valueOf(ByteBuffer namespace, ByteBuffer qualifier) {
509 if (namespace == null || namespace.remaining() < 1) {
510 return createTableNameIfNecessary(
511 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), qualifier);
514 return createTableNameIfNecessary(namespace, qualifier);
517 @Override
518 public boolean equals(Object o) {
519 if (this == o) return true;
520 if (o == null || getClass() != o.getClass()) return false;
522 TableName tableName = (TableName) o;
524 return o.hashCode() == hashCode && nameAsString.equals(tableName.nameAsString);
527 @Override
528 public int hashCode() {
529 return hashCode;
533 * For performance reasons, the ordering is not lexicographic.
535 @Override
536 public int compareTo(TableName tableName) {
537 if (this == tableName) return 0;
538 if (this.hashCode < tableName.hashCode()) {
539 return -1;
541 if (this.hashCode > tableName.hashCode()) {
542 return 1;
544 return this.nameAsString.compareTo(tableName.getNameAsString());