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.rest.protobuf.generated.ColumnSchemaMessage.ColumnSchema;
044import org.apache.hadoop.hbase.rest.protobuf.generated.TableSchemaMessage.TableSchema;
045import org.apache.hadoop.hbase.util.Bytes;
046import org.apache.yetus.audience.InterfaceAudience;
047
048import org.apache.hbase.thirdparty.com.google.protobuf.CodedInputStream;
049import org.apache.hbase.thirdparty.com.google.protobuf.Message;
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(TableDescriptorBuilder.IS_META);
070  private static final QName IS_ROOT = new QName("IS_ROOT");
071  private static final QName READONLY = new QName(TableDescriptorBuilder.READONLY);
072  private static final QName TTL = new QName(ColumnFamilyDescriptorBuilder.TTL);
073  private static final QName VERSIONS = new QName(HConstants.VERSIONS);
074  private static final QName COMPRESSION = new QName(ColumnFamilyDescriptorBuilder.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  /**
087   * Constructor
088   * @param tableDescriptor the table descriptor
089   */
090  public TableSchemaModel(TableDescriptor tableDescriptor) {
091    setName(tableDescriptor.getTableName().getNameAsString());
092    for (Map.Entry<Bytes, Bytes> e : tableDescriptor.getValues().entrySet()) {
093      addAttribute(Bytes.toString(e.getKey().get()), Bytes.toString(e.getValue().get()));
094    }
095    for (ColumnFamilyDescriptor hcd : tableDescriptor.getColumnFamilies()) {
096      ColumnSchemaModel columnModel = new ColumnSchemaModel();
097      columnModel.setName(hcd.getNameAsString());
098      for (Map.Entry<Bytes, Bytes> e : hcd.getValues().entrySet()) {
099        columnModel.addAttribute(Bytes.toString(e.getKey().get()),
100          Bytes.toString(e.getValue().get()));
101      }
102      addColumnFamily(columnModel);
103    }
104  }
105
106  /**
107   * Add an attribute to the table descriptor
108   * @param name  attribute name
109   * @param value attribute value
110   */
111  @JsonAnySetter
112  public void addAttribute(String name, Object value) {
113    attrs.put(new QName(name), value);
114  }
115
116  /**
117   * Return a table descriptor value as a string. Calls toString() on the object stored in the
118   * descriptor value map.
119   * @param name the attribute name
120   * @return the attribute value
121   */
122  public String getAttribute(String name) {
123    Object o = attrs.get(new QName(name));
124    return o != null ? o.toString() : null;
125  }
126
127  /**
128   * Add a column family to the table descriptor
129   * @param family the column family model
130   */
131  public void addColumnFamily(ColumnSchemaModel family) {
132    columns.add(family);
133  }
134
135  /**
136   * Retrieve the column family at the given index from the table descriptor
137   * @param index the index
138   * @return the column family model
139   */
140  public ColumnSchemaModel getColumnFamily(int index) {
141    return columns.get(index);
142  }
143
144  /** Returns the table name */
145  @XmlAttribute
146  public String getName() {
147    return name;
148  }
149
150  /** Returns the map for holding unspecified (user) attributes */
151  @XmlAnyAttribute
152  @JsonAnyGetter
153  public Map<QName, Object> getAny() {
154    return attrs;
155  }
156
157  /** Returns the columns */
158  @XmlElement(name = "ColumnSchema")
159  public List<ColumnSchemaModel> getColumns() {
160    return columns;
161  }
162
163  /**
164   * @param name the table name
165   */
166  public void setName(String name) {
167    this.name = name;
168  }
169
170  /**
171   * @param columns the columns to set
172   */
173  public void setColumns(List<ColumnSchemaModel> columns) {
174    this.columns = columns;
175  }
176
177  /*
178   * (non-Javadoc)
179   * @see java.lang.Object#toString()
180   */
181  @Override
182  public String toString() {
183    StringBuilder sb = new StringBuilder();
184    sb.append("{ NAME=> '");
185    sb.append(name);
186    sb.append('\'');
187    for (Map.Entry<QName, Object> e : attrs.entrySet()) {
188      sb.append(", ");
189      sb.append(e.getKey().getLocalPart());
190      sb.append(" => '");
191      sb.append(e.getValue().toString());
192      sb.append('\'');
193    }
194    sb.append(", COLUMNS => [ ");
195    Iterator<ColumnSchemaModel> i = columns.iterator();
196    while (i.hasNext()) {
197      ColumnSchemaModel family = i.next();
198      sb.append(family.toString());
199      if (i.hasNext()) {
200        sb.append(',');
201      }
202      sb.append(' ');
203    }
204    sb.append("] }");
205    return sb.toString();
206  }
207
208  // getters and setters for common schema attributes
209
210  // cannot be standard bean type getters and setters, otherwise this would
211  // confuse JAXB
212
213  /** Returns true if IS_META attribute exists and is truel */
214  public boolean __getIsMeta() {
215    Object o = attrs.get(IS_META);
216    return o != null && Boolean.parseBoolean(o.toString());
217  }
218
219  /** Returns true if IS_ROOT attribute exists and is truel */
220  public boolean __getIsRoot() {
221    Object o = attrs.get(IS_ROOT);
222    return o != null && Boolean.parseBoolean(o.toString());
223  }
224
225  /** Returns true if READONLY attribute exists and is truel */
226  public boolean __getReadOnly() {
227    Object o = attrs.get(READONLY);
228    return o != null ? Boolean.parseBoolean(o.toString()) : TableDescriptorBuilder.DEFAULT_READONLY;
229  }
230
231  /**
232   * @param value desired value of IS_META attribute
233   */
234  public void __setIsMeta(boolean value) {
235    attrs.put(IS_META, Boolean.toString(value));
236  }
237
238  /**
239   * @param value desired value of IS_ROOT attribute
240   */
241  public void __setIsRoot(boolean value) {
242    attrs.put(IS_ROOT, Boolean.toString(value));
243  }
244
245  /**
246   * @param value desired value of READONLY attribute
247   */
248  public void __setReadOnly(boolean value) {
249    attrs.put(READONLY, Boolean.toString(value));
250  }
251
252  @Override
253  public Message messageFromObject() {
254    TableSchema.Builder builder = TableSchema.newBuilder();
255    builder.setName(name);
256    for (Map.Entry<QName, Object> e : attrs.entrySet()) {
257      TableSchema.Attribute.Builder attrBuilder = TableSchema.Attribute.newBuilder();
258      attrBuilder.setName(e.getKey().getLocalPart());
259      attrBuilder.setValue(e.getValue().toString());
260      builder.addAttrs(attrBuilder);
261    }
262    for (ColumnSchemaModel family : columns) {
263      Map<QName, Object> familyAttrs = family.getAny();
264      ColumnSchema.Builder familyBuilder = ColumnSchema.newBuilder();
265      familyBuilder.setName(family.getName());
266      for (Map.Entry<QName, Object> e : familyAttrs.entrySet()) {
267        ColumnSchema.Attribute.Builder attrBuilder = ColumnSchema.Attribute.newBuilder();
268        attrBuilder.setName(e.getKey().getLocalPart());
269        attrBuilder.setValue(e.getValue().toString());
270        familyBuilder.addAttrs(attrBuilder);
271      }
272      if (familyAttrs.containsKey(TTL)) {
273        familyBuilder.setTtl(Integer.parseInt(familyAttrs.get(TTL).toString()));
274      }
275      if (familyAttrs.containsKey(VERSIONS)) {
276        familyBuilder.setMaxVersions(Integer.parseInt(familyAttrs.get(VERSIONS).toString()));
277      }
278      if (familyAttrs.containsKey(COMPRESSION)) {
279        familyBuilder.setCompression(familyAttrs.get(COMPRESSION).toString());
280      }
281      builder.addColumns(familyBuilder);
282    }
283    if (attrs.containsKey(READONLY)) {
284      builder.setReadOnly(Boolean.parseBoolean(attrs.get(READONLY).toString()));
285    }
286    return builder.build();
287  }
288
289  @Override
290  public ProtobufMessageHandler getObjectFromMessage(CodedInputStream cis) throws IOException {
291    TableSchema.Builder builder = TableSchema.newBuilder();
292    RestUtil.mergeFrom(builder, cis);
293    this.setName(builder.getName());
294    for (TableSchema.Attribute attr : builder.getAttrsList()) {
295      this.addAttribute(attr.getName(), attr.getValue());
296    }
297    if (builder.hasReadOnly()) {
298      this.addAttribute(TableDescriptorBuilder.READONLY, builder.getReadOnly());
299    }
300    for (ColumnSchema family : builder.getColumnsList()) {
301      ColumnSchemaModel familyModel = new ColumnSchemaModel();
302      familyModel.setName(family.getName());
303      for (ColumnSchema.Attribute attr : family.getAttrsList()) {
304        familyModel.addAttribute(attr.getName(), attr.getValue());
305      }
306      if (family.hasTtl()) {
307        familyModel.addAttribute(ColumnFamilyDescriptorBuilder.TTL, family.getTtl());
308      }
309      if (family.hasMaxVersions()) {
310        familyModel.addAttribute(HConstants.VERSIONS, family.getMaxVersions());
311      }
312      if (family.hasCompression()) {
313        familyModel.addAttribute(ColumnFamilyDescriptorBuilder.COMPRESSION,
314          family.getCompression());
315      }
316      this.addColumnFamily(familyModel);
317    }
318    return this;
319  }
320
321  /** Returns a table descriptor */
322  @JsonIgnore
323  public TableDescriptor getTableDescriptor() {
324    TableDescriptorBuilder tableDescriptorBuilder =
325      TableDescriptorBuilder.newBuilder(TableName.valueOf(getName()));
326    for (Map.Entry<QName, Object> e : getAny().entrySet()) {
327      tableDescriptorBuilder.setValue(e.getKey().getLocalPart(), e.getValue().toString());
328    }
329    for (ColumnSchemaModel column : getColumns()) {
330      ColumnFamilyDescriptorBuilder cfdb =
331        ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(column.getName()));
332      for (Map.Entry<QName, Object> e : column.getAny().entrySet()) {
333        cfdb.setValue(e.getKey().getLocalPart(), e.getValue().toString());
334      }
335      tableDescriptorBuilder.setColumnFamily(cfdb.build());
336    }
337    return tableDescriptorBuilder.build();
338  }
339}