package ch.unibe.scg.cells.hadoop; import java.io.IOException; import java.security.SecureRandom; import javax.inject.Inject; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.io.hfile.Compression.Algorithm; import ch.unibe.scg.cells.CellSource; import com.google.common.base.Charsets; import com.google.common.io.BaseEncoding; import com.google.protobuf.ByteString; /** Grants access to HBase's tables. */ public class TableAdmin { final private Configuration configuration; final private HTableFactory hTableFactory; @Inject TableAdmin(Configuration configuration, HTableFactory hTableFactory) { this.configuration = configuration; this.hTableFactory = hTableFactory; } /** Drop table if it exists. Silent otherwise. */ public void deleteTable(String tableName) throws IOException { try (HBaseAdmin admin = new HBaseAdmin(configuration)) { if (admin.isTableAvailable(tableName)) { if (admin.isTableEnabled(tableName)) { admin.disableTable(tableName); } admin.deleteTable(tableName); } } } /** Create a temporary hbase table. On close, The temporary table gets deleted from HBase. */ @SuppressWarnings("resource") // Tab gets closed properly in close of return value. public <T> Table<T> createTemporaryTable(final ByteString family) throws IOException { SecureRandom random = new SecureRandom(); byte bytes[] = new byte[20]; random.nextBytes(bytes); final String tableName = "tmp" + BaseEncoding.base16().encode(bytes); final HTable tab = createHTable(tableName, family); String decoded = new String(tab.getTableName(), Charsets.UTF_8); if (!decoded.equals(tableName)) { throw new AssertionError( "There are encoding issues with the name we picked. It's coming back as " + decoded); } return new Table<T>() { @Override public void close() throws IOException { tab.close(); deleteTable(getTableName()); } @Override public String getTableName() { return tableName; } @Override public ByteString getFamilyName() { return family; } @Override public CellSource<T> asCellSource() { return cellSourceToTable(this); } }; } /** Create a non-temporary table. On close, the table will NOT be deleted. */ @SuppressWarnings("resource") // Tab will be properly closed in the return value's close. public <T> Table<T> createTable(final String tableName, final ByteString family) throws IOException { final HTable tab = createHTable(tableName, family); String decoded = new String(tab.getTableName(), Charsets.UTF_8); if (!decoded.equals(tableName)) { throw new IllegalStateException( "There are encoding issues with the name you chose. It's coming back as " + decoded); } return new Table<T>() { @Override public void close() throws IOException { tab.close(); } @Override public String getTableName() { return tableName; } @Override public ByteString getFamilyName() { return family; } @Override public CellSource<T> asCellSource() { return cellSourceToTable(this); } }; } /** @return the handle to an existing named table. Does NOT immediately check if the table exists. */ public <T> Table<T> existing(final String tabName, final ByteString famName) { // TODO: Check if table exists. return new Table<T>() { @Override public void close() throws IOException { // Nothing to do. } @Override public String getTableName() { return tabName; } @Override public ByteString getFamilyName() { return famName; } @Override public CellSource<T> asCellSource() { return cellSourceToTable(this); } }; } private HTable createHTable(String tableName, ByteString family) throws IOException { try (HBaseAdmin admin = new HBaseAdmin(configuration)) { HTableDescriptor td = new HTableDescriptor(tableName); td.addFamily(new HColumnDescriptor(family.toByteArray()).setCompressionType(Algorithm.SNAPPY)); td.addFamily(new HColumnDescriptor(HBaseStorage.INDEX_FAMILY.toByteArray())); admin.createTable(td); } return hTableFactory.make(tableName); } private <T> CellSource<T> cellSourceToTable(Table<T> tab) { SerializableHTable hTable = new SerializableHTable(hTableFactory.make(tab.getTableName()), hTableFactory); return new HBaseCellSource<>(tab.getFamilyName(), hTable); } }