001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.rest.model;
020
021import com.fasterxml.jackson.annotation.JsonAnyGetter;
022import com.fasterxml.jackson.annotation.JsonAnySetter;
023import com.fasterxml.jackson.annotation.JsonIgnore;
024
025import java.io.IOException;
026import java.io.Serializable;
027import java.util.ArrayList;
028import java.util.Iterator;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032
033import javax.xml.bind.annotation.XmlAnyAttribute;
034import javax.xml.bind.annotation.XmlAttribute;
035import javax.xml.bind.annotation.XmlElement;
036import javax.xml.bind.annotation.XmlRootElement;
037import javax.xml.namespace.QName;
038
039import org.apache.hadoop.hbase.HColumnDescriptor;
040import org.apache.hadoop.hbase.HConstants;
041import org.apache.hadoop.hbase.HTableDescriptor;
042import org.apache.hadoop.hbase.TableName;
043import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
044import org.apache.hadoop.hbase.rest.ProtobufMessageHandler;
045import org.apache.hadoop.hbase.rest.protobuf.generated.ColumnSchemaMessage.ColumnSchema;
046import org.apache.hadoop.hbase.rest.protobuf.generated.TableSchemaMessage.TableSchema;
047import org.apache.hadoop.hbase.util.Bytes;
048import org.apache.yetus.audience.InterfaceAudience;
049
050/**
051 * A representation of HBase table descriptors.
052 *
053 * <pre>
054 * &lt;complexType name="TableSchema"&gt;
055 *   &lt;sequence&gt;
056 *     &lt;element name="column" type="tns:ColumnSchema"
057 *       maxOccurs="unbounded" minOccurs="1"&gt;&lt;/element&gt;
058 *   &lt;/sequence&gt;
059 *   &lt;attribute name="name" type="string"&gt;&lt;/attribute&gt;
060 *   &lt;anyAttribute&gt;&lt;/anyAttribute&gt;
061 * &lt;/complexType&gt;
062 * </pre>
063 */
064@XmlRootElement(name="TableSchema")
065@InterfaceAudience.Private
066public class TableSchemaModel implements Serializable, ProtobufMessageHandler {
067  private static final long serialVersionUID = 1L;
068  private static final QName IS_META = new QName(HTableDescriptor.IS_META);
069  private static final QName IS_ROOT = new QName(HTableDescriptor.IS_ROOT);
070  private static final QName READONLY = new QName(HTableDescriptor.READONLY);
071  private static final QName TTL = new QName(HColumnDescriptor.TTL);
072  private static final QName VERSIONS = new QName(HConstants.VERSIONS);
073  private static final QName COMPRESSION =
074    new QName(HColumnDescriptor.COMPRESSION);
075
076  private String name;
077  private Map<QName,Object> attrs = new LinkedHashMap<>();
078  private List<ColumnSchemaModel> columns = new ArrayList<>();
079
080  /**
081   * Default constructor.
082   */
083  public TableSchemaModel() {}
084
085  /**
086   * Constructor
087   * @param htd the table descriptor
088   */
089  public TableSchemaModel(HTableDescriptor htd) {
090    setName(htd.getTableName().getNameAsString());
091    for (Map.Entry<Bytes, Bytes> e : htd.getValues().entrySet()) {
092      addAttribute(Bytes.toString(e.getKey().get()),
093        Bytes.toString(e.getValue().get()));
094    }
095    for (HColumnDescriptor hcd : htd.getFamilies()) {
096      ColumnSchemaModel columnModel = new ColumnSchemaModel();
097      columnModel.setName(hcd.getNameAsString());
098      for (Map.Entry<Bytes, Bytes> e:
099          hcd.getValues().entrySet()) {
100        columnModel.addAttribute(Bytes.toString(e.getKey().get()),
101            Bytes.toString(e.getValue().get()));
102      }
103      addColumnFamily(columnModel);
104    }
105  }
106
107  /**
108   * Add an attribute to the table descriptor
109   * @param name attribute name
110   * @param value attribute value
111   */
112  @JsonAnySetter
113  public void addAttribute(String name, Object value) {
114    attrs.put(new QName(name), value);
115  }
116
117  /**
118   * Return a table descriptor value as a string. Calls toString() on the
119   * object stored in the descriptor value map.
120   * @param name the attribute name
121   * @return the attribute value
122   */
123  public String getAttribute(String name) {
124    Object o = attrs.get(new QName(name));
125    return o != null ? o.toString() : null;
126  }
127
128  /**
129   * Add a column family to the table descriptor
130   * @param family the column family model
131   */
132  public void addColumnFamily(ColumnSchemaModel family) {
133    columns.add(family);
134  }
135
136  /**
137   * Retrieve the column family at the given index from the table descriptor
138   * @param index the index
139   * @return the column family model
140   */
141  public ColumnSchemaModel getColumnFamily(int index) {
142    return columns.get(index);
143  }
144
145  /**
146   * @return the table name
147   */
148  @XmlAttribute
149  public String getName() {
150    return name;
151  }
152
153  /**
154   * @return the map for holding unspecified (user) attributes
155   */
156  @XmlAnyAttribute
157  @JsonAnyGetter
158  public Map<QName,Object> getAny() {
159    return attrs;
160  }
161
162  /**
163   * @return the columns
164   */
165  @XmlElement(name="ColumnSchema")
166  public List<ColumnSchemaModel> getColumns() {
167    return columns;
168  }
169
170  /**
171   * @param name the table name
172   */
173  public void setName(String name) {
174    this.name = name;
175  }
176
177  /**
178   * @param columns the columns to set
179   */
180  public void setColumns(List<ColumnSchemaModel> columns) {
181    this.columns = columns;
182  }
183
184  /* (non-Javadoc)
185   * @see java.lang.Object#toString()
186   */
187  @Override
188  public String toString() {
189    StringBuilder sb = new StringBuilder();
190    sb.append("{ NAME=> '");
191    sb.append(name);
192    sb.append('\'');
193    for (Map.Entry<QName,Object> e : attrs.entrySet()) {
194      sb.append(", ");
195      sb.append(e.getKey().getLocalPart());
196      sb.append(" => '");
197      sb.append(e.getValue().toString());
198      sb.append('\'');
199    }
200    sb.append(", COLUMNS => [ ");
201    Iterator<ColumnSchemaModel> i = columns.iterator();
202    while (i.hasNext()) {
203      ColumnSchemaModel family = i.next();
204      sb.append(family.toString());
205      if (i.hasNext()) {
206        sb.append(',');
207      }
208      sb.append(' ');
209    }
210    sb.append("] }");
211    return sb.toString();
212  }
213
214  // getters and setters for common schema attributes
215
216  // cannot be standard bean type getters and setters, otherwise this would
217  // confuse JAXB
218
219  /**
220   * @return true if IS_META attribute exists and is truel
221   */
222  public boolean __getIsMeta() {
223    Object o = attrs.get(IS_META);
224    return o != null && Boolean.parseBoolean(o.toString());
225  }
226
227  /**
228   * @return true if IS_ROOT attribute exists and is truel
229   */
230  public boolean __getIsRoot() {
231    Object o = attrs.get(IS_ROOT);
232    return o != null && Boolean.parseBoolean(o.toString());
233  }
234
235  /**
236   * @return true if READONLY attribute exists and is truel
237   */
238  public boolean __getReadOnly() {
239    Object o = attrs.get(READONLY);
240    return o != null ? Boolean.parseBoolean(o.toString()) : HTableDescriptor.DEFAULT_READONLY;
241  }
242
243  /**
244   * @param value desired value of IS_META attribute
245   */
246  public void __setIsMeta(boolean value) {
247    attrs.put(IS_META, Boolean.toString(value));
248  }
249
250  /**
251   * @param value desired value of IS_ROOT attribute
252   */
253  public void __setIsRoot(boolean value) {
254    attrs.put(IS_ROOT, Boolean.toString(value));
255  }
256
257  /**
258   * @param value desired value of READONLY attribute
259   */
260  public void __setReadOnly(boolean value) {
261    attrs.put(READONLY, Boolean.toString(value));
262  }
263
264  @Override
265  public byte[] createProtobufOutput() {
266    TableSchema.Builder builder = TableSchema.newBuilder();
267    builder.setName(name);
268    for (Map.Entry<QName, Object> e : attrs.entrySet()) {
269      TableSchema.Attribute.Builder attrBuilder =
270        TableSchema.Attribute.newBuilder();
271      attrBuilder.setName(e.getKey().getLocalPart());
272      attrBuilder.setValue(e.getValue().toString());
273      builder.addAttrs(attrBuilder);
274    }
275    for (ColumnSchemaModel family : columns) {
276      Map<QName, Object> familyAttrs = family.getAny();
277      ColumnSchema.Builder familyBuilder = ColumnSchema.newBuilder();
278      familyBuilder.setName(family.getName());
279      for (Map.Entry<QName, Object> e : familyAttrs.entrySet()) {
280        ColumnSchema.Attribute.Builder attrBuilder =
281          ColumnSchema.Attribute.newBuilder();
282        attrBuilder.setName(e.getKey().getLocalPart());
283        attrBuilder.setValue(e.getValue().toString());
284        familyBuilder.addAttrs(attrBuilder);
285      }
286      if (familyAttrs.containsKey(TTL)) {
287        familyBuilder.setTtl(Integer.parseInt(familyAttrs.get(TTL).toString()));
288      }
289      if (familyAttrs.containsKey(VERSIONS)) {
290        familyBuilder.setMaxVersions(Integer.parseInt(familyAttrs.get(VERSIONS).toString()));
291      }
292      if (familyAttrs.containsKey(COMPRESSION)) {
293        familyBuilder.setCompression(familyAttrs.get(COMPRESSION).toString());
294      }
295      builder.addColumns(familyBuilder);
296    }
297    if (attrs.containsKey(READONLY)) {
298      builder.setReadOnly(Boolean.parseBoolean(attrs.get(READONLY).toString()));
299    }
300    return builder.build().toByteArray();
301  }
302
303  @Override
304  public ProtobufMessageHandler getObjectFromMessage(byte[] message)
305      throws IOException {
306    TableSchema.Builder builder = TableSchema.newBuilder();
307    ProtobufUtil.mergeFrom(builder, message);
308    this.setName(builder.getName());
309    for (TableSchema.Attribute attr : builder.getAttrsList()) {
310      this.addAttribute(attr.getName(), attr.getValue());
311    }
312    if (builder.hasReadOnly()) {
313      this.addAttribute(HTableDescriptor.READONLY, builder.getReadOnly());
314    }
315    for (ColumnSchema family : builder.getColumnsList()) {
316      ColumnSchemaModel familyModel = new ColumnSchemaModel();
317      familyModel.setName(family.getName());
318      for (ColumnSchema.Attribute attr : family.getAttrsList()) {
319        familyModel.addAttribute(attr.getName(), attr.getValue());
320      }
321      if (family.hasTtl()) {
322        familyModel.addAttribute(HColumnDescriptor.TTL, family.getTtl());
323      }
324      if (family.hasMaxVersions()) {
325        familyModel.addAttribute(HConstants.VERSIONS,
326          family.getMaxVersions());
327      }
328      if (family.hasCompression()) {
329        familyModel.addAttribute(HColumnDescriptor.COMPRESSION,
330          family.getCompression());
331      }
332      this.addColumnFamily(familyModel);
333    }
334    return this;
335  }
336
337  /**
338   * @return a table descriptor
339   */
340  @JsonIgnore
341  public HTableDescriptor getTableDescriptor() {
342    HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(getName()));
343    for (Map.Entry<QName, Object> e : getAny().entrySet()) {
344      htd.setValue(e.getKey().getLocalPart(), e.getValue().toString());
345    }
346    for (ColumnSchemaModel column: getColumns()) {
347      HColumnDescriptor hcd = new HColumnDescriptor(column.getName());
348      for (Map.Entry<QName, Object> e : column.getAny().entrySet()) {
349        hcd.setValue(e.getKey().getLocalPart(), e.getValue().toString());
350      }
351      htd.addFamily(hcd);
352    }
353    return htd;
354  }
355}