@Override
  public void createTable(ByteBuffer in_tableName, List<ColumnDescriptor> columnFamilies)
    throws IOError, IllegalArgument, AlreadyExists {
    TableName tableName = getTableName(in_tableName);
    try {
      if (getAdmin().tableExists(tableName)) {
        throw new AlreadyExists("table name already in use");
      }
      TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
      for (ColumnDescriptor col : columnFamilies) {
        builder.setColumnFamily(ThriftUtilities.colDescFromThrift(col));
      }
      getAdmin().createTable(builder.build());
    } catch (IOException e) {
      LOG.warn(e.getMessage(), e);
      throw getIOError(e);
    } catch (IllegalArgumentException e) {
      LOG.warn(e.getMessage(), e);
      throw new IllegalArgument(Throwables.getStackTraceAsString(e));
    }
  }
Note: this internal interface is slightly different from public APIs in regard to handling of
the qualifier. Here we differ from the public Java API in that null != byte[0]. Rather, we
respect qual == null as a request for the entire column family. The caller (
{@link #getVerTs(ByteBuffer, ByteBuffer, ByteBuffer, long, int, Map)}) interface IS consistent
in that the column is parse like normal.
hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHBaseServiceHandler.java