package eu.fbk.knowledgestore.datastore.hbase.utils; import static eu.fbk.knowledgestore.datastore.hbase.utils.HBaseConstants.DEFAULT_CON_TAB_NAME; import static eu.fbk.knowledgestore.datastore.hbase.utils.HBaseConstants.DEFAULT_ENT_TAB_NAME; import static eu.fbk.knowledgestore.datastore.hbase.utils.HBaseConstants.DEFAULT_MEN_TAB_NAME; import static eu.fbk.knowledgestore.datastore.hbase.utils.HBaseConstants.DEFAULT_RES_TAB_NAME; import static eu.fbk.knowledgestore.datastore.hbase.utils.HBaseConstants.DEFAULT_USR_TAB_NAME; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.annotation.Nullable; import com.continuuity.tephra.TransactionContext; import com.continuuity.tephra.TransactionFailureException; import com.continuuity.tephra.TransactionSystemClient; import com.continuuity.tephra.hbase.TransactionAwareHTable; import com.continuuity.tephra.inmemory.InMemoryTxSystemClient; import com.continuuity.tephra.inmemory.InMemoryTransactionManager; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Bytes; import org.openrdf.model.URI; import eu.fbk.knowledgestore.data.Record; import eu.fbk.knowledgestore.data.Stream; import eu.fbk.knowledgestore.data.XPath; import eu.fbk.knowledgestore.datastore.hbase.HBaseScanIterator; import eu.fbk.knowledgestore.runtime.DataCorruptedException; /** * Implements HBase operations using Continuuity Tephra. */ public class TephraHBaseUtils extends AbstractHBaseUtils { /** Transaction client managed by Tephra */ private TransactionSystemClient txClient; /** Transaction context managed by Tephra */ private final TransactionContext txContext; /** The map tableName -> table handle */ private static Map<String, TransactionAwareHTable> tableNameHandleMap = new HashMap<String, TransactionAwareHTable>(); /** * Constructor. * * @param properties * the configuration properties */ public TephraHBaseUtils(final Properties properties) { // setting basic configuration inside parent class. super(properties); final String TEPHRA_HOST = "data.tx.bind.address"; final String TEPHRA_PORT = "data.tx.bind.port"; getHbcfg().set(TEPHRA_HOST, "escher"); getHbcfg().setInt(TEPHRA_PORT, 15165); // create and cache all the 5 TransactionAwareHTable tables (one for each HBase table) final List<TransactionAwareHTable> txTabList = new ArrayList<TransactionAwareHTable>(5); try { logger.debug("TEPHRA create the 5 TransactionAwareHTable tables"); final List<String> tableNames = Arrays.asList(DEFAULT_RES_TAB_NAME, DEFAULT_MEN_TAB_NAME, DEFAULT_ENT_TAB_NAME, DEFAULT_CON_TAB_NAME, DEFAULT_USR_TAB_NAME); HTable hTab; TransactionAwareHTable txTab; for (final String tableName : tableNames) { hTab = new HTable(this.getHbcfg(), tableName); txTab = new TransactionAwareHTable(hTab); tableNameHandleMap.put(tableName, txTab); txTabList.add(txTab); logger.debug("TEPHRA Cached a handle of table: " + tableName); } } catch (final IOException e) { logger.error("TEPHRA Error while creating 5 TransactionAwareHTable tables"); logger.error(e.getMessage()); } // Creating transaction client and context this.txClient = new InMemoryTxSystemClient(new InMemoryTransactionManager(this.getHbcfg())); this.txContext = new TransactionContext(txClient, txTabList.toArray(new TransactionAwareHTable[0])); } /** * Commits work done. */ @Override public void commit() throws DataCorruptedException, IOException, IllegalStateException { try { this.txContext.finish(); } catch (final TransactionFailureException tfe1) { try { this.txContext.abort(); } catch (final TransactionFailureException tfe2) { } throw new IOException("Error trying to commit transaction.", tfe1); } } /** * Rollbacks work done. */ @Override public void rollback() throws DataCorruptedException, IOException, IllegalStateException { try { this.txContext.abort(); } catch (final Exception e) { throw new DataCorruptedException("Error trying to rollback a Transaction.", e); } } /** * Gets a handle of a specific table. * * @param tableName * of the table to be accessed. * @return HTable of the table found. */ @Override public Object getTable(final String tableName) { logger.debug("TEPHRA Begin of getTable for " + tableName); final TransactionAwareHTable table = tableNameHandleMap.get(tableName); if (table == null) { // TODO propagate exceptions logger.error("TEPHRA error: unebale to find cached table " + tableName); return table; } else { logger.debug("TEPHRA Found a cached handle for table " + tableName); return table; } } @Override public void processPut(final Record record, final String tabName, final String famName, final String quaName) { logger.debug("TEPHRA Begin processPut(" + record + ", " + tabName + ")"); final TransactionAwareHTable txTable = (TransactionAwareHTable) getTable(tabName); Put op = null; try { op = createPut(record, tabName, famName, quaName); txTable.put(op); // TODO rollback if there is an exception } catch (final IllegalArgumentException e) { // TODO propagate exceptions logger.error("Error while attempting to perform operations at HBaseDataTransactions."); logger.error(e.getMessage()); } catch (final IOException e) { // TODO propagate exceptions logger.error("Error while attempting to perform operations at HBaseDataTransactions."); logger.error(e.getMessage()); } } @Override public void begin() { try { this.txContext.start(); } catch (final TransactionFailureException e) { logger.error("Error while attempting to perform operations at HBaseDataTransactions."); logger.error(e.getMessage()); } } @Override public void processDelete(final URI id, final String tabName, final String famName, final String quaName) { logger.debug("TEPHRA Begin processDelete(" + id + ", " + tabName + ")"); final TransactionAwareHTable txTable = (TransactionAwareHTable) getTable(tabName); Delete op = null; try { op = createDelete(id, tabName); txTable.delete(op); } catch (final IllegalArgumentException e) { logger.error("Error while attempting to perform operations at HBaseDataTransactions."); logger.error(e.getMessage()); } catch (final IOException e) { logger.error("Error while attempting to perform operations at HBaseDataTransactions."); logger.error(e.getMessage()); } } /** * Gets a Record based on information passed. * * @param tableName * to do the get. * @param id * of the Record needed * @throws IOException */ @Override public Record get(final String tableName, final URI id) throws IOException { logger.debug("TEPHRA Begin of get(" + tableName + ", " + id + ")"); final TransactionAwareHTable txTable = (TransactionAwareHTable) getTable(tableName); Record resGotten = null; if (txTable != null) { // Resource's Key final Get get = new Get(Bytes.toBytes(id.toString())).setMaxVersions(1); final Result rs = txTable.get(get); logger.debug("Value obtained: " + new String(rs.value())); final AvroSerializer serializer = getSerializer(); resGotten = (Record) serializer.fromBytes(rs.value()); } return resGotten; } @Override public List<Record> get(final String tableName, final List<URI> ids) throws IOException { logger.debug("TEPHRA Begin of get(" + tableName + ", " + ids + ")"); final TransactionAwareHTable txTable = (TransactionAwareHTable) getTable(tableName); final List<Record> resGotten = new ArrayList<Record>(); final List<Get> gets = new ArrayList<Get>(); final AvroSerializer serializer = getSerializer(); for (final URI id : ids) { gets.add(new Get(Bytes.toBytes(id.toString()))); } // OMID does support the usage of a list of gets final Result[] results = txTable.get(gets); for (final Result res : results) { final byte[] bytes = res.value(); if (bytes != null) { resGotten.add((Record) serializer.fromBytes(bytes)); } } return resGotten; } @Override public List<Object> checkForErrors(final Object[] objs) { return new ArrayList<Object>(); } /** * Gets a number of record of tableName matching condition * * @param tableName * to scan * @param condition * to match * @param scan * to scan * @throws IOException */ @Override public long count(final String tableName, final String familyName, @Nullable final XPath condition) throws IOException { logger.debug("TEPHRA Begin count"); // VERY INEFFICIENT: to be improved! final Stream<Record> cur = Stream.create(new HBaseScanIterator(this, tableName, familyName, condition, null, getServerFilterFlag())); try { return cur.count(); } finally { cur.close(); } } /** * Gets a scanner for a specific table * * @param tableName * to get the scanner from * @param scan * for the specific table * @param conf * object to get a hold of an HBase table */ @Override public ResultScanner getScanner(final String tableName, final Scan scan) { logger.debug("TEPHRA Begin of getScanner(" + tableName + ", " + scan + ")"); final TransactionAwareHTable txTable = (TransactionAwareHTable) getTable(tableName); ResultScanner resScanner = null; try { resScanner = txTable.getScanner(scan); } catch (final IOException e) { logger.error("Error while trying to obtain a ResultScanner: " + tableName); logger.error(e.getMessage()); } return resScanner; } }