package meetup.beeno.util; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Set; import meetup.beeno.mapping.EntityInfo; import meetup.beeno.mapping.FieldMapping; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.HTablePool; import org.apache.hadoop.hbase.util.Bytes; import org.apache.log4j.Logger; /** * Basic utilities to enable easier interaction with HBase. */ public class HUtil { // max number of digits in a long value private static final int LONG_CHAR_LENGTH = 19; private static byte[] ZERO_FILL = new byte[LONG_CHAR_LENGTH]; static { Arrays.fill(ZERO_FILL, (byte)'0'); } // FIXME: this shouldn't be static and should be configurable private static final int MAX_POOL_SIZE = 100; private static Logger log = Logger.getLogger(HUtil.class.getName()); private static HTablePool pool = new HTablePool(new HBaseConfiguration(), MAX_POOL_SIZE); public static HTablePool getPool() { return pool; } public static void setPool(HTablePool newPool) { pool = newPool; } public static HTable getTable(String tablename) { if (log.isDebugEnabled()) log.debug("Getting table "+tablename+" from pool"); return pool.getTable(tablename); } public static void releaseTable(HTable table) { if (table != null) { if (log.isDebugEnabled()) log.debug("Returning table "+Bytes.toString(table.getTableName())+" to pool"); pool.putTable(table); } } /** * Returns the column name portion of a HBase column description * in the form "family:column" * * @param fullColumn * @return */ public static String column(String fullColumn) { int sepIdx = fullColumn.indexOf(":"); if (sepIdx >= 0 && sepIdx < (fullColumn.length()-1)) return fullColumn.substring(sepIdx+1); return fullColumn; } /** * Returns the column family portion of a HBase column description * in the form "family:column" * * @param fullColumn * @return */ public static String family(String fullColumn) { int sepIdx = fullColumn.indexOf(":"); if (sepIdx > 0) return fullColumn.substring(0, sepIdx); return null; } /** * Attempts to convert the byte array value to the specified type */ public static Object convertValue(byte[] val, Class propType) { if (val == null && propType == null) { return null; } if (propType == Integer.TYPE || propType == Integer.class) { return Bytes.toInt(val); } else if (propType == Float.TYPE || propType == Float.class) { return Bytes.toFloat(val); } else if (propType == Double.TYPE || propType == Double.class) { return Bytes.toDouble(val); } else if (propType == Long.TYPE || propType == Long.class) { return Bytes.toLong(val); } else if (propType == String.class) { return Bytes.toString(val); } else if (propType == Date.class) { // FIXME: no timezone handling return new Date(Bytes.toLong(val)); } else if (Enum.class.isAssignableFrom(propType)) { String enumName = null; try { enumName = Bytes.toString(val); if (enumName == null) return null; return Enum.valueOf(propType, enumName.toUpperCase()); } catch (IllegalArgumentException iae) { log.error(String.format("Invalid enum constant '%s' for class %s", enumName, propType.getName())); return null; } } else { // not handled log.warn(String.format("Unknown conversion for property type %s", propType.getName())); return val; } } /** * Attempts to convert the value object to a byte array for storage * @param val * @return */ public static byte[] convertToBytes(Object val) { if (val == null) { return null; } if (val.getClass().isArray() && val.getClass().getComponentType() == Byte.TYPE) { return (byte[])val; } else if (val instanceof Integer) { return Bytes.toBytes( ((Integer)val) ); } else if (val instanceof Float) { return Bytes.toBytes(((Float)val)); } else if (val instanceof Double) { return Bytes.toBytes(((Double)val)); } else if (val instanceof Long) { return Bytes.toBytes(((Long)val)); } else if (val instanceof String) { return Bytes.toBytes(((String)val)); } else if (val instanceof Date) { // FIXME: no timezone handling return Bytes.toBytes(((Date)val).getTime()); } else if (val instanceof Enum) { return Bytes.toBytes(((Enum)val).name().toLowerCase()); } else { // not handled log.warn(String.format("Unknown conversion to bytes for property value type %s", val.getClass().getName())); return null; } } public static String[] getMappedFamilies(EntityInfo info) { Set<String> families = new HashSet<String>(); for (FieldMapping field : info.getMappedFields()) { families.add(field.getFamily()+":"); } return families.toArray(new String[0]); } public static byte[] toOrderedBytes(Long ts) { return toOrderedBytes(ts, false); } public static byte[] toOrderedBytes(Long ts, boolean invert) { long idxval = (invert ? (Long.MAX_VALUE - ts) : ts); byte[] valbytes = Bytes.toBytes( Long.toString(idxval) ); byte[] paddedbytes = new byte[LONG_CHAR_LENGTH]; int padchars = LONG_CHAR_LENGTH - valbytes.length; if (padchars > 0) { System.arraycopy(ZERO_FILL, 0, paddedbytes, 0, padchars); } System.arraycopy(valbytes, 0, paddedbytes, padchars, valbytes.length); return paddedbytes; } public static Object cast(Object val, Class type) throws ClassCastException { if (val == null) return null; if (type == Boolean.class || type == Boolean.TYPE) { return ((Boolean)val).booleanValue(); } else if (type == Character.class || type == Character.TYPE) { return ((Character)val).charValue(); } else if (type == Byte.class || type == Byte.TYPE) { return ((Byte)val).byteValue(); } else if (type == Short.class || type == Short.TYPE) { if (val instanceof Long) return ((Long)val).shortValue(); else if (val instanceof Integer) return ((Integer)val).shortValue(); else if (val instanceof Short) return ((Short)val).shortValue(); } else if (type == Integer.class || type == Integer.TYPE) { if (val instanceof Long) return ((Long)val).intValue(); else if (val instanceof Integer) return ((Integer)val).intValue(); } else if (type == Long.class || type == Long.TYPE) { if (val instanceof Integer) return ((Integer)val).longValue(); else if (val instanceof Long) return ((Long)val).longValue(); } else if (type == Float.class || type == Float.TYPE) { if (val instanceof Double) return ((Double)val).floatValue(); else if (val instanceof Float) return ((Float)val).floatValue(); } else if (type == Double.class || type == Double.TYPE) { if (val instanceof Double) return ((Double)val).doubleValue(); else if (val instanceof Float) return ((Float)val).doubleValue(); } else if (type.isAssignableFrom(val.getClass())) { return type.cast(val); } return null; } public static class HCol { private byte[] family; private byte[] column; public HCol(String familyName, String columnName) { this(Bytes.toBytes(familyName), Bytes.toBytes(columnName)); } public HCol(byte[] familyBytes, byte[] columnBytes) { this.family = familyBytes; this.column = columnBytes; } public byte[] family() { return this.family; } public byte[] column() { return this.column; } public static HCol parse(String fullName) { byte[][] parts = KeyValue.parseColumn(Bytes.toBytes(fullName)); if (parts == null || parts.length != 2) return null; return new HCol(parts[0], parts[1]); } } }