View Javadoc

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
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
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.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import org.apache.hadoop.classification.InterfaceAudience;
22  import org.apache.hadoop.classification.InterfaceStability;
23  import org.apache.hadoop.hbase.util.Bytes;
24  
25  /**
26   * Immutable POJO class for representing a table name.
27   * Which is of the form:
28   * <table namespace>:<table qualifier>
29   *
30   * Two special namespaces:
31   *
32   * 1. hbase - system namespace, used to contain hbase internal tables
33   * 2. default - tables with no explicit specified namespace will
34   * automatically fall into this namespace.
35   *
36   * ie
37   *
38   * a) foo:bar, means namespace=foo and qualifier=bar
39   * b) bar, means namespace=default and qualifier=bar
40   * c) default:bar, means namespace=default and qualifier=bar
41   *
42   */
43  @InterfaceAudience.Public
44  @InterfaceStability.Evolving
45  public final class TableName implements Comparable<TableName> {
46  
47    /** Namespace delimiter */
48    //this should always be only 1 byte long
49    public final static char NAMESPACE_DELIM = ':';
50  
51    // A non-capture group so that this can be embedded.
52    // regex is a bit more complicated to support nuance of tables
53    // in default namespace
54    //Allows only letters, digits and '_'
55    public static final String VALID_NAMESPACE_REGEX =
56        "(?:[a-zA-Z_0-9]+)";
57    //Allows only letters, digits, '_', '-' and '.'
58    public static final String VALID_TABLE_QUALIFIER_REGEX =
59        "(?:[a-zA-Z_0-9][a-zA-Z_0-9-.]*)";
60    //Concatenation of NAMESPACE_REGEX and TABLE_QUALIFIER_REGEX,
61    //with NAMESPACE_DELIM as delimiter
62    public static final String VALID_USER_TABLE_REGEX =
63        "(?:(?:(?:"+VALID_NAMESPACE_REGEX+"\\"+NAMESPACE_DELIM+")?)" +
64           "(?:"+VALID_TABLE_QUALIFIER_REGEX+"))";
65  
66    /** The root table's name.*/
67    public static final TableName ROOT_TABLE_NAME =
68        valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "root");
69  
70    /** The META table's name. */
71    public static final TableName META_TABLE_NAME =
72        valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta");
73  
74    /** The Namespace table's name. */
75    public static final TableName NAMESPACE_TABLE_NAME =
76        valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "namespace");
77  
78    private static final String OLD_META_STR = ".META.";
79    private static final String OLD_ROOT_STR = "-ROOT-";
80  
81    private byte[] name;
82    private String nameAsString;
83    private byte[] namespace;
84    private String namespaceAsString;
85    private byte[] qualifier;
86    private String qualifierAsString;
87  
88    private TableName() {}
89  
90    /**
91     * Check passed byte array, "tableName", is legal user-space table name.
92     * @return Returns passed <code>tableName</code> param
93     * @throws IllegalArgumentException if passed a tableName is null or
94     * is made of other than 'word' characters or underscores: i.e.
95     * <code>[a-zA-Z_0-9.-:]</code>. The ':' is used to delimit the namespace
96     * from the table name and can be used for nothing else.
97     *
98     * Namespace names can only contain 'word' characters
99     * <code>[a-zA-Z_0-9]</code> or '_'
100    *
101    * Qualifier names can only contain 'word' characters
102    * <code>[a-zA-Z_0-9]</code> or '_', '.' or '-'.
103    * The name may not start with '.' or '-'.
104    *
105    * Valid fully qualified table names:
106    * foo:bar, namespace=>foo, table=>bar
107    * org:foo.bar, namespace=org, table=>foo.bar
108    */
109   public static byte [] isLegalFullyQualifiedTableName(final byte[] tableName) {
110     if (tableName == null || tableName.length <= 0) {
111       throw new IllegalArgumentException("Name is null or empty");
112     }
113     int namespaceDelimIndex = com.google.common.primitives.Bytes.lastIndexOf(tableName,
114         (byte) NAMESPACE_DELIM);
115     if (namespaceDelimIndex == 0 || namespaceDelimIndex == -1){
116       isLegalTableQualifierName(tableName);
117     } else {
118       isLegalNamespaceName(tableName, 0, namespaceDelimIndex);
119       isLegalTableQualifierName(tableName, namespaceDelimIndex + 1, tableName.length);
120     }
121     return tableName;
122   }
123 
124   public static void isLegalTableQualifierName(final byte[] qualifierName){
125     isLegalTableQualifierName(qualifierName, 0, qualifierName.length);
126   }
127 
128   /**
129    * Qualifier names can only contain 'word' characters
130    * <code>[a-zA-Z_0-9]</code> or '_', '.' or '-'.
131    * The name may not start with '.' or '-'.
132    *
133    * @param qualifierName byte array containing the qualifier name
134    * @param start start index
135    * @param end end index (exclusive)
136    */
137   public static void isLegalTableQualifierName(final byte[] qualifierName,
138                                                 int start,
139                                                 int end){
140     if(end - start < 1) {
141       throw new IllegalArgumentException("Table qualifier must not be empty");
142     }
143     if (qualifierName[start] == '.' || qualifierName[start] == '-') {
144       throw new IllegalArgumentException("Illegal first character <" + qualifierName[0] +
145           "> at 0. Namespaces can only start with alphanumeric " +
146           "characters': i.e. [a-zA-Z_0-9]: " + Bytes.toString(qualifierName));
147     }
148     for (int i = start; i < end; i++) {
149       if (Character.isLetterOrDigit(qualifierName[i]) ||
150           qualifierName[i] == '_' ||
151           qualifierName[i] == '-' ||
152           qualifierName[i] == '.') {
153         continue;
154       }
155       throw new IllegalArgumentException("Illegal character <" + qualifierName[i] +
156         "> at " + i + ". User-space table qualifiers can only contain " +
157         "'alphanumeric characters': i.e. [a-zA-Z_0-9-.]: " +
158           Bytes.toString(qualifierName, start, end));
159     }
160   }
161 
162   public static void isLegalNamespaceName(byte[] namespaceName) {
163     isLegalNamespaceName(namespaceName, 0, namespaceName.length);
164   }
165 
166   /**
167    * Valid namespace characters are [a-zA-Z_0-9]
168    * @param namespaceName
169    * @param offset
170    * @param length
171    */
172   public static void isLegalNamespaceName(byte[] namespaceName, int offset, int length) {
173     for (int i = offset; i < length; i++) {
174       if (Character.isLetterOrDigit(namespaceName[i])|| namespaceName[i] == '_') {
175         continue;
176       }
177       throw new IllegalArgumentException("Illegal character <" + namespaceName[i] +
178         "> at " + i + ". Namespaces can only contain " +
179         "'alphanumeric characters': i.e. [a-zA-Z_0-9]: " + Bytes.toString(namespaceName,
180           offset, length));
181     }
182   }
183 
184   public byte[] getName() {
185     return name;
186   }
187 
188   public String getNameAsString() {
189     return nameAsString;
190   }
191 
192   public byte[] getNamespace() {
193     return namespace;
194   }
195 
196   public String getNamespaceAsString() {
197     return namespaceAsString;
198   }
199 
200   public byte[] getQualifier() {
201     return qualifier;
202   }
203 
204   public String getQualifierAsString() {
205     return qualifierAsString;
206   }
207 
208   public byte[] toBytes() {
209     return name;
210   }
211 
212   @Override
213   public String toString() {
214     return nameAsString;
215   }
216 
217   public static TableName valueOf(byte[] namespace, byte[] qualifier) {
218     TableName ret = new TableName();
219     if(namespace == null || namespace.length < 1) {
220       namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
221     }
222     ret.namespace = namespace;
223     ret.namespaceAsString = Bytes.toString(namespace);
224     ret.qualifier = qualifier;
225     ret.qualifierAsString = Bytes.toString(qualifier);
226 
227     finishValueOf(ret);
228 
229     return ret;
230   }
231 
232   public static TableName valueOf(String namespaceAsString, String qualifierAsString) {
233     TableName ret = new TableName();
234     if(namespaceAsString == null || namespaceAsString.length() < 1) {
235       namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
236     }
237     ret.namespaceAsString = namespaceAsString;
238     ret.namespace = Bytes.toBytes(namespaceAsString);
239     ret.qualifier = Bytes.toBytes(qualifierAsString);
240     ret.qualifierAsString = qualifierAsString;
241 
242     finishValueOf(ret);
243 
244     return ret;
245   }
246 
247   private static void finishValueOf(TableName tableName) {
248     isLegalNamespaceName(tableName.namespace);
249     isLegalTableQualifierName(tableName.qualifier);
250 
251     tableName.nameAsString =
252         createFullyQualified(tableName.namespaceAsString, tableName.qualifierAsString);
253     tableName.name = Bytes.toBytes(tableName.nameAsString);
254   }
255 
256   public static TableName valueOf(byte[] name) {
257     return valueOf(Bytes.toString(name));
258   }
259 
260   public static TableName valueOf(String name) {
261     if(name.equals(OLD_ROOT_STR)) {
262       throw new IllegalArgumentException(OLD_ROOT_STR + " has been deprecated.");
263     }
264     if(name.equals(OLD_META_STR)) {
265       throw new IllegalArgumentException(OLD_META_STR + " no longer exists. The table has been " +
266           "renamed to "+META_TABLE_NAME);
267     }
268 
269     isLegalFullyQualifiedTableName(Bytes.toBytes(name));
270     int index = name.indexOf(NAMESPACE_DELIM);
271     if (index != -1) {
272       return TableName.valueOf(name.substring(0, index), name.substring(index + 1));
273     }
274     return TableName.valueOf(NamespaceDescriptor.DEFAULT_NAMESPACE.getName(), name);
275   }
276 
277   private static String createFullyQualified(String namespace, String tableQualifier) {
278     if (namespace.equals(NamespaceDescriptor.DEFAULT_NAMESPACE.getName())) {
279       return tableQualifier;
280     }
281     return namespace+NAMESPACE_DELIM+tableQualifier;
282   }
283 
284   @Override
285   public boolean equals(Object o) {
286     if (this == o) return true;
287     if (o == null || getClass() != o.getClass()) return false;
288 
289     TableName tableName = (TableName) o;
290 
291     if (!nameAsString.equals(tableName.nameAsString)) return false;
292 
293     return true;
294   }
295 
296   @Override
297   public int hashCode() {
298     int result = nameAsString.hashCode();
299     return result;
300   }
301 
302   @Override
303   public int compareTo(TableName tableName) {
304     return this.nameAsString.compareTo(tableName.getNameAsString());
305   }
306 }