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.HConstants;
036import org.apache.hadoop.hbase.TableName;
037import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
038import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
039import org.apache.hadoop.hbase.client.TableDescriptor;
040import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
041import org.apache.hadoop.hbase.rest.ProtobufMessageHandler;
042import org.apache.hadoop.hbase.rest.RestUtil;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.apache.yetus.audience.InterfaceAudience;
045
046import org.apache.hbase.thirdparty.com.google.protobuf.CodedInputStream;
047import org.apache.hbase.thirdparty.com.google.protobuf.Message;
048
049import org.apache.hadoop.hbase.shaded.rest.protobuf.generated.ColumnSchemaMessage.ColumnSchema;
050import org.apache.hadoop.hbase.shaded.rest.protobuf.generated.TableSchemaMessage.TableSchema;
051
052/**
053 * A representation of HBase table descriptors.
054 *
055 * <pre>
056 * &lt;complexType name="TableSchema"&gt;
057 *   &lt;sequence&gt;
058 *     &lt;element name="column" type="tns:ColumnSchema"
059 *       maxOccurs="unbounded" minOccurs="1"&gt;&lt;/element&gt;
060 *   &lt;/sequence&gt;
061 *   &lt;attribute name="name" type="string"&gt;&lt;/attribute&gt;
062 *   &lt;anyAttribute&gt;&lt;/anyAttribute&gt;
063 * &lt;/complexType&gt;
064 * </pre>
065 */
066@XmlRootElement(name = "TableSchema")
067@InterfaceAudience.Private
068public class TableSchemaModel implements Serializable, ProtobufMessageHandler {
069  private static final long serialVersionUID = 1L;
070  private static final QName IS_META = new QName(TableDescriptorBuilder.IS_META);
071  private static final QName IS_ROOT = new QName("IS_ROOT");
072  private static final QName READONLY = new QName(TableDescriptorBuilder.READONLY);
073  private static final QName TTL = new QName(ColumnFamilyDescriptorBuilder.TTL);
074  private static final QName VERSIONS = new QName(HConstants.VERSIONS);
075  private static final QName COMPRESSION = new QName(ColumnFamilyDescriptorBuilder.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  /**
088   * Constructor
089   * @param tableDescriptor the table descriptor
090   */
091  public TableSchemaModel(TableDescriptor tableDescriptor) {
092    setName(tableDescriptor.getTableName().getNameAsString());
093    for (Map.Entry<Bytes, Bytes> e : tableDescriptor.getValues().entrySet()) {
094      addAttribute(Bytes.toString(e.getKey().get()), Bytes.toString(e.getValue().get()));
095    }
096    for (ColumnFamilyDescriptor hcd : tableDescriptor.getColumnFamilies()) {
097      ColumnSchemaModel columnModel = new ColumnSchemaModel();
098      columnModel.setName(hcd.getNameAsString());
099      for (Map.Entry<Bytes, Bytes> e : 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 object stored in the
119   * 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  /** Returns the table name */
146  @XmlAttribute
147  public String getName() {
148    return name;
149  }
150
151  /** Returns the map for holding unspecified (user) attributes */
152  @XmlAnyAttribute
153  @JsonAnyGetter
154  public Map<QName, Object> getAny() {
155    return attrs;
156  }
157
158  /** Returns the columns */
159  @XmlElement(name = "ColumnSchema")
160  public List<ColumnSchemaModel> getColumns() {
161    return columns;
162  }
163
164  /**
165   * @param name the table name
166   */
167  public void setName(String name) {
168    this.name = name;
169  }
170
171  /**
172   * @param columns the columns to set
173   */
174  public void setColumns(List<ColumnSchemaModel> columns) {
175    this.columns = columns;
176  }
177
178  /*
179   * (non-Javadoc)
180   * @see java.lang.Object#toString()
181   */
182  @Override
183  public String toString() {
184    StringBuilder sb = new StringBuilder();
185    sb.append("{ NAME=> '");
186    sb.append(name);
187    sb.append('\'');
188    for (Map.Entry<QName, Object> e : attrs.entrySet()) {
189      sb.append(", ");
190      sb.append(e.getKey().getLocalPart());
191      sb.append(" => '");
192      sb.append(e.getValue().toString());
193      sb.append('\'');
194    }
195    sb.append(", COLUMNS => [ ");
196    Iterator<ColumnSchemaModel> i = columns.iterator();
197    while (i.hasNext()) {
198      ColumnSchemaModel family = i.next();
199      sb.append(family.toString());
200      if (i.hasNext()) {
201        sb.append(',');
202      }
203      sb.append(' ');
204    }
205    sb.append("] }");
206    return sb.toString();
207  }
208
209  // getters and setters for common schema attributes
210
211  // cannot be standard bean type getters and setters, otherwise this would
212  // confuse JAXB
213
214  /** Returns true if IS_META attribute exists and is truel */
215  public boolean __getIsMeta() {
216    Object o = attrs.get(IS_META);
217    return o != null && Boolean.parseBoolean(o.toString());
218  }
219
220  /** Returns true if IS_ROOT attribute exists and is truel */
221  public boolean __getIsRoot() {
222    Object o = attrs.get(IS_ROOT);
223    return o != null && Boolean.parseBoolean(o.toString());
224  }
225
226  /** Returns true if READONLY attribute exists and is truel */
227  public boolean __getReadOnly() {
228    Object o = attrs.get(READONLY);
229    return o != null ? Boolean.parseBoolean(o.toString()) : TableDescriptorBuilder.DEFAULT_READONLY;
230  }
231
232  /**
233   * @param value desired value of IS_META attribute
234   */
235  public void __setIsMeta(boolean value) {
236    attrs.put(IS_META, Boolean.toString(value));
237  }
238
239  /**
240   * @param value desired value of IS_ROOT attribute
241   */
242  public void __setIsRoot(boolean value) {
243    attrs.put(IS_ROOT, Boolean.toString(value));
244  }
245
246  /**
247   * @param value desired value of READONLY attribute
248   */
249  public void __setReadOnly(boolean value) {
250    attrs.put(READONLY, Boolean.toString(value));
251  }
252
253  @Override
254  public Message messageFromObject() {
255    TableSchema.Builder builder = TableSchema.newBuilder();
256    builder.setName(name);
257    for (Map.Entry<QName, Object> e : attrs.entrySet()) {
258      TableSchema.Attribute.Builder attrBuilder = TableSchema.Attribute.newBuilder();
259      attrBuilder.setName(e.getKey().getLocalPart());
260      attrBuilder.setValue(e.getValue().toString());
261      builder.addAttrs(attrBuilder);
262    }
263    for (ColumnSchemaModel family : columns) {
264      Map<QName, Object> familyAttrs = family.getAny();
265      ColumnSchema.Builder familyBuilder = ColumnSchema.newBuilder();
266      familyBuilder.setName(family.getName());
267      for (Map.Entry<QName, Object> e : familyAttrs.entrySet()) {
268        ColumnSchema.Attribute.Builder attrBuilder = ColumnSchema.Attribute.newBuilder();
269        attrBuilder.setName(e.getKey().getLocalPart());
270        attrBuilder.setValue(e.getValue().toString());
271        familyBuilder.addAttrs(attrBuilder);
272      }
273      if (familyAttrs.containsKey(TTL)) {
274        familyBuilder.setTtl(Integer.parseInt(familyAttrs.get(TTL).toString()));
275      }
276      if (familyAttrs.containsKey(VERSIONS)) {
277        familyBuilder.setMaxVersions(Integer.parseInt(familyAttrs.get(VERSIONS).toString()));
278      }
279      if (familyAttrs.containsKey(COMPRESSION)) {
280        familyBuilder.setCompression(familyAttrs.get(COMPRESSION).toString());
281      }
282      builder.addColumns(familyBuilder);
283    }
284    if (attrs.containsKey(READONLY)) {
285      builder.setReadOnly(Boolean.parseBoolean(attrs.get(READONLY).toString()));
286    }
287    return builder.build();
288  }
289
290  @Override
291  public ProtobufMessageHandler getObjectFromMessage(CodedInputStream cis) throws IOException {
292    TableSchema.Builder builder = TableSchema.newBuilder();
293    RestUtil.mergeFrom(builder, cis);
294    this.setName(builder.getName());
295    for (TableSchema.Attribute attr : builder.getAttrsList()) {
296      this.addAttribute(attr.getName(), attr.getValue());
297    }
298    if (builder.hasReadOnly()) {
299      this.addAttribute(TableDescriptorBuilder.READONLY, builder.getReadOnly());
300    }
301    for (ColumnSchema family : builder.getColumnsList()) {
302      ColumnSchemaModel familyModel = new ColumnSchemaModel();
303      familyModel.setName(family.getName());
304      for (ColumnSchema.Attribute attr : family.getAttrsList()) {
305        familyModel.addAttribute(attr.getName(), attr.getValue());
306      }
307      if (family.hasTtl()) {
308        familyModel.addAttribute(ColumnFamilyDescriptorBuilder.TTL, family.getTtl());
309      }
310      if (family.hasMaxVersions()) {
311        familyModel.addAttribute(HConstants.VERSIONS, family.getMaxVersions());
312      }
313      if (family.hasCompression()) {
314        familyModel.addAttribute(ColumnFamilyDescriptorBuilder.COMPRESSION,
315          family.getCompression());
316      }
317      this.addColumnFamily(familyModel);
318    }
319    return this;
320  }
321
322  /** Returns a table descriptor */
323  @JsonIgnore
324  public TableDescriptor getTableDescriptor() {
325    TableDescriptorBuilder tableDescriptorBuilder =
326      TableDescriptorBuilder.newBuilder(TableName.valueOf(getName()));
327    for (Map.Entry<QName, Object> e : getAny().entrySet()) {
328      tableDescriptorBuilder.setValue(e.getKey().getLocalPart(), e.getValue().toString());
329    }
330    for (ColumnSchemaModel column : getColumns()) {
331      ColumnFamilyDescriptorBuilder cfdb =
332        ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(column.getName()));
333      for (Map.Entry<QName, Object> e : column.getAny().entrySet()) {
334        cfdb.setValue(e.getKey().getLocalPart(), e.getValue().toString());
335      }
336      tableDescriptorBuilder.setColumnFamily(cfdb.build());
337    }
338    return tableDescriptorBuilder.build();
339  }
340}