package backtype.storm.contrib.hbase.trident; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.util.Bytes; import storm.trident.state.OpaqueValue; import storm.trident.state.Serializer; import storm.trident.state.StateFactory; import storm.trident.state.StateType; import storm.trident.state.TransactionalValue; import storm.trident.state.map.IBackingMap; import backtype.storm.contrib.hbase.utils.HTableConnector; import backtype.storm.contrib.hbase.utils.TridentConfig; /** * A HBase persistentAggregate source of state for Storm Trident topologies * @param <T> The type of value being persisted. Either {@link OpaqueValue} or * {@link TransactionalValue} */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class HBaseAggregateState<T> implements IBackingMap<T> { /** * @param config The {@link TridentConfig} * @return {@link StateFactory} for opaque transactional topologies */ public static StateFactory opaque(TridentConfig<OpaqueValue> config) { return new HBaseAggregateFactory(config, StateType.OPAQUE); } /** * @param config The {@link TridentConfig} * @return {@link StateFactory} for transactional topologies */ public static StateFactory transactional(TridentConfig<TransactionalValue> config) { return new HBaseAggregateFactory(config, StateType.TRANSACTIONAL); } /** * @param config The {@link TridentConfig} * @return {@link StateFactory} for non-transactional topologies */ public static StateFactory nonTransactional(TridentConfig<TransactionalValue> config) { return new HBaseAggregateFactory(config, StateType.NON_TRANSACTIONAL); } private HTableConnector connector; private Serializer serializer; public HBaseAggregateState(TridentConfig config) { this.serializer = config.getStateSerializer(); try { this.connector = new HTableConnector(config); } catch (IOException e) { throw new RuntimeException(e); } } /** {@inheritDoc} */ @Override public List<T> multiGet(List<List<Object>> keys) { List<Get> gets = new ArrayList<Get>(keys.size()); byte[] rk; byte[] cf; byte[] cq; for (List<Object> k : keys) { rk = Bytes.toBytes((String) k.get(0)); cf = Bytes.toBytes((String) k.get(1)); cq = Bytes.toBytes((String) k.get(2)); Get g = new Get(rk); gets.add(g.addColumn(cf, cq)); } // Log.debug("GETS: " + gets.toString()); Result[] results = null; try { results = connector.getTable().get(gets); } catch (IOException e) { new RuntimeException(e); } List<T> rtn = new ArrayList<T>(keys.size()); for (int i = 0; i < keys.size(); i++) { cf = Bytes.toBytes((String) keys.get(i).get(1)); cq = Bytes.toBytes((String) keys.get(i).get(2)); Result r = results[i]; if (r.isEmpty()) { rtn.add(null); } else { rtn.add((T) serializer.deserialize(r.getValue(cf, cq))); } } return rtn; } /** {@inheritDoc} */ @Override public void multiPut(List<List<Object>> keys, List<T> vals) { List<Put> puts = new ArrayList<Put>(); for (int i = 0; i < keys.size(); i++) { byte[] rk = Bytes.toBytes((String) keys.get(i).get(0)); byte[] cf = Bytes.toBytes((String) keys.get(i).get(1)); byte[] cq = Bytes.toBytes((String) keys.get(i).get(2)); byte[] cv = serializer.serialize(vals.get(i)); Put p = new Put(rk); puts.add(p.add(cf, cq, cv)); } // Log.debug("PUTS: " + puts.toString()); try { connector.getTable().put(puts); connector.getTable().flushCommits(); } catch (IOException e) { new RuntimeException(e); } } }