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