package mil.nga.giat.geowave.datastore.hbase.io;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import mil.nga.giat.geowave.core.index.StringUtils;
import mil.nga.giat.geowave.core.store.base.Writer;
import mil.nga.giat.geowave.datastore.hbase.operations.BasicHBaseOperations;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.BufferedMutator;
import org.apache.hadoop.hbase.client.BufferedMutatorParams;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
import org.apache.hadoop.hbase.client.RowMutations;
import org.apache.hadoop.hbase.client.BufferedMutator.ExceptionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Functionality similar to <code> BatchWriterWrapper </code>
*
* This class directly writes to the HBase table instead of using any existing
* Writer API provided by HBase.
*
*/
public class HBaseWriter implements
Writer<RowMutations>
{
private final static Logger LOGGER = LoggerFactory.getLogger(HBaseWriter.class);
private final TableName tableName;
private final Admin admin;
private static final long SLEEP_INTERVAL_FOR_CF_VERIFY = 100L;
private final HashMap<String, Boolean> cfMap;
private BufferedMutator mutator;
private final boolean schemaUpdateEnabled;
public HBaseWriter(
final Admin admin,
final String tableName ) {
this.admin = admin;
this.tableName = TableName.valueOf(tableName);
cfMap = new HashMap<String, Boolean>();
schemaUpdateEnabled = admin.getConfiguration().getBoolean(
"hbase.online.schema.update.enable",
false);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Schema Update Enabled = " + schemaUpdateEnabled);
final String check = admin.getConfiguration().get(
"hbase.online.schema.update.enable");
if (check == null) {
LOGGER.debug("'hbase.online.schema.update.enable' property should be true for best performance");
}
}
}
public BufferedMutator getBufferedMutator()
throws IOException {
if (mutator == null) {
final BufferedMutatorParams params = new BufferedMutatorParams(
tableName);
params.listener(new ExceptionListener() {
@Override
public void onException(
final RetriesExhaustedWithDetailsException exception,
final BufferedMutator mutator )
throws RetriesExhaustedWithDetailsException {
LOGGER.error(
"Error in buffered mutator",
exception);
// Get details
for (Throwable cause : exception.getCauses()) {
cause.printStackTrace();
}
}
});
mutator = admin.getConnection().getBufferedMutator(
params);
}
return mutator;
}
@Override
public void write(
final RowMutations rowMutation ) {
try {
getBufferedMutator().mutate(
rowMutation.getMutations());
}
catch (final IOException e) {
LOGGER.error(
"Unable to write mutation.",
e);
}
}
@Override
public void write(
final Iterable<RowMutations> mutations ) {
for (final RowMutations rowMutation : mutations) {
write(rowMutation);
}
}
@Override
public void close() {
try {
if (mutator != null) {
mutator.close();
mutator = null;
}
}
catch (final IOException e) {
LOGGER.warn(
"Unable to close BufferedMutator",
e);
}
}
public void write(
final Iterable<RowMutations> iterable,
final String columnFamily )
throws IOException {
addColumnFamilyIfNotExist(columnFamily);
for (final RowMutations rowMutation : iterable) {
write(rowMutation);
}
}
public void write(
final List<Put> puts,
final String columnFamily )
throws IOException {
addColumnFamilyIfNotExist(columnFamily);
getBufferedMutator().mutate(
puts);
}
private void addColumnFamilyIfNotExist(
final String columnFamily )
throws IOException {
synchronized (BasicHBaseOperations.ADMIN_MUTEX) {
if (!columnFamilyExists(columnFamily)) {
addColumnFamilyToTable(
tableName,
columnFamily);
}
}
}
public void write(
final RowMutations mutation,
final String columnFamily ) {
try {
addColumnFamilyIfNotExist(columnFamily);
}
catch (final IOException e) {
LOGGER.warn(
"Unable to add column family " + columnFamily,
e);
}
write(mutation);
}
private boolean columnFamilyExists(
final String columnFamily ) {
Boolean found = false;
try {
found = cfMap.get(columnFamily);
if (found == null) {
found = Boolean.FALSE;
}
if (!found) {
if (!schemaUpdateEnabled && !admin.isTableEnabled(tableName)) {
admin.enableTable(tableName);
}
// update the table descriptor
HTableDescriptor tableDescriptor = admin.getTableDescriptor(tableName);
found = tableDescriptor.hasFamily(columnFamily.getBytes(StringUtils.UTF8_CHAR_SET));
cfMap.put(
columnFamily,
found);
}
}
catch (final IOException e) {
LOGGER.warn(
"Unable to check existence of column family " + columnFamily,
e);
}
return found;
}
private void addColumnFamilyToTable(
final TableName tableName,
final String columnFamilyName )
throws IOException {
LOGGER.debug("Creating column family: " + columnFamilyName);
final HColumnDescriptor cfDescriptor = new HColumnDescriptor(
columnFamilyName);
if (!schemaUpdateEnabled && !admin.isTableDisabled(tableName)) {
admin.disableTable(tableName);
}
// Try adding column family to the table descriptor instead
admin.addColumn(
tableName,
cfDescriptor);
if (schemaUpdateEnabled) {
do {
try {
Thread.sleep(SLEEP_INTERVAL_FOR_CF_VERIFY);
}
catch (final InterruptedException e) {
LOGGER.warn(
"Sleeping while column family added interrupted",
e);
}
}
while (admin.getAlterStatus(
tableName).getFirst() > 0);
}
cfMap.put(
columnFamilyName,
Boolean.TRUE);
// Enable table once done
if (!schemaUpdateEnabled) {
admin.enableTable(tableName);
}
}
public void delete(
final Iterable<RowMutations> iterable )
throws IOException {
for (final RowMutations rowMutation : iterable) {
write(rowMutation);
}
}
public void delete(
final Delete delete )
throws IOException {
getBufferedMutator().mutate(
delete);
}
public void delete(
final List<Delete> deletes )
throws IOException {
getBufferedMutator().mutate(
deletes);
}
@Override
public void flush() {
try {
if (mutator != null) {
mutator.flush();
}
}
catch (final IOException e) {
LOGGER.warn(
"Unable to flush BufferedMutator",
e);
}
}
}