View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import java.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.TreeMap;
34  import java.util.TreeSet;
35  import java.util.regex.Matcher;
36  
37  import org.apache.hadoop.hbase.util.ByteStringer;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.hadoop.hbase.classification.InterfaceAudience;
41  import org.apache.hadoop.hbase.classification.InterfaceStability;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.fs.Path;
44  import org.apache.hadoop.hbase.client.Durability;
45  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
46  import org.apache.hadoop.hbase.exceptions.DeserializationException;
47  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
48  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
49  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.BytesBytesPair;
50  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ColumnFamilySchema;
51  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
52  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableSchema;
53  import org.apache.hadoop.hbase.regionserver.BloomType;
54  import org.apache.hadoop.hbase.security.User;
55  import org.apache.hadoop.hbase.util.Bytes;
56  import org.apache.hadoop.hbase.util.Writables;
57  import org.apache.hadoop.io.WritableComparable;
58  
59  /**
60   * HTableDescriptor contains the details about an HBase table  such as the descriptors of
61   * all the column families, is the table a catalog table, <code> -ROOT- </code> or
62   * <code> hbase:meta </code>, if the table is read only, the maximum size of the memstore,
63   * when the region split should occur, coprocessors associated with it etc...
64   */
65  @InterfaceAudience.Public
66  @InterfaceStability.Evolving
67  public class HTableDescriptor implements WritableComparable<HTableDescriptor> {
68  
69    private static final Log LOG = LogFactory.getLog(HTableDescriptor.class);
70  
71    /**
72     *  Changes prior to version 3 were not recorded here.
73     *  Version 3 adds metadata as a map where keys and values are byte[].
74     *  Version 4 adds indexes
75     *  Version 5 removed transactional pollution -- e.g. indexes
76     *  Version 6 changed metadata to BytesBytesPair in PB
77     *  Version 7 adds table-level configuration
78     */
79    private static final byte TABLE_DESCRIPTOR_VERSION = 7;
80  
81    private TableName name = null;
82  
83    /**
84     * A map which holds the metadata information of the table. This metadata
85     * includes values like IS_ROOT, IS_META, DEFERRED_LOG_FLUSH, SPLIT_POLICY,
86     * MAX_FILE_SIZE, READONLY, MEMSTORE_FLUSHSIZE etc...
87     */
88    private final Map<ImmutableBytesWritable, ImmutableBytesWritable> values =
89      new HashMap<ImmutableBytesWritable, ImmutableBytesWritable>();
90  
91    /**
92     * A map which holds the configuration specific to the table.
93     * The keys of the map have the same names as config keys and override the defaults with
94     * table-specific settings. Example usage may be for compactions, etc.
95     */
96    private final Map<String, String> configuration = new HashMap<String, String>();
97  
98    public static final String SPLIT_POLICY = "SPLIT_POLICY";
99  
100   /**
101    * <em>INTERNAL</em> Used by HBase Shell interface to access this metadata
102    * attribute which denotes the maximum size of the store file after which
103    * a region split occurs
104    *
105    * @see #getMaxFileSize()
106    */
107   public static final String MAX_FILESIZE = "MAX_FILESIZE";
108   private static final ImmutableBytesWritable MAX_FILESIZE_KEY =
109     new ImmutableBytesWritable(Bytes.toBytes(MAX_FILESIZE));
110 
111   public static final String OWNER = "OWNER";
112   public static final ImmutableBytesWritable OWNER_KEY =
113     new ImmutableBytesWritable(Bytes.toBytes(OWNER));
114 
115   /**
116    * <em>INTERNAL</em> Used by rest interface to access this metadata
117    * attribute which denotes if the table is Read Only
118    *
119    * @see #isReadOnly()
120    */
121   public static final String READONLY = "READONLY";
122   private static final ImmutableBytesWritable READONLY_KEY =
123     new ImmutableBytesWritable(Bytes.toBytes(READONLY));
124 
125   /**
126    * <em>INTERNAL</em> Used by HBase Shell interface to access this metadata
127    * attribute which denotes if the table is compaction enabled
128    *
129    * @see #isCompactionEnabled()
130    */
131   public static final String COMPACTION_ENABLED = "COMPACTION_ENABLED";
132   private static final ImmutableBytesWritable COMPACTION_ENABLED_KEY =
133     new ImmutableBytesWritable(Bytes.toBytes(COMPACTION_ENABLED));
134 
135   /**
136    * <em>INTERNAL</em> Used by HBase Shell interface to access this metadata
137    * attribute which represents the maximum size of the memstore after which
138    * its contents are flushed onto the disk
139    *
140    * @see #getMemStoreFlushSize()
141    */
142   public static final String MEMSTORE_FLUSHSIZE = "MEMSTORE_FLUSHSIZE";
143   private static final ImmutableBytesWritable MEMSTORE_FLUSHSIZE_KEY =
144     new ImmutableBytesWritable(Bytes.toBytes(MEMSTORE_FLUSHSIZE));
145 
146   public static final String FLUSH_POLICY = "FLUSH_POLICY";
147 
148   /**
149    * <em>INTERNAL</em> Used by rest interface to access this metadata
150    * attribute which denotes if the table is a -ROOT- region or not
151    *
152    * @see #isRootRegion()
153    */
154   public static final String IS_ROOT = "IS_ROOT";
155   private static final ImmutableBytesWritable IS_ROOT_KEY =
156     new ImmutableBytesWritable(Bytes.toBytes(IS_ROOT));
157 
158   /**
159    * <em>INTERNAL</em> Used by rest interface to access this metadata
160    * attribute which denotes if it is a catalog table, either
161    * <code> hbase:meta </code> or <code> -ROOT- </code>
162    *
163    * @see #isMetaRegion()
164    */
165   public static final String IS_META = "IS_META";
166   private static final ImmutableBytesWritable IS_META_KEY =
167     new ImmutableBytesWritable(Bytes.toBytes(IS_META));
168 
169   /**
170    * <em>INTERNAL</em> Used by HBase Shell interface to access this metadata
171    * attribute which denotes if the deferred log flush option is enabled.
172    * @deprecated Use {@link #DURABILITY} instead.
173    */
174   @Deprecated
175   public static final String DEFERRED_LOG_FLUSH = "DEFERRED_LOG_FLUSH";
176   @Deprecated
177   private static final ImmutableBytesWritable DEFERRED_LOG_FLUSH_KEY =
178     new ImmutableBytesWritable(Bytes.toBytes(DEFERRED_LOG_FLUSH));
179 
180   /**
181    * <em>INTERNAL</em> {@link Durability} setting for the table.
182    */
183   public static final String DURABILITY = "DURABILITY";
184   private static final ImmutableBytesWritable DURABILITY_KEY =
185       new ImmutableBytesWritable(Bytes.toBytes("DURABILITY"));
186 
187   /**
188    * <em>INTERNAL</em> number of region replicas for the table.
189    */
190   public static final String REGION_REPLICATION = "REGION_REPLICATION";
191   private static final ImmutableBytesWritable REGION_REPLICATION_KEY =
192       new ImmutableBytesWritable(Bytes.toBytes(REGION_REPLICATION));
193 
194   /**
195    * <em>INTERNAL</em> flag to indicate whether or not the memstore should be replicated
196    * for read-replicas (CONSISTENCY =&gt; TIMELINE).
197    */
198   public static final String REGION_MEMSTORE_REPLICATION = "REGION_MEMSTORE_REPLICATION";
199   private static final ImmutableBytesWritable REGION_MEMSTORE_REPLICATION_KEY =
200       new ImmutableBytesWritable(Bytes.toBytes(REGION_MEMSTORE_REPLICATION));
201 
202   /**
203    * <em>INTERNAL</em> Used by shell/rest interface to access this metadata
204    * attribute which denotes if the table should be treated by region normalizer.
205    *
206    * @see #isNormalizationEnabled()
207    */
208   public static final String NORMALIZATION_ENABLED = "NORMALIZATION_ENABLED";
209   private static final ImmutableBytesWritable NORMALIZATION_ENABLED_KEY =
210     new ImmutableBytesWritable(Bytes.toBytes(NORMALIZATION_ENABLED));
211 
212   /** Default durability for HTD is USE_DEFAULT, which defaults to HBase-global default value */
213   private static final Durability DEFAULT_DURABLITY = Durability.USE_DEFAULT;
214 
215   /*
216    *  The below are ugly but better than creating them each time till we
217    *  replace booleans being saved as Strings with plain booleans.  Need a
218    *  migration script to do this.  TODO.
219    */
220   private static final ImmutableBytesWritable FALSE =
221     new ImmutableBytesWritable(Bytes.toBytes(Boolean.FALSE.toString()));
222 
223   private static final ImmutableBytesWritable TRUE =
224     new ImmutableBytesWritable(Bytes.toBytes(Boolean.TRUE.toString()));
225 
226   private static final boolean DEFAULT_DEFERRED_LOG_FLUSH = false;
227 
228   /**
229    * Constant that denotes whether the table is READONLY by default and is false
230    */
231   public static final boolean DEFAULT_READONLY = false;
232 
233   /**
234    * Constant that denotes whether the table is compaction enabled by default
235    */
236   public static final boolean DEFAULT_COMPACTION_ENABLED = true;
237 
238   /**
239    * Constant that denotes whether the table is normalized by default.
240    */
241   public static final boolean DEFAULT_NORMALIZATION_ENABLED = false;
242 
243   /**
244    * Constant that denotes the maximum default size of the memstore after which
245    * the contents are flushed to the store files
246    */
247   public static final long DEFAULT_MEMSTORE_FLUSH_SIZE = 1024*1024*128L;
248 
249   public static final int DEFAULT_REGION_REPLICATION = 1;
250 
251   public static final boolean DEFAULT_REGION_MEMSTORE_REPLICATION = true;
252 
253   private final static Map<String, String> DEFAULT_VALUES
254     = new HashMap<String, String>();
255   private final static Set<ImmutableBytesWritable> RESERVED_KEYWORDS
256     = new HashSet<ImmutableBytesWritable>();
257   static {
258     DEFAULT_VALUES.put(MAX_FILESIZE,
259         String.valueOf(HConstants.DEFAULT_MAX_FILE_SIZE));
260     DEFAULT_VALUES.put(READONLY, String.valueOf(DEFAULT_READONLY));
261     DEFAULT_VALUES.put(MEMSTORE_FLUSHSIZE,
262         String.valueOf(DEFAULT_MEMSTORE_FLUSH_SIZE));
263     DEFAULT_VALUES.put(DEFERRED_LOG_FLUSH,
264         String.valueOf(DEFAULT_DEFERRED_LOG_FLUSH));
265     DEFAULT_VALUES.put(DURABILITY, DEFAULT_DURABLITY.name()); //use the enum name
266     DEFAULT_VALUES.put(REGION_REPLICATION, String.valueOf(DEFAULT_REGION_REPLICATION));
267     DEFAULT_VALUES.put(NORMALIZATION_ENABLED, String.valueOf(DEFAULT_NORMALIZATION_ENABLED));
268     for (String s : DEFAULT_VALUES.keySet()) {
269       RESERVED_KEYWORDS.add(new ImmutableBytesWritable(Bytes.toBytes(s)));
270     }
271     RESERVED_KEYWORDS.add(IS_ROOT_KEY);
272     RESERVED_KEYWORDS.add(IS_META_KEY);
273   }
274 
275   /**
276    * Cache of whether this is a meta table or not.
277    */
278   private volatile Boolean meta = null;
279   /**
280    * Cache of whether this is root table or not.
281    */
282   private volatile Boolean root = null;
283 
284   /**
285    * Durability setting for the table
286    */
287   private Durability durability = null;
288 
289   /**
290    * Maps column family name to the respective HColumnDescriptors
291    */
292   private final Map<byte [], HColumnDescriptor> families =
293     new TreeMap<byte [], HColumnDescriptor>(Bytes.BYTES_RAWCOMPARATOR);
294 
295   /**
296    * <em> INTERNAL </em> Private constructor used internally creating table descriptors for
297    * catalog tables, <code>hbase:meta</code> and <code>-ROOT-</code>.
298    */
299   @InterfaceAudience.Private
300   protected HTableDescriptor(final TableName name, HColumnDescriptor[] families) {
301     setName(name);
302     for(HColumnDescriptor descriptor : families) {
303       this.families.put(descriptor.getName(), descriptor);
304     }
305   }
306 
307   /**
308    * <em> INTERNAL </em>Private constructor used internally creating table descriptors for
309    * catalog tables, <code>hbase:meta</code> and <code>-ROOT-</code>.
310    */
311   protected HTableDescriptor(final TableName name, HColumnDescriptor[] families,
312       Map<ImmutableBytesWritable,ImmutableBytesWritable> values) {
313     setName(name);
314     for(HColumnDescriptor descriptor : families) {
315       this.families.put(descriptor.getName(), descriptor);
316     }
317     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> entry:
318         values.entrySet()) {
319       setValue(entry.getKey(), entry.getValue());
320     }
321   }
322 
323   /**
324    * Default constructor which constructs an empty object.
325    * For deserializing an HTableDescriptor instance only.
326    * @deprecated As of release 0.96
327    *             (<a href="https://issues.apache.org/jira/browse/HBASE-5453">HBASE-5453</a>).
328    *             This will be removed in HBase 2.0.0.
329    *             Used by Writables and Writables are going away.
330    */
331   @Deprecated
332   public HTableDescriptor() {
333     super();
334   }
335 
336   /**
337    * Construct a table descriptor specifying a TableName object
338    * @param name Table name.
339    * @see <a href="HADOOP-1581">HADOOP-1581 HBASE: Un-openable tablename bug</a>
340    */
341   public HTableDescriptor(final TableName name) {
342     super();
343     setName(name);
344   }
345 
346   /**
347    * Construct a table descriptor specifying a byte array table name
348    * @param name Table name.
349    * @see <a href="HADOOP-1581">HADOOP-1581 HBASE: Un-openable tablename bug</a>
350    */
351   @Deprecated
352   public HTableDescriptor(final byte[] name) {
353     this(TableName.valueOf(name));
354   }
355 
356   /**
357    * Construct a table descriptor specifying a String table name
358    * @param name Table name.
359    * @see <a href="HADOOP-1581">HADOOP-1581 HBASE: Un-openable tablename bug</a>
360    */
361   @Deprecated
362   public HTableDescriptor(final String name) {
363     this(TableName.valueOf(name));
364   }
365 
366   /**
367    * Construct a table descriptor by cloning the descriptor passed as a parameter.
368    * <p>
369    * Makes a deep copy of the supplied descriptor.
370    * Can make a modifiable descriptor from an UnmodifyableHTableDescriptor.
371    * @param desc The descriptor.
372    */
373   public HTableDescriptor(final HTableDescriptor desc) {
374     this(desc.name, desc);
375   }
376 
377   /**
378    * Construct a table descriptor by cloning the descriptor passed as a parameter
379    * but using a different table name.
380    * <p>
381    * Makes a deep copy of the supplied descriptor.
382    * Can make a modifiable descriptor from an UnmodifyableHTableDescriptor.
383    * @param name Table name.
384    * @param desc The descriptor.
385    */
386   public HTableDescriptor(final TableName name, final HTableDescriptor desc) {
387     super();
388     setName(name);
389     setMetaFlags(this.name);
390     for (HColumnDescriptor c: desc.families.values()) {
391       this.families.put(c.getName(), new HColumnDescriptor(c));
392     }
393     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
394         desc.values.entrySet()) {
395       setValue(e.getKey(), e.getValue());
396     }
397     for (Map.Entry<String, String> e : desc.configuration.entrySet()) {
398       this.configuration.put(e.getKey(), e.getValue());
399     }
400   }
401 
402   /*
403    * Set meta flags on this table.
404    * IS_ROOT_KEY is set if its a -ROOT- table
405    * IS_META_KEY is set either if its a -ROOT- or a hbase:meta table
406    * Called by constructors.
407    * @param name
408    */
409   private void setMetaFlags(final TableName name) {
410     setMetaRegion(isRootRegion() ||
411       name.equals(TableName.META_TABLE_NAME));
412   }
413 
414   /**
415    * Check if the descriptor represents a <code> -ROOT- </code> region.
416    *
417    * @return true if this is a <code> -ROOT- </code> region
418    */
419   public boolean isRootRegion() {
420     if (this.root == null) {
421       this.root = isSomething(IS_ROOT_KEY, false)? Boolean.TRUE: Boolean.FALSE;
422     }
423     return this.root.booleanValue();
424   }
425 
426   /**
427    * <em> INTERNAL </em> Used to denote if the current table represents
428    * <code> -ROOT- </code> region. This is used internally by the
429    * HTableDescriptor constructors
430    *
431    * @param isRoot true if this is the <code> -ROOT- </code> region
432    */
433   protected void setRootRegion(boolean isRoot) {
434     // TODO: Make the value a boolean rather than String of boolean.
435     setValue(IS_ROOT_KEY, isRoot ? TRUE : FALSE);
436   }
437 
438   /**
439    * Checks if this table is <code> hbase:meta </code>
440    * region.
441    *
442    * @return true if this table is <code> hbase:meta </code>
443    * region
444    */
445   public boolean isMetaRegion() {
446     if (this.meta == null) {
447       this.meta = calculateIsMetaRegion();
448     }
449     return this.meta.booleanValue();
450   }
451 
452   private synchronized Boolean calculateIsMetaRegion() {
453     byte [] value = getValue(IS_META_KEY);
454     return (value != null)? Boolean.valueOf(Bytes.toString(value)): Boolean.FALSE;
455   }
456 
457   private boolean isSomething(final ImmutableBytesWritable key,
458       final boolean valueIfNull) {
459     byte [] value = getValue(key);
460     if (value != null) {
461       return Boolean.valueOf(Bytes.toString(value));
462     }
463     return valueIfNull;
464   }
465 
466   /**
467    * <em> INTERNAL </em> Used to denote if the current table represents
468    * <code> -ROOT- </code> or <code> hbase:meta </code> region. This is used
469    * internally by the HTableDescriptor constructors
470    *
471    * @param isMeta true if its either <code> -ROOT- </code> or
472    * <code> hbase:meta </code> region
473    */
474   protected void setMetaRegion(boolean isMeta) {
475     setValue(IS_META_KEY, isMeta? TRUE: FALSE);
476   }
477 
478   /**
479    * Checks if the table is a <code>hbase:meta</code> table
480    *
481    * @return true if table is <code> hbase:meta </code> region.
482    */
483   public boolean isMetaTable() {
484     return isMetaRegion() && !isRootRegion();
485   }
486 
487   /**
488    * Getter for accessing the metadata associated with the key
489    *
490    * @param key The key.
491    * @return The value.
492    * @see #values
493    */
494   public byte[] getValue(byte[] key) {
495     return getValue(new ImmutableBytesWritable(key));
496   }
497 
498   private byte[] getValue(final ImmutableBytesWritable key) {
499     ImmutableBytesWritable ibw = values.get(key);
500     if (ibw == null)
501       return null;
502     return ibw.get();
503   }
504 
505   /**
506    * Getter for accessing the metadata associated with the key
507    *
508    * @param key The key.
509    * @return The value.
510    * @see #values
511    */
512   public String getValue(String key) {
513     byte[] value = getValue(Bytes.toBytes(key));
514     if (value == null)
515       return null;
516     return Bytes.toString(value);
517   }
518 
519   /**
520    * Getter for fetching an unmodifiable {@link #values} map.
521    *
522    * @return unmodifiable map {@link #values}.
523    * @see #values
524    */
525   public Map<ImmutableBytesWritable,ImmutableBytesWritable> getValues() {
526     // shallow pointer copy
527     return Collections.unmodifiableMap(values);
528   }
529 
530   /**
531    * Setter for storing metadata as a (key, value) pair in {@link #values} map
532    *
533    * @param key The key.
534    * @param value The value.
535    * @see #values
536    */
537   public void setValue(byte[] key, byte[] value) {
538     setValue(new ImmutableBytesWritable(key), new ImmutableBytesWritable(value));
539   }
540 
541   /*
542    * @param key The key.
543    * @param value The value.
544    */
545   private void setValue(final ImmutableBytesWritable key,
546       final String value) {
547     setValue(key, new ImmutableBytesWritable(Bytes.toBytes(value)));
548   }
549 
550   /*
551    * Setter for storing metadata as a (key, value) pair in {@link #values} map
552    *
553    * @param key The key.
554    * @param value The value.
555    */
556   public void setValue(final ImmutableBytesWritable key,
557       final ImmutableBytesWritable value) {
558     if (key.compareTo(DEFERRED_LOG_FLUSH_KEY) == 0) {
559       boolean isDeferredFlush = Boolean.valueOf(Bytes.toString(value.get()));
560       LOG.warn("HTableDescriptor property:" + DEFERRED_LOG_FLUSH + " is deprecated, " +
561           "use " + DURABILITY + " instead");
562       setDurability(isDeferredFlush ? Durability.ASYNC_WAL : DEFAULT_DURABLITY);
563     } else {
564       values.put(key, value);
565     }
566   }
567 
568   /**
569    * Setter for storing metadata as a (key, value) pair in {@link #values} map
570    *
571    * @param key The key.
572    * @param value The value.
573    * @see #values
574    */
575   public void setValue(String key, String value) {
576     if (value == null) {
577       remove(key);
578     } else {
579       setValue(Bytes.toBytes(key), Bytes.toBytes(value));
580     }
581   }
582 
583   /**
584    * Remove metadata represented by the key from the {@link #values} map
585    *
586    * @param key Key whose key and value we're to remove from HTableDescriptor
587    * parameters.
588    */
589   public void remove(final String key) {
590     remove(new ImmutableBytesWritable(Bytes.toBytes(key)));
591   }
592 
593   /**
594    * Remove metadata represented by the key from the {@link #values} map
595    *
596    * @param key Key whose key and value we're to remove from HTableDescriptor
597    * parameters.
598    */
599   public void remove(ImmutableBytesWritable key) {
600     values.remove(key);
601   }
602 
603   /**
604    * Remove metadata represented by the key from the {@link #values} map
605    *
606    * @param key Key whose key and value we're to remove from HTableDescriptor
607    * parameters.
608    */
609   public void remove(final byte [] key) {
610     remove(new ImmutableBytesWritable(key));
611   }
612 
613   /**
614    * Check if the readOnly flag of the table is set. If the readOnly flag is
615    * set then the contents of the table can only be read from but not modified.
616    *
617    * @return true if all columns in the table should be read only
618    */
619   public boolean isReadOnly() {
620     return isSomething(READONLY_KEY, DEFAULT_READONLY);
621   }
622 
623   /**
624    * Setting the table as read only sets all the columns in the table as read
625    * only. By default all tables are modifiable, but if the readOnly flag is
626    * set to true then the contents of the table can only be read but not modified.
627    *
628    * @param readOnly True if all of the columns in the table should be read
629    * only.
630    */
631   public void setReadOnly(final boolean readOnly) {
632     setValue(READONLY_KEY, readOnly? TRUE: FALSE);
633   }
634 
635   /**
636    * Check if the compaction enable flag of the table is true. If flag is
637    * false then no minor/major compactions will be done in real.
638    *
639    * @return true if table compaction enabled
640    */
641   public boolean isCompactionEnabled() {
642     return isSomething(COMPACTION_ENABLED_KEY, DEFAULT_COMPACTION_ENABLED);
643   }
644 
645   /**
646    * Setting the table compaction enable flag.
647    *
648    * @param isEnable True if enable compaction.
649    */
650   public void setCompactionEnabled(final boolean isEnable) {
651     setValue(COMPACTION_ENABLED_KEY, isEnable ? TRUE : FALSE);
652   }
653 
654   /**
655    * Check if normalization enable flag of the table is true. If flag is
656    * false then no region normalizer won't attempt to normalize this table.
657    *
658    * @return true if region normalization is enabled for this table
659    */
660   public boolean isNormalizationEnabled() {
661     return isSomething(NORMALIZATION_ENABLED_KEY, DEFAULT_NORMALIZATION_ENABLED);
662   }
663 
664   /**
665    * Setting the table normalization enable flag.
666    *
667    * @param isEnable True if enable normalization.
668    */
669   public HTableDescriptor setNormalizationEnabled(final boolean isEnable) {
670     setValue(NORMALIZATION_ENABLED_KEY, isEnable ? TRUE : FALSE);
671     return this;
672   }
673 
674   /**
675    * This method is no longer being used in CDH5.4. Please see HBASE-10471 for more details.
676    * Retaining this so as not to break compatability with earlier CDH5.x versions.
677    * @deprecated
678    */
679   @Deprecated
680   public synchronized boolean isDeferredLogFlush() {
681     return getDurability() == Durability.ASYNC_WAL;
682   }
683 
684   /**
685    * This method is no longer being used in CDH5.4. Please see HBASE-10471 for more details.
686    * Retaining this so as not to break compatability with earlier CDH5.x versions.
687    * @deprecated
688    */
689   @Deprecated
690   public synchronized void setDeferredLogFlush(final boolean isAsyncLogFlush) {
691     this.setDurability(isAsyncLogFlush ? Durability.ASYNC_WAL : DEFAULT_DURABLITY);
692   }
693 
694   /**
695    * Sets the {@link Durability} setting for the table. This defaults to Durability.USE_DEFAULT.
696    * @param durability enum value
697    */
698   public void setDurability(Durability durability) {
699     this.durability = durability;
700     setValue(DURABILITY_KEY, durability.name());
701   }
702 
703   /**
704    * Returns the durability setting for the table.
705    * @return durability setting for the table.
706    */
707   public Durability getDurability() {
708     if (this.durability == null) {
709       byte[] durabilityValue = getValue(DURABILITY_KEY);
710       if (durabilityValue == null) {
711         this.durability = DEFAULT_DURABLITY;
712       } else {
713         try {
714           this.durability = Durability.valueOf(Bytes.toString(durabilityValue));
715         } catch (IllegalArgumentException ex) {
716           LOG.warn("Received " + ex + " because Durability value for HTableDescriptor"
717             + " is not known. Durability:" + Bytes.toString(durabilityValue));
718           this.durability = DEFAULT_DURABLITY;
719         }
720       }
721     }
722     return this.durability;
723   }
724 
725   /**
726    * Get the name of the table
727    *
728    * @return TableName
729    */
730   public TableName getTableName() {
731     return name;
732   }
733 
734   /**
735    * Get the name of the table as a byte array.
736    *
737    * @return name of table
738    * @deprecated Use {@link #getTableName()} instead
739    */
740   @Deprecated
741   public byte[] getName() {
742     return name.getName();
743   }
744 
745   /**
746    * Get the name of the table as a String
747    *
748    * @return name of table as a String
749    */
750   public String getNameAsString() {
751     return name.getNameAsString();
752   }
753 
754   /**
755    * This sets the class associated with the region split policy which
756    * determines when a region split should occur.  The class used by
757    * default is defined in {@link org.apache.hadoop.hbase.regionserver.RegionSplitPolicy}
758    * @param clazz the class name
759    */
760   public HTableDescriptor setRegionSplitPolicyClassName(String clazz) {
761     setValue(SPLIT_POLICY, clazz);
762     return this;
763   }
764 
765   /**
766    * This gets the class associated with the region split policy which
767    * determines when a region split should occur.  The class used by
768    * default is defined in {@link org.apache.hadoop.hbase.regionserver.RegionSplitPolicy}
769    *
770    * @return the class name of the region split policy for this table.
771    * If this returns null, the default split policy is used.
772    */
773    public String getRegionSplitPolicyClassName() {
774     return getValue(SPLIT_POLICY);
775   }
776 
777   /**
778    * Set the name of the table.
779    *
780    * @param name name of table
781    */
782   @Deprecated
783   public void setName(byte[] name) {
784     setName(TableName.valueOf(name));
785   }
786 
787   @Deprecated
788   public void setName(TableName name) {
789     this.name = name;
790     setMetaFlags(this.name);
791   }
792 
793   /**
794    * Returns the maximum size upto which a region can grow to after which a region
795    * split is triggered. The region size is represented by the size of the biggest
796    * store file in that region.
797    *
798    * @return max hregion size for table, -1 if not set.
799    *
800    * @see #setMaxFileSize(long)
801    */
802   public long getMaxFileSize() {
803     byte [] value = getValue(MAX_FILESIZE_KEY);
804     if (value != null) {
805       return Long.parseLong(Bytes.toString(value));
806     }
807     return -1;
808   }
809 
810   /**
811    * Sets the maximum size upto which a region can grow to after which a region
812    * split is triggered. The region size is represented by the size of the biggest
813    * store file in that region, i.e. If the biggest store file grows beyond the
814    * maxFileSize, then the region split is triggered. This defaults to a value of
815    * 256 MB.
816    * <p>
817    * This is not an absolute value and might vary. Assume that a single row exceeds
818    * the maxFileSize then the storeFileSize will be greater than maxFileSize since
819    * a single row cannot be split across multiple regions
820    * </p>
821    *
822    * @param maxFileSize The maximum file size that a store file can grow to
823    * before a split is triggered.
824    */
825   public void setMaxFileSize(long maxFileSize) {
826     setValue(MAX_FILESIZE_KEY, Long.toString(maxFileSize));
827   }
828 
829   /**
830    * Returns the size of the memstore after which a flush to filesystem is triggered.
831    *
832    * @return memory cache flush size for each hregion, -1 if not set.
833    *
834    * @see #setMemStoreFlushSize(long)
835    */
836   public long getMemStoreFlushSize() {
837     byte [] value = getValue(MEMSTORE_FLUSHSIZE_KEY);
838     if (value != null) {
839       return Long.parseLong(Bytes.toString(value));
840     }
841     return -1;
842   }
843 
844   /**
845    * Represents the maximum size of the memstore after which the contents of the
846    * memstore are flushed to the filesystem. This defaults to a size of 64 MB.
847    *
848    * @param memstoreFlushSize memory cache flush size for each hregion
849    */
850   public void setMemStoreFlushSize(long memstoreFlushSize) {
851     setValue(MEMSTORE_FLUSHSIZE_KEY, Long.toString(memstoreFlushSize));
852   }
853 
854   /**
855    * This sets the class associated with the flush policy which determines determines the stores
856    * need to be flushed when flushing a region. The class used by default is defined in
857    * {@link org.apache.hadoop.hbase.regionserver.FlushPolicy}
858    * @param clazz the class name
859    */
860   public HTableDescriptor setFlushPolicyClassName(String clazz) {
861     setValue(FLUSH_POLICY, clazz);
862     return this;
863   }
864 
865   /**
866    * This gets the class associated with the flush policy which determines the stores need to be
867    * flushed when flushing a region. The class used by default is defined in
868    * {@link org.apache.hadoop.hbase.regionserver.FlushPolicy}
869    * @return the class name of the flush policy for this table. If this returns null, the default
870    *         flush policy is used.
871    */
872   public String getFlushPolicyClassName() {
873     return getValue(FLUSH_POLICY);
874   }
875 
876   /**
877    * Adds a column family.
878    * For the updating purpose please use {@link #modifyFamily(HColumnDescriptor)} instead.
879    * @param family HColumnDescriptor of family to add.
880    */
881   public void addFamily(final HColumnDescriptor family) {
882     if (family.getName() == null || family.getName().length <= 0) {
883       throw new IllegalArgumentException("Family name cannot be null or empty");
884     }
885     if (hasFamily(family.getName())) {
886       throw new IllegalArgumentException("Family '" +
887         family.getNameAsString() + "' already exists so cannot be added");
888     }
889     this.families.put(family.getName(), family);
890   }
891 
892   /**
893    * Modifies the existing column family.
894    * @param family HColumnDescriptor of family to update
895    * @return this (for chained invocation)
896    */
897   public HTableDescriptor modifyFamily(final HColumnDescriptor family) {
898     if (family.getName() == null || family.getName().length <= 0) {
899       throw new IllegalArgumentException("Family name cannot be null or empty");
900     }
901     if (!hasFamily(family.getName())) {
902       throw new IllegalArgumentException("Column family '" + family.getNameAsString()
903         + "' does not exist");
904     }
905     this.families.put(family.getName(), family);
906     return this;
907   }
908 
909   /**
910    * Checks to see if this table contains the given column family
911    * @param familyName Family name or column name.
912    * @return true if the table contains the specified family name
913    */
914   public boolean hasFamily(final byte [] familyName) {
915     return families.containsKey(familyName);
916   }
917 
918   /**
919    * @return Name of this table and then a map of all of the column family
920    * descriptors.
921    * @see #getNameAsString()
922    */
923   @Override
924   public String toString() {
925     StringBuilder s = new StringBuilder();
926     s.append('\'').append(Bytes.toString(name.getName())).append('\'');
927     s.append(getValues(true));
928     for (HColumnDescriptor f : families.values()) {
929       s.append(", ").append(f);
930     }
931     return s.toString();
932   }
933 
934   /**
935    * @return Name of this table and then a map of all of the column family
936    * descriptors (with only the non-default column family attributes)
937    */
938   public String toStringCustomizedValues() {
939     StringBuilder s = new StringBuilder();
940     s.append('\'').append(Bytes.toString(name.getName())).append('\'');
941     s.append(getValues(false));
942     for(HColumnDescriptor hcd : families.values()) {
943       s.append(", ").append(hcd.toStringCustomizedValues());
944     }
945     return s.toString();
946   }
947 
948   /**
949    * @return map of all table attributes formatted into string.
950    */
951   public String toStringTableAttributes() {
952    return getValues(true).toString();
953   }
954 
955   private StringBuilder getValues(boolean printDefaults) {
956     StringBuilder s = new StringBuilder();
957 
958     // step 1: set partitioning and pruning
959     Set<ImmutableBytesWritable> reservedKeys = new TreeSet<ImmutableBytesWritable>();
960     Set<ImmutableBytesWritable> userKeys = new TreeSet<ImmutableBytesWritable>();
961     for (ImmutableBytesWritable k : values.keySet()) {
962       if (k == null || k.get() == null) continue;
963       String key = Bytes.toString(k.get());
964       // in this section, print out reserved keywords + coprocessor info
965       if (!RESERVED_KEYWORDS.contains(k) && !key.startsWith("coprocessor$")) {
966         userKeys.add(k);
967         continue;
968       }
969       // only print out IS_ROOT/IS_META if true
970       String value = Bytes.toString(values.get(k).get());
971       if (key.equalsIgnoreCase(IS_ROOT) || key.equalsIgnoreCase(IS_META)) {
972         if (Boolean.valueOf(value) == false) continue;
973       }
974       // see if a reserved key is a default value. may not want to print it out
975       if (printDefaults
976           || !DEFAULT_VALUES.containsKey(key)
977           || !DEFAULT_VALUES.get(key).equalsIgnoreCase(value)) {
978         reservedKeys.add(k);
979       }
980     }
981 
982     // early exit optimization
983     boolean hasAttributes = !reservedKeys.isEmpty() || !userKeys.isEmpty();
984     if (!hasAttributes && configuration.isEmpty()) return s;
985 
986     s.append(", {");
987     // step 2: printing attributes
988     if (hasAttributes) {
989       s.append("TABLE_ATTRIBUTES => {");
990 
991       // print all reserved keys first
992       boolean printCommaForAttr = false;
993       for (ImmutableBytesWritable k : reservedKeys) {
994         String key = Bytes.toString(k.get());
995         String value = Bytes.toStringBinary(values.get(k).get());
996         if (printCommaForAttr) s.append(", ");
997         printCommaForAttr = true;
998         s.append(key);
999         s.append(" => ");
1000         s.append('\'').append(value).append('\'');
1001       }
1002 
1003       if (!userKeys.isEmpty()) {
1004         // print all non-reserved, advanced config keys as a separate subset
1005         if (printCommaForAttr) s.append(", ");
1006         printCommaForAttr = true;
1007         s.append(HConstants.METADATA).append(" => ");
1008         s.append("{");
1009         boolean printCommaForCfg = false;
1010         for (ImmutableBytesWritable k : userKeys) {
1011           String key = Bytes.toString(k.get());
1012           String value = Bytes.toStringBinary(values.get(k).get());
1013           if (printCommaForCfg) s.append(", ");
1014           printCommaForCfg = true;
1015           s.append('\'').append(key).append('\'');
1016           s.append(" => ");
1017           s.append('\'').append(value).append('\'');
1018         }
1019         s.append("}");
1020       }
1021     }
1022 
1023     // step 3: printing all configuration:
1024     if (!configuration.isEmpty()) {
1025       if (hasAttributes) {
1026         s.append(", ");
1027       }
1028       s.append(HConstants.CONFIGURATION).append(" => ");
1029       s.append('{');
1030       boolean printCommaForConfig = false;
1031       for (Map.Entry<String, String> e : configuration.entrySet()) {
1032         if (printCommaForConfig) s.append(", ");
1033         printCommaForConfig = true;
1034         s.append('\'').append(e.getKey()).append('\'');
1035         s.append(" => ");
1036         s.append('\'').append(e.getValue()).append('\'');
1037       }
1038       s.append("}");
1039     }
1040     s.append("}"); // end METHOD
1041     return s;
1042   }
1043 
1044   /**
1045    * Compare the contents of the descriptor with another one passed as a parameter.
1046    * Checks if the obj passed is an instance of HTableDescriptor, if yes then the
1047    * contents of the descriptors are compared.
1048    *
1049    * @return true if the contents of the the two descriptors exactly match
1050    *
1051    * @see java.lang.Object#equals(java.lang.Object)
1052    */
1053   @Override
1054   public boolean equals(Object obj) {
1055     if (this == obj) {
1056       return true;
1057     }
1058     if (obj == null) {
1059       return false;
1060     }
1061     if (!(obj instanceof HTableDescriptor)) {
1062       return false;
1063     }
1064     return compareTo((HTableDescriptor)obj) == 0;
1065   }
1066 
1067   /**
1068    * @see java.lang.Object#hashCode()
1069    */
1070   @Override
1071   public int hashCode() {
1072     int result = this.name.hashCode();
1073     result ^= Byte.valueOf(TABLE_DESCRIPTOR_VERSION).hashCode();
1074     if (this.families != null && this.families.size() > 0) {
1075       for (HColumnDescriptor e: this.families.values()) {
1076         result ^= e.hashCode();
1077       }
1078     }
1079     result ^= values.hashCode();
1080     result ^= configuration.hashCode();
1081     return result;
1082   }
1083 
1084   /**
1085    * <em> INTERNAL </em> This method is a part of {@link WritableComparable} interface
1086    * and is used for de-serialization of the HTableDescriptor over RPC
1087    * @deprecated Writables are going away.  Use pb {@link #parseFrom(byte[])} instead.
1088    */
1089   @Deprecated
1090   @Override
1091   public void readFields(DataInput in) throws IOException {
1092     int version = in.readInt();
1093     if (version < 3)
1094       throw new IOException("versions < 3 are not supported (and never existed!?)");
1095     // version 3+
1096     name = TableName.valueOf(Bytes.readByteArray(in));
1097     setRootRegion(in.readBoolean());
1098     setMetaRegion(in.readBoolean());
1099     values.clear();
1100     configuration.clear();
1101     int numVals = in.readInt();
1102     for (int i = 0; i < numVals; i++) {
1103       ImmutableBytesWritable key = new ImmutableBytesWritable();
1104       ImmutableBytesWritable value = new ImmutableBytesWritable();
1105       key.readFields(in);
1106       value.readFields(in);
1107       setValue(key, value);
1108     }
1109     families.clear();
1110     int numFamilies = in.readInt();
1111     for (int i = 0; i < numFamilies; i++) {
1112       HColumnDescriptor c = new HColumnDescriptor();
1113       c.readFields(in);
1114       families.put(c.getName(), c);
1115     }
1116     if (version >= 7) {
1117       int numConfigs = in.readInt();
1118       for (int i = 0; i < numConfigs; i++) {
1119         ImmutableBytesWritable key = new ImmutableBytesWritable();
1120         ImmutableBytesWritable value = new ImmutableBytesWritable();
1121         key.readFields(in);
1122         value.readFields(in);
1123         configuration.put(
1124           Bytes.toString(key.get(), key.getOffset(), key.getLength()),
1125           Bytes.toString(value.get(), value.getOffset(), value.getLength()));
1126       }
1127     }
1128   }
1129 
1130   /**
1131    * <em> INTERNAL </em> This method is a part of {@link WritableComparable} interface
1132    * and is used for serialization of the HTableDescriptor over RPC
1133    * @deprecated Writables are going away.
1134    * Use {@link com.google.protobuf.MessageLite#toByteArray} instead.
1135    */
1136   @Deprecated
1137   @Override
1138   public void write(DataOutput out) throws IOException {
1139     out.writeInt(TABLE_DESCRIPTOR_VERSION);
1140     Bytes.writeByteArray(out, name.toBytes());
1141     out.writeBoolean(isRootRegion());
1142     out.writeBoolean(isMetaRegion());
1143     out.writeInt(values.size());
1144     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
1145         values.entrySet()) {
1146       e.getKey().write(out);
1147       e.getValue().write(out);
1148     }
1149     out.writeInt(families.size());
1150     for(Iterator<HColumnDescriptor> it = families.values().iterator();
1151         it.hasNext(); ) {
1152       HColumnDescriptor family = it.next();
1153       family.write(out);
1154     }
1155     out.writeInt(configuration.size());
1156     for (Map.Entry<String, String> e : configuration.entrySet()) {
1157       new ImmutableBytesWritable(Bytes.toBytes(e.getKey())).write(out);
1158       new ImmutableBytesWritable(Bytes.toBytes(e.getValue())).write(out);
1159     }
1160   }
1161 
1162   // Comparable
1163 
1164   /**
1165    * Compares the descriptor with another descriptor which is passed as a parameter.
1166    * This compares the content of the two descriptors and not the reference.
1167    *
1168    * @return 0 if the contents of the descriptors are exactly matching,
1169    *         1 if there is a mismatch in the contents
1170    */
1171   @Override
1172   public int compareTo(final HTableDescriptor other) {
1173     int result = this.name.compareTo(other.name);
1174     if (result == 0) {
1175       result = families.size() - other.families.size();
1176     }
1177     if (result == 0 && families.size() != other.families.size()) {
1178       result = Integer.valueOf(families.size()).compareTo(
1179           Integer.valueOf(other.families.size()));
1180     }
1181     if (result == 0) {
1182       for (Iterator<HColumnDescriptor> it = families.values().iterator(),
1183           it2 = other.families.values().iterator(); it.hasNext(); ) {
1184         result = it.next().compareTo(it2.next());
1185         if (result != 0) {
1186           break;
1187         }
1188       }
1189     }
1190     if (result == 0) {
1191       // punt on comparison for ordering, just calculate difference
1192       result = this.values.hashCode() - other.values.hashCode();
1193       if (result < 0)
1194         result = -1;
1195       else if (result > 0)
1196         result = 1;
1197     }
1198     if (result == 0) {
1199       result = this.configuration.hashCode() - other.configuration.hashCode();
1200       if (result < 0)
1201         result = -1;
1202       else if (result > 0)
1203         result = 1;
1204     }
1205     return result;
1206   }
1207 
1208   /**
1209    * Returns an unmodifiable collection of all the {@link HColumnDescriptor}
1210    * of all the column families of the table.
1211    *
1212    * @return Immutable collection of {@link HColumnDescriptor} of all the
1213    * column families.
1214    */
1215   public Collection<HColumnDescriptor> getFamilies() {
1216     return Collections.unmodifiableCollection(this.families.values());
1217   }
1218 
1219   /**
1220    * Returns the configured replicas per region
1221    */
1222   public int getRegionReplication() {
1223     byte[] val = getValue(REGION_REPLICATION_KEY);
1224     if (val == null || val.length == 0) {
1225       return DEFAULT_REGION_REPLICATION;
1226     }
1227     return Integer.parseInt(Bytes.toString(val));
1228   }
1229 
1230   /**
1231    * Sets the number of replicas per region.
1232    * @param regionReplication the replication factor per region
1233    */
1234   public HTableDescriptor setRegionReplication(int regionReplication) {
1235     setValue(REGION_REPLICATION_KEY,
1236         new ImmutableBytesWritable(Bytes.toBytes(Integer.toString(regionReplication))));
1237     return this;
1238   }
1239 
1240   /**
1241    * @return true if the read-replicas memstore replication is enabled.
1242    */
1243   public boolean hasRegionMemstoreReplication() {
1244     return isSomething(REGION_MEMSTORE_REPLICATION_KEY, DEFAULT_REGION_MEMSTORE_REPLICATION);
1245   }
1246 
1247   /**
1248    * Enable or Disable the memstore replication from the primary region to the replicas.
1249    * The replication will be used only for meta operations (e.g. flush, compaction, ...)
1250    *
1251    * @param memstoreReplication true if the new data written to the primary region
1252    *                                 should be replicated.
1253    *                            false if the secondaries can tollerate to have new
1254    *                                  data only when the primary flushes the memstore.
1255    */
1256   public HTableDescriptor setRegionMemstoreReplication(boolean memstoreReplication) {
1257     setValue(REGION_MEMSTORE_REPLICATION_KEY, memstoreReplication ? TRUE : FALSE);
1258     // If the memstore replication is setup, we do not have to wait for observing a flush event
1259     // from primary before starting to serve reads, because gaps from replication is not applicable
1260     setConfiguration(RegionReplicaUtil.REGION_REPLICA_WAIT_FOR_PRIMARY_FLUSH_CONF_KEY,
1261       Boolean.toString(memstoreReplication));
1262     return this;
1263   }
1264 
1265   /**
1266    * Returns all the column family names of the current table. The map of
1267    * HTableDescriptor contains mapping of family name to HColumnDescriptors.
1268    * This returns all the keys of the family map which represents the column
1269    * family names of the table.
1270    *
1271    * @return Immutable sorted set of the keys of the families.
1272    */
1273   public Set<byte[]> getFamiliesKeys() {
1274     return Collections.unmodifiableSet(this.families.keySet());
1275   }
1276 
1277   /**
1278    * Returns an array all the {@link HColumnDescriptor} of the column families
1279    * of the table.
1280    *
1281    * @return Array of all the HColumnDescriptors of the current table
1282    *
1283    * @see #getFamilies()
1284    */
1285   public HColumnDescriptor[] getColumnFamilies() {
1286     Collection<HColumnDescriptor> hColumnDescriptors = getFamilies();
1287     return hColumnDescriptors.toArray(new HColumnDescriptor[hColumnDescriptors.size()]);
1288   }
1289 
1290 
1291   /**
1292    * Returns the HColumnDescriptor for a specific column family with name as
1293    * specified by the parameter column.
1294    *
1295    * @param column Column family name
1296    * @return Column descriptor for the passed family name or the family on
1297    * passed in column.
1298    */
1299   public HColumnDescriptor getFamily(final byte [] column) {
1300     return this.families.get(column);
1301   }
1302 
1303 
1304   /**
1305    * Removes the HColumnDescriptor with name specified by the parameter column
1306    * from the table descriptor
1307    *
1308    * @param column Name of the column family to be removed.
1309    * @return Column descriptor for the passed family name or the family on
1310    * passed in column.
1311    */
1312   public HColumnDescriptor removeFamily(final byte [] column) {
1313     return this.families.remove(column);
1314   }
1315 
1316   /**
1317    * Add a table coprocessor to this table. The coprocessor
1318    * type must be {@link org.apache.hadoop.hbase.coprocessor.RegionObserver}
1319    * or Endpoint.
1320    * It won't check if the class can be loaded or not.
1321    * Whether a coprocessor is loadable or not will be determined when
1322    * a region is opened.
1323    * @param className Full class name.
1324    * @throws IOException
1325    */
1326   public void addCoprocessor(String className) throws IOException {
1327     addCoprocessor(className, null, Coprocessor.PRIORITY_USER, null);
1328   }
1329 
1330   /**
1331    * Add a table coprocessor to this table. The coprocessor
1332    * type must be {@link org.apache.hadoop.hbase.coprocessor.RegionObserver}
1333    * or Endpoint.
1334    * It won't check if the class can be loaded or not.
1335    * Whether a coprocessor is loadable or not will be determined when
1336    * a region is opened.
1337    * @param jarFilePath Path of the jar file. If it's null, the class will be
1338    * loaded from default classloader.
1339    * @param className Full class name.
1340    * @param priority Priority
1341    * @param kvs Arbitrary key-value parameter pairs passed into the coprocessor.
1342    * @throws IOException
1343    */
1344   public void addCoprocessor(String className, Path jarFilePath,
1345                              int priority, final Map<String, String> kvs)
1346   throws IOException {
1347     checkHasCoprocessor(className);
1348 
1349     // Validate parameter kvs and then add key/values to kvString.
1350     StringBuilder kvString = new StringBuilder();
1351     if (kvs != null) {
1352       for (Map.Entry<String, String> e: kvs.entrySet()) {
1353         if (!e.getKey().matches(HConstants.CP_HTD_ATTR_VALUE_PARAM_KEY_PATTERN)) {
1354           throw new IOException("Illegal parameter key = " + e.getKey());
1355         }
1356         if (!e.getValue().matches(HConstants.CP_HTD_ATTR_VALUE_PARAM_VALUE_PATTERN)) {
1357           throw new IOException("Illegal parameter (" + e.getKey() +
1358               ") value = " + e.getValue());
1359         }
1360         if (kvString.length() != 0) {
1361           kvString.append(',');
1362         }
1363         kvString.append(e.getKey());
1364         kvString.append('=');
1365         kvString.append(e.getValue());
1366       }
1367     }
1368 
1369     String value = ((jarFilePath == null)? "" : jarFilePath.toString()) +
1370         "|" + className + "|" + Integer.toString(priority) + "|" +
1371         kvString.toString();
1372     addCoprocessorToMap(value);
1373   }
1374 
1375   /**
1376    * Add a table coprocessor to this table. The coprocessor
1377    * type must be {@link org.apache.hadoop.hbase.coprocessor.RegionObserver}
1378    * or Endpoint.
1379    * It won't check if the class can be loaded or not.
1380    * Whether a coprocessor is loadable or not will be determined when
1381    * a region is opened.
1382    * @param specStr The Coprocessor specification all in in one String formatted so matches
1383    * {@link HConstants#CP_HTD_ATTR_VALUE_PATTERN}
1384    * @throws IOException
1385    */
1386   public HTableDescriptor addCoprocessorWithSpec(final String specStr) throws IOException {
1387     String className = getCoprocessorClassNameFromSpecStr(specStr);
1388     if (className == null) {
1389       throw new IllegalArgumentException("Format does not match " +
1390         HConstants.CP_HTD_ATTR_VALUE_PATTERN + ": " + specStr);
1391     }
1392     checkHasCoprocessor(className);
1393     return addCoprocessorToMap(specStr);
1394   }
1395 
1396   private void checkHasCoprocessor(final String className) throws IOException {
1397     if (hasCoprocessor(className)) {
1398       throw new IOException("Coprocessor " + className + " already exists.");
1399     }
1400   }
1401 
1402   /**
1403    * Add coprocessor to values Map
1404    * @param specStr The Coprocessor specification all in in one String formatted so matches
1405    * {@link HConstants#CP_HTD_ATTR_VALUE_PATTERN}
1406    * @return Returns <code>this</code>
1407    */
1408   private HTableDescriptor addCoprocessorToMap(final String specStr) {
1409     if (specStr == null) return this;
1410     // generate a coprocessor key
1411     int maxCoprocessorNumber = 0;
1412     Matcher keyMatcher;
1413     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
1414         this.values.entrySet()) {
1415       keyMatcher =
1416           HConstants.CP_HTD_ATTR_KEY_PATTERN.matcher(
1417               Bytes.toString(e.getKey().get()));
1418       if (!keyMatcher.matches()) {
1419         continue;
1420       }
1421       maxCoprocessorNumber = Math.max(Integer.parseInt(keyMatcher.group(1)), maxCoprocessorNumber);
1422     }
1423     maxCoprocessorNumber++;
1424     String key = "coprocessor$" + Integer.toString(maxCoprocessorNumber);
1425     this.values.put(new ImmutableBytesWritable(Bytes.toBytes(key)),
1426       new ImmutableBytesWritable(Bytes.toBytes(specStr)));
1427     return this;
1428   }
1429 
1430   /**
1431    * Check if the table has an attached co-processor represented by the name className
1432    *
1433    * @param classNameToMatch - Class name of the co-processor
1434    * @return true of the table has a co-processor className
1435    */
1436   public boolean hasCoprocessor(String classNameToMatch) {
1437     Matcher keyMatcher;
1438     Matcher valueMatcher;
1439     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
1440         this.values.entrySet()) {
1441       keyMatcher =
1442           HConstants.CP_HTD_ATTR_KEY_PATTERN.matcher(
1443               Bytes.toString(e.getKey().get()));
1444       if (!keyMatcher.matches()) {
1445         continue;
1446       }
1447       String className = getCoprocessorClassNameFromSpecStr(Bytes.toString(e.getValue().get()));
1448       if (className == null) continue;
1449       if (className.equals(classNameToMatch.trim())) {
1450         return true;
1451       }
1452     }
1453     return false;
1454   }
1455 
1456   /**
1457    * Return the list of attached co-processor represented by their name className
1458    *
1459    * @return The list of co-processors classNames
1460    */
1461   public List<String> getCoprocessors() {
1462     List<String> result = new ArrayList<String>();
1463     Matcher keyMatcher;
1464     Matcher valueMatcher;
1465     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e : this.values.entrySet()) {
1466       keyMatcher = HConstants.CP_HTD_ATTR_KEY_PATTERN.matcher(Bytes.toString(e.getKey().get()));
1467       if (!keyMatcher.matches()) {
1468         continue;
1469       }
1470       String className = getCoprocessorClassNameFromSpecStr(Bytes.toString(e.getValue().get()));
1471       if (className == null) continue;
1472       result.add(className); // classname is the 2nd field
1473     }
1474     return result;
1475   }
1476 
1477   /**
1478    * @param spec String formatted as per {@link HConstants#CP_HTD_ATTR_VALUE_PATTERN}
1479    * @return Class parsed from passed in <code>spec</code> or null if no match or classpath found
1480    */
1481   private static String getCoprocessorClassNameFromSpecStr(final String spec) {
1482     Matcher matcher = HConstants.CP_HTD_ATTR_VALUE_PATTERN.matcher(spec);
1483     // Classname is the 2nd field
1484     return matcher != null && matcher.matches()? matcher.group(2).trim(): null;
1485   }
1486 
1487   /**
1488    * Remove a coprocessor from those set on the table
1489    * @param className Class name of the co-processor
1490    */
1491   public void removeCoprocessor(String className) {
1492     ImmutableBytesWritable match = null;
1493     Matcher keyMatcher;
1494     Matcher valueMatcher;
1495     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e : this.values
1496         .entrySet()) {
1497       keyMatcher = HConstants.CP_HTD_ATTR_KEY_PATTERN.matcher(Bytes.toString(e
1498           .getKey().get()));
1499       if (!keyMatcher.matches()) {
1500         continue;
1501       }
1502       valueMatcher = HConstants.CP_HTD_ATTR_VALUE_PATTERN.matcher(Bytes
1503           .toString(e.getValue().get()));
1504       if (!valueMatcher.matches()) {
1505         continue;
1506       }
1507       // get className and compare
1508       String clazz = valueMatcher.group(2).trim(); // classname is the 2nd field
1509       // remove the CP if it is present
1510       if (clazz.equals(className.trim())) {
1511         match = e.getKey();
1512         break;
1513       }
1514     }
1515     // if we found a match, remove it
1516     if (match != null)
1517       remove(match);
1518   }
1519 
1520   /**
1521    * Returns the {@link Path} object representing the table directory under
1522    * path rootdir
1523    *
1524    * Deprecated use FSUtils.getTableDir() instead.
1525    *
1526    * @param rootdir qualified path of HBase root directory
1527    * @param tableName name of table
1528    * @return {@link Path} for table
1529    */
1530   @Deprecated
1531   public static Path getTableDir(Path rootdir, final byte [] tableName) {
1532     //This is bad I had to mirror code from FSUTils.getTableDir since
1533     //there is no module dependency between hbase-client and hbase-server
1534     TableName name = TableName.valueOf(tableName);
1535     return new Path(rootdir, new Path(HConstants.BASE_NAMESPACE_DIR,
1536               new Path(name.getNamespaceAsString(), new Path(name.getQualifierAsString()))));
1537   }
1538 
1539   /**
1540    * Table descriptor for <code>hbase:meta</code> catalog table
1541    * @deprecated Use TableDescriptors#get(TableName.META_TABLE_NAME) or
1542    * HBaseAdmin#getTableDescriptor(TableName.META_TABLE_NAME) instead.
1543    */
1544   @Deprecated
1545   public static final HTableDescriptor META_TABLEDESC = new HTableDescriptor(
1546       TableName.META_TABLE_NAME,
1547       new HColumnDescriptor[] {
1548           new HColumnDescriptor(HConstants.CATALOG_FAMILY)
1549               // Ten is arbitrary number.  Keep versions to help debugging.
1550               .setMaxVersions(HConstants.DEFAULT_HBASE_META_VERSIONS)
1551               .setInMemory(true)
1552               .setBlocksize(HConstants.DEFAULT_HBASE_META_BLOCK_SIZE)
1553               .setScope(HConstants.REPLICATION_SCOPE_LOCAL)
1554               // Disable blooms for meta.  Needs work.  Seems to mess w/ getClosestOrBefore.
1555               .setBloomFilterType(BloomType.NONE)
1556               // Enable cache of data blocks in L1 if more than one caching tier deployed:
1557               // e.g. if using CombinedBlockCache (BucketCache).
1558               .setCacheDataInL1(true)
1559       });
1560 
1561   static {
1562     try {
1563       META_TABLEDESC.addCoprocessor(
1564           "org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint",
1565           null, Coprocessor.PRIORITY_SYSTEM, null);
1566     } catch (IOException ex) {
1567       //LOG.warn("exception in loading coprocessor for the hbase:meta table");
1568       throw new RuntimeException(ex);
1569     }
1570   }
1571 
1572   public final static String NAMESPACE_FAMILY_INFO = "info";
1573   public final static byte[] NAMESPACE_FAMILY_INFO_BYTES = Bytes.toBytes(NAMESPACE_FAMILY_INFO);
1574   public final static byte[] NAMESPACE_COL_DESC_BYTES = Bytes.toBytes("d");
1575 
1576   /** Table descriptor for namespace table */
1577   public static final HTableDescriptor NAMESPACE_TABLEDESC = new HTableDescriptor(
1578       TableName.NAMESPACE_TABLE_NAME,
1579       new HColumnDescriptor[] {
1580           new HColumnDescriptor(NAMESPACE_FAMILY_INFO)
1581               // Ten is arbitrary number.  Keep versions to help debugging.
1582               .setMaxVersions(10)
1583               .setInMemory(true)
1584               .setBlocksize(8 * 1024)
1585               .setScope(HConstants.REPLICATION_SCOPE_LOCAL)
1586               // Enable cache of data blocks in L1 if more than one caching tier deployed:
1587               // e.g. if using CombinedBlockCache (BucketCache).
1588               .setCacheDataInL1(true)
1589       });
1590 
1591   @Deprecated
1592   public void setOwner(User owner) {
1593     setOwnerString(owner != null ? owner.getShortName() : null);
1594   }
1595 
1596   // used by admin.rb:alter(table_name,*args) to update owner.
1597   @Deprecated
1598   public void setOwnerString(String ownerString) {
1599     if (ownerString != null) {
1600       setValue(OWNER_KEY, ownerString);
1601     } else {
1602       remove(OWNER_KEY);
1603     }
1604   }
1605 
1606   @Deprecated
1607   public String getOwnerString() {
1608     if (getValue(OWNER_KEY) != null) {
1609       return Bytes.toString(getValue(OWNER_KEY));
1610     }
1611     // Note that every table should have an owner (i.e. should have OWNER_KEY set).
1612     // hbase:meta and -ROOT- should return system user as owner, not null (see
1613     // MasterFileSystem.java:bootstrap()).
1614     return null;
1615   }
1616 
1617   /**
1618    * @return This instance serialized with pb with pb magic prefix
1619    * @see #parseFrom(byte[])
1620    */
1621   public byte [] toByteArray() {
1622     return ProtobufUtil.prependPBMagic(convert().toByteArray());
1623   }
1624 
1625   /**
1626    * @param bytes A pb serialized {@link HTableDescriptor} instance with pb magic prefix
1627    * @return An instance of {@link HTableDescriptor} made from <code>bytes</code>
1628    * @throws DeserializationException
1629    * @throws IOException
1630    * @see #toByteArray()
1631    */
1632   public static HTableDescriptor parseFrom(final byte [] bytes)
1633   throws DeserializationException, IOException {
1634     if (!ProtobufUtil.isPBMagicPrefix(bytes)) {
1635       return (HTableDescriptor)Writables.getWritable(bytes, new HTableDescriptor());
1636     }
1637     int pblen = ProtobufUtil.lengthOfPBMagic();
1638     TableSchema.Builder builder = TableSchema.newBuilder();
1639     TableSchema ts;
1640     try {
1641       ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen);
1642       ts = builder.build();
1643     } catch (IOException e) {
1644       throw new DeserializationException(e);
1645     }
1646     return convert(ts);
1647   }
1648 
1649   /**
1650    * @return Convert the current {@link HTableDescriptor} into a pb TableSchema instance.
1651    */
1652   public TableSchema convert() {
1653     TableSchema.Builder builder = TableSchema.newBuilder();
1654     builder.setTableName(ProtobufUtil.toProtoTableName(getTableName()));
1655     for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e: this.values.entrySet()) {
1656       BytesBytesPair.Builder aBuilder = BytesBytesPair.newBuilder();
1657       aBuilder.setFirst(ByteStringer.wrap(e.getKey().get()));
1658       aBuilder.setSecond(ByteStringer.wrap(e.getValue().get()));
1659       builder.addAttributes(aBuilder.build());
1660     }
1661     for (HColumnDescriptor hcd: getColumnFamilies()) {
1662       builder.addColumnFamilies(hcd.convert());
1663     }
1664     for (Map.Entry<String, String> e : this.configuration.entrySet()) {
1665       NameStringPair.Builder aBuilder = NameStringPair.newBuilder();
1666       aBuilder.setName(e.getKey());
1667       aBuilder.setValue(e.getValue());
1668       builder.addConfiguration(aBuilder.build());
1669     }
1670     return builder.build();
1671   }
1672 
1673   /**
1674    * @param ts A pb TableSchema instance.
1675    * @return An {@link HTableDescriptor} made from the passed in pb <code>ts</code>.
1676    */
1677   public static HTableDescriptor convert(final TableSchema ts) {
1678     List<ColumnFamilySchema> list = ts.getColumnFamiliesList();
1679     HColumnDescriptor [] hcds = new HColumnDescriptor[list.size()];
1680     int index = 0;
1681     for (ColumnFamilySchema cfs: list) {
1682       hcds[index++] = HColumnDescriptor.convert(cfs);
1683     }
1684     HTableDescriptor htd = new HTableDescriptor(
1685         ProtobufUtil.toTableName(ts.getTableName()),
1686         hcds);
1687     for (BytesBytesPair a: ts.getAttributesList()) {
1688       htd.setValue(a.getFirst().toByteArray(), a.getSecond().toByteArray());
1689     }
1690     for (NameStringPair a: ts.getConfigurationList()) {
1691       htd.setConfiguration(a.getName(), a.getValue());
1692     }
1693     return htd;
1694   }
1695 
1696   /**
1697    * Getter for accessing the configuration value by key
1698    */
1699   public String getConfigurationValue(String key) {
1700     return configuration.get(key);
1701   }
1702 
1703   /**
1704    * Getter for fetching an unmodifiable {@link #configuration} map.
1705    */
1706   public Map<String, String> getConfiguration() {
1707     // shallow pointer copy
1708     return Collections.unmodifiableMap(configuration);
1709   }
1710 
1711   /**
1712    * Setter for storing a configuration setting in {@link #configuration} map.
1713    * @param key Config key. Same as XML config key e.g. hbase.something.or.other.
1714    * @param value String value. If null, removes the setting.
1715    */
1716   public void setConfiguration(String key, String value) {
1717     if (value == null) {
1718       removeConfiguration(key);
1719     } else {
1720       configuration.put(key, value);
1721     }
1722   }
1723 
1724   /**
1725    * Remove a config setting represented by the key from the {@link #configuration} map
1726    */
1727   public void removeConfiguration(final String key) {
1728     configuration.remove(key);
1729   }
1730 
1731   public static HTableDescriptor metaTableDescriptor(final Configuration conf)
1732       throws IOException {
1733     HTableDescriptor metaDescriptor = new HTableDescriptor(
1734       TableName.META_TABLE_NAME,
1735       new HColumnDescriptor[] {
1736         new HColumnDescriptor(HConstants.CATALOG_FAMILY)
1737           .setMaxVersions(conf.getInt(HConstants.HBASE_META_VERSIONS,
1738             HConstants.DEFAULT_HBASE_META_VERSIONS))
1739           .setInMemory(true)
1740           .setBlocksize(conf.getInt(HConstants.HBASE_META_BLOCK_SIZE,
1741             HConstants.DEFAULT_HBASE_META_BLOCK_SIZE))
1742           .setScope(HConstants.REPLICATION_SCOPE_LOCAL)
1743           // Disable blooms for meta.  Needs work.  Seems to mess w/ getClosestOrBefore.
1744           .setBloomFilterType(BloomType.NONE)
1745          });
1746     metaDescriptor.addCoprocessor(
1747       "org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint",
1748       null, Coprocessor.PRIORITY_SYSTEM, null);
1749     return metaDescriptor;
1750   }
1751 
1752 }