METAMODEL-1205: Fixed CassandraUnit, Guava, Hadoop for JDK9+
[metamodel.git] / hbase / src / main / java / org / apache / metamodel / hbase / HBaseClient.java
1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.metamodel.hbase;
20
21 import java.io.IOException;
22 import java.util.Set;
23
24 import org.apache.hadoop.hbase.TableName;
25 import org.apache.hadoop.hbase.client.Admin;
26 import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
27 import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
28 import org.apache.hadoop.hbase.client.Connection;
29 import org.apache.hadoop.hbase.client.Delete;
30 import org.apache.hadoop.hbase.client.Get;
31 import org.apache.hadoop.hbase.client.Put;
32 import org.apache.hadoop.hbase.client.Table;
33 import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
34 import org.apache.hadoop.hbase.util.Bytes;
35 import org.apache.metamodel.MetaModelException;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40 * This class can perform client-operations on a HBase datastore
41 */
42 final class HBaseClient {
43
44 private static final Logger logger = LoggerFactory.getLogger(HBaseClient.class);
45
46 private final Connection _connection;
47
48 public HBaseClient(final Connection connection) {
49 this._connection = connection;
50 }
51
52 /**
53 * Insert a single row of values to a HBase table.
54 * @param tableName
55 * @param columns
56 * @param values
57 * @throws IllegalArgumentException when any parameter is null or the indexOfIdColumn is impossible
58 * @throws MetaModelException when no ID-column is found.
59 * @throws MetaModelException when a {@link IOException} is caught
60 */
61 public void insertRow(final String tableName, final HBaseColumn[] columns, final Object[] values,
62 final int indexOfIdColumn) {
63 if (tableName == null || columns == null || values == null || indexOfIdColumn >= values.length
64 || values[indexOfIdColumn] == null) {
65 throw new IllegalArgumentException(
66 "Can't insert a row without having (correct) tableName, columns, values or indexOfIdColumn");
67 }
68 if (columns.length != values.length) {
69 throw new IllegalArgumentException("The amount of columns don't match the amount of values");
70 }
71 try (final Table table = _connection.getTable(TableName.valueOf(tableName))) {
72 // Create a put with the values of indexOfIdColumn as rowkey
73 final Put put = new Put(getValueAsByteArray(values[indexOfIdColumn]));
74
75 // Add the other values to the put
76 for (int i = 0; i < columns.length; i++) {
77 if (i != indexOfIdColumn) {
78 // NullChecker is already forced within the HBaseColumn class
79 final byte[] columnFamily = Bytes.toBytes(columns[i].getColumnFamily());
80 // An HBaseColumn doesn't need a qualifier, this only works when the qualifier is empty (not
81 // null). Otherwise NullPointer exceptions will happen
82 byte[] qualifier = null;
83 if (columns[i].getQualifier() != null) {
84 qualifier = Bytes.toBytes(columns[i].getQualifier());
85 } else {
86 qualifier = Bytes.toBytes(new String(""));
87 }
88 final byte[] value = getValueAsByteArray(values[i]);
89 // A NULL value, doesn't get inserted in HBase
90 // TODO: Do we delete the cell (and therefore the qualifier) if the table get's updated by a NULL
91 // value?
92 if (value == null) {
93 logger.info("The value of column '{}' is null. This insertion is skipped", columns[i]
94 .getName());
95 } else {
96 put.addColumn(columnFamily, qualifier, value);
97 }
98 }
99 }
100 // Add the put to the table
101 table.put(put);
102 } catch (IOException e) {
103 throw new MetaModelException(e);
104 }
105 }
106
107 /**
108 * Delete 1 row based on the key
109 * @param tableName
110 * @param rowKey
111 * @throws IllegalArgumentException when any parameter is null
112 * @throws MetaModelException when a {@link IOException} is caught
113 */
114 public void deleteRow(final String tableName, final Object rowKey) {
115 if (tableName == null || rowKey == null) {
116 throw new IllegalArgumentException("Can't delete a row without having tableName or rowKey");
117 }
118 byte[] rowKeyAsByteArray = getValueAsByteArray(rowKey);
119 if (rowKeyAsByteArray.length > 0) {
120 try (final Table table = _connection.getTable(TableName.valueOf(tableName))) {
121 if (rowExists(table, rowKeyAsByteArray)) {
122 table.delete(new Delete(rowKeyAsByteArray));
123 } else {
124 logger.warn("Rowkey with value {} doesn't exist in the table", rowKey.toString());
125 }
126 } catch (IOException e) {
127 throw new MetaModelException(e);
128 }
129 } else {
130 throw new IllegalArgumentException("Can't delete a row without an empty rowKey.");
131 }
132 }
133
134 /**
135 * Checks in the HBase datastore if a row exists based on the key
136 * @param table
137 * @param rowKey
138 * @return boolean
139 * @throws IOException
140 */
141 private boolean rowExists(final Table table, final byte[] rowKey) throws IOException {
142 final Get get = new Get(rowKey);
143 return !table.get(get).isEmpty();
144 }
145
146 /**
147 * Creates a HBase table based on a tableName and it's columnFamilies
148 * @param tableName
149 * @param columnFamilies
150 * @throws IllegalArgumentException when any parameter is null
151 * @throws MetaModelException when a {@link IOException} is caught
152 */
153 public void createTable(final String tableName, final Set<String> columnFamilies) {
154 if (tableName == null || columnFamilies == null || columnFamilies.isEmpty()) {
155 throw new IllegalArgumentException("Can't create a table without having the tableName or columnFamilies");
156 }
157 try (final Admin admin = _connection.getAdmin()) {
158 final TableName hBasetableName = TableName.valueOf(tableName);
159 final TableDescriptorBuilder tableBuilder = TableDescriptorBuilder.newBuilder(hBasetableName);
160 // Add all columnFamilies to the tableDescriptor.
161 for (final String columnFamily : columnFamilies) {
162 // The ID-column isn't needed because, it will automatically be created.
163 if (!columnFamily.equals(HBaseDataContext.FIELD_ID)) {
164 final ColumnFamilyDescriptor columnDescriptor = ColumnFamilyDescriptorBuilder.of(columnFamily);
165 tableBuilder.setColumnFamily(columnDescriptor);
166 }
167 }
168 admin.createTable(tableBuilder.build());
169 } catch (IOException e) {
170 throw new MetaModelException(e);
171 }
172 }
173
174 /**
175 * Disable and drop a table from a HBase datastore
176 * @param tableName
177 * @throws IllegalArgumentException when tableName is null
178 * @throws MetaModelException when a {@link IOException} is caught
179 */
180 public void dropTable(final String tableName) {
181 if (tableName == null) {
182 throw new IllegalArgumentException("Can't drop a table without having the tableName");
183 }
184 try (final Admin admin = _connection.getAdmin()) {
185 final TableName hBasetableName = TableName.valueOf(tableName);
186 admin.disableTable(hBasetableName); // A table must be disabled first, before it can be deleted
187 admin.deleteTable(hBasetableName);
188 } catch (IOException e) {
189 throw new MetaModelException(e);
190 }
191 }
192
193 /**
194 * Converts a Object value into a byte array, if it isn't a byte array already
195 * @param value
196 * @return value as a byte array
197 */
198 private byte[] getValueAsByteArray(final Object value) {
199 byte[] valueAsByteArray;
200 if (value == null) {
201 valueAsByteArray = null;
202 } else if (value instanceof byte[]) {
203 valueAsByteArray = (byte[]) value;
204 } else {
205 valueAsByteArray = Bytes.toBytes(value.toString());
206 }
207 return valueAsByteArray;
208 }
209 }