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