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