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