RHHBaseRecorder.java Source code

Java tutorial

Introduction

Here is the source code for RHHBaseRecorder.java

Source

/**
 * Copyright 2010 Mozilla Foundation
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Description: Creating a custom Input Format, by help of RecordReader. Detail can be found in below links
 * https://www.inkling.com/read/hadoop-definitive-guide-tom-white-3rd/chapter-7/input-formats
 * http://hadoopi.wordpress.com/2013/05/27/understand-recordreader-inputsplit/
 * 
 * Implemented using RecordReader concepts.
 */
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configurable;
import org.apache.hadoop.conf.Configuration;
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.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.mapreduce.TableSplit;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.JobContext;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.util.StringUtils;
import org.godhuli.rhipe.RHRaw;

public class RHHBaseRecorder extends org.apache.hadoop.mapreduce.InputFormat<RHRaw, RHResult>
        implements Configurable {

    private final static Log LOG = LogFactory.getLog(RHHBaseRecorder.class);
    public static boolean ValueIsString = false;
    public static boolean SingleCFQ = false;
    public static byte[][][] CFQ;
    /** Job parameter that specifies the input table. */
    public static final String INPUT_TABLE = "rhipe.hbase.tablename";

    /** Job parameter that specifies the filtering parameter for table. */
    public static final String Filter = "rhipe.hbase.filter";

    /************SET BATCH for better performance****************/
    public static final String batch = "rhipe.hbase.set.batch";

    /************SET BATCH for better performance****************/
    public static final String sizecal = "rhipe.hbase.set.size";

    /** Base-64 encoded array of scanners.    */
    public static final String RHIPE_COLSPEC = "rhipe.hbase.colspec";

    private Configuration conf = null;
    private HTable table = null;
    private Scan[] scans = null;
    private TableRecordReader trr = null;

    /* (non-Javadoc)
     * @see org.apache.hadoop.mapreduce.InputFormat#createRecordReader(org.apache.hadoop.mapreduce.InputSplit, org.apache.hadoop.mapreduce.TaskAttemptContext)
     *   http://hadoop.apache.org/docs/current2/api/org/apache/hadoop/mapreduce/InputFormat.html
     *
     * RHRAW and RHRESULT is immutable raw bytes format used in RHIPE. For more details see RHIPE.
     */
    @Override
    public RecordReader<RHRaw, RHResult> createRecordReader(InputSplit split, TaskAttemptContext context)
            throws IOException, InterruptedException {
        if (scans == null) {
            throw new IOException("No scans were provided");
        }
        if (table == null) {
            throw new IOException("No table was provided.");
        }
        if (trr == null) {
            trr = new TableRecordReader();
        }

        TableSplit tSplit = (TableSplit) split;
        LOG.info("Split in Record Reader  " + tSplit);
        Scan scan = new Scan(scans[0]);
        scan.setStartRow(tSplit.getStartRow());
        scan.setStopRow(tSplit.getEndRow());
        //LOG.info("Table in Record Reader  " + Bytes.toStringBinary(tSplit.getTableName()));
        trr.setScan(scan);
        trr.setHTable(table);
        trr.init();
        return trr;
    }

    /* (non-Javadoc)
     * @see org.apache.hadoop.mapreduce.InputFormat#getSplits(org.apache.hadoop.mapreduce.JobContext)
     * http://hadoop.apache.org/docs/current2/api/org/apache/hadoop/mapreduce/InputFormat.html
     * 
     * Get Number of splits between the start and end rowkeys.
     */
    @Override
    public List<InputSplit> getSplits(JobContext context) throws IOException, InterruptedException {
        if (table == null) {
            throw new IOException("No table was provided.");
        }

        Pair<byte[][], byte[][]> keys = table.getStartEndKeys();
        if (keys == null || keys.getFirst() == null || keys.getFirst().length == 0) {
            throw new IOException("Expecting at least one region.");
        }

        Set<InputSplit> splits = new HashSet<InputSplit>();
        for (int i = 0; i < keys.getFirst().length; i++) {
            String regionLocation = table.getRegionLocation(keys.getFirst()[i]).getServerAddress().getHostname();
            //LOG.info("Split length  " + keys.getFirst().length);
            for (Scan s : scans) {
                byte[] startRow = s.getStartRow();
                byte[] stopRow = s.getStopRow();
                // determine if the given start an stop key fall into the region
                if ((startRow.length == 0 || keys.getSecond()[i].length == 0
                        || Bytes.compareTo(startRow, keys.getSecond()[i]) < 0)
                        && (stopRow.length == 0 || Bytes.compareTo(stopRow, keys.getFirst()[i]) > 0)) {
                    byte[] splitStart = startRow.length == 0 || Bytes.compareTo(keys.getFirst()[i], startRow) >= 0
                            ? keys.getFirst()[i]
                            : startRow;
                    byte[] splitStop = (stopRow.length == 0 || Bytes.compareTo(keys.getSecond()[i], stopRow) <= 0)
                            && keys.getSecond()[i].length > 0 ? keys.getSecond()[i] : stopRow;
                    //LOG.info("Start Split now " +  Bytes.toStringBinary(splitStart));   
                    //LOG.info("Start stop now " +   Bytes.toStringBinary(splitStop));
                    //System.out.println("\n================" + Bytes.toLong(splitStart,0));
                    //System.out.println("\n================" + splitStart.length);
                    //LOG.info("  Start row-***-   " +Bytes.toLong(startRow,0));
                    //LOG.info("  End row-***-   " +Bytes.toLong(stopRow,0));                        
                    InputSplit split = new TableSplit(table.getTableName(), splitStart, splitStop, regionLocation);
                    LOG.info("the current regionInfo's startKey is :" + Bytes.toStringBinary(splitStart)
                            + "  , the current regionInfo's endkey is : " + Bytes.toStringBinary(splitStop)
                            + "  , the current regionInfo's table is " + Bytes.toStringBinary(table.getTableName())
                            + "  , the current regionInfo's regionLocation is :" + regionLocation);
                    //LOG.info("Table Name  " + table.getTableName());
                    //LOG.info("Split server =>" + "  " + split);
                    splits.add(split);
                }
            }
        }

        return new ArrayList<InputSplit>(splits);
    }

    /* (non-Javadoc)
     * @see org.apache.hadoop.conf.Configurable#getConf()
     */
    @Override
    public Configuration getConf() {
        return conf;
    }

    /* (non-Javadoc)
     * @see org.apache.hadoop.conf.Configurable#setConf(org.apache.hadoop.conf.Configuration)
     */
    @Override
    public void setConf(Configuration conf) {
        this.conf = conf;
        RHHBaseRecorder.ValueIsString = conf.get("rhipe_hbase_values_are_string") != null
                && conf.get("rhipe_hbase_values_are_string").equals("TRUE");
        RHHBaseRecorder.SingleCFQ = conf.get("rhipe.hbase.single.cfq") != null
                && conf.get("rhipe.hbase.single.cfq").equals("TRUE");
        String tableName = conf.get(INPUT_TABLE);
        try {
            setHTable(new HTable(HBaseConfiguration.create(conf), tableName));
        } catch (Exception e) {
            LOG.error(StringUtils.stringifyException(e));
        }
        Scan[] scans = null;
        if (conf.get(RHIPE_COLSPEC) != null) {
            try {
                String[] cols = conf.get(RHIPE_COLSPEC).split(",");
                ArrayList<Pair<String, String>> l = null;
                if (cols.length > 0) {
                    l = new ArrayList<Pair<String, String>>(cols.length);
                    for (int i = 0; i < cols.length; i++) {
                        String[] x = cols[i].split(":");
                        if (x.length == 1) {
                            l.add(new Pair<String, String>(x[0], null));
                            LOG.info("Added family: " + x[0]);
                        } else {
                            l.add(new Pair<String, String>(x[0], x[1]));
                            LOG.info("Added " + x[0] + ":" + x[1]);
                        }
                    }
                }
                String[] x = conf.get("rhipe.hbase.mozilla.cacheblocks").split(":");
                scans = Fun.generateScans(conf.get("rhipe.hbase.rowlim.start"), conf.get("rhipe.hbase.rowlim.end"),
                        l, Integer.parseInt(x[0]), Integer.parseInt(x[1]) == 1 ? true : false);
            } catch (Exception e) {
                LOG.error("An error occurred.", e);
            }
        } else {
            //Scan[] scans = null;
            scans = new Scan[] { new Scan() };
            LOG.info("Start Row Key" + Bytes.toStringBinary(
                    org.apache.commons.codec.binary.Base64.decodeBase64(conf.get("rhipe.hbase.rowlim.start"))));
            LOG.info("End Row Key" + Bytes.toStringBinary(
                    org.apache.commons.codec.binary.Base64.decodeBase64(conf.get("rhipe.hbase.rowlim.end"))));
            //LOG.info("Filter in   " + Bytes.toStringBinary(org.apache.commons.codec.binary.Base64.decodeBase64(conf.get("rhipe.hbase.filter"))));
            //LOG.info("Filter out  " + conf.get("rhipe.hbase.filter"));
            String[] x = conf.get("rhipe.hbase.mozilla.cacheblocks").split(":");
            LOG.info("cache " + Integer.parseInt(x[0]) + " block " + Integer.parseInt(x[1]));
            scans = Fun.generateScansRows(conf.get("rhipe.hbase.rowlim.start"), conf.get("rhipe.hbase.rowlim.end"),
                    Integer.parseInt(x[0]), Integer.parseInt(x[1]) == 1 ? true : false,
                    conf.get("rhipe.hbase.filter"), Integer.parseInt(conf.get("rhipe.hbase.set.batch")));
            //scans = getAllColumnQualifier(table);            
        }
        setScans(scans);
    }

    public static int getSizeofFile(String st, String en, int caching, boolean cacheBlocks, String filter,
            int batch) throws IOException {
        Scan s = new Scan();
        s.setCacheBlocks(false); // don't set to true for MR jobs
        LOG.info(" Calculation of File size in hbase at client side. ------   ");
        if (st != null) {
            byte[] stb1 = org.apache.commons.codec.binary.Base64.decodeBase64(st);
            //LOG.info("  Start row in ------   " +Bytes.toStringBinary(stb1));
            s.setStartRow(stb1);
        }
        if (en != null) {
            byte[] enb2 = org.apache.commons.codec.binary.Base64.decodeBase64(en);
            //LOG.info("  End row in------   " +Bytes.toStringBinary(enb2));
            s.setStopRow(enb2);
        }
        //LOG.info("  Filter- -----   " +  filter);
        RowFilter rowFilterRegex = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator(
                Bytes.toString(org.apache.commons.codec.binary.Base64.decodeBase64(filter))));
        s.setFilter(rowFilterRegex);
        HTable tt = new HTable(HBaseConfiguration.create(), "tsdb");
        ResultScanner ss = tt.getScanner(s);
        int col = 0;
        int size = 0;
        for (Result r : ss) {
            col = 0;
            for (KeyValue kv : r.raw()) {
                col = col + kv.getLength();
                //System.out.print("\n Length keyValue " +kv.getLength() + "\n");    
            }
            size = size + col / 1000;
        }
        System.out.print("\n Size " + size + "\n");
        return size;
    }

    public static Scan[] getAllColumnQualifier(HTable table) {
        ArrayList<Scan> scans = new ArrayList<Scan>();
        Scan scans2 = new Scan();
        try {
            int count = 0;

            ResultScanner ss = table.getScanner(scans2);
            for (Result r : ss) {
                for (KeyValue kv : r.raw()) {
                    //System.out.print("\n Rowkey " +org.apache.commons.codec.binary.Base64.encodeBase64String(kv.getRow())  ); 
                    // System.out.print(new String(kv.getFamily()) + ":");
                    //System.out.print(org.apache.commons.codec.binary.Base64.encodeBase64String(kv.getQualifier() )+ " ");   
                    //s.addFamily(kv.getFamily());
                    scans2.addColumn(kv.getFamily(), kv.getQualifier());
                }
            }
            //return s;
        } catch (IOException e) {
            e.printStackTrace();
        }
        scans.add(scans2);
        return scans.toArray(new Scan[scans.size()]);
    }

    /**
     * Allows subclasses to get the {@link HTable}.
     * Get HBase Table
     */
    protected HTable getHTable() {
        return this.table;
    }

    /**
     * Allows subclasses to set the {@link HTable}.
     * 
     * @param table
     *  The table to get the data from. Set Hbase table For our case it is normally tsdb.
     */
    protected void setHTable(HTable table) {
        this.table = table;
    }

    /**
     * @return
     */
    public Scan[] getScans() {
        return scans;
    }

    /**
     * @param scans The scans to use as boundaries.
     */
    public void setScans(Scan[] scans) {
        this.scans = scans;
    }

    /**
     * Iterate over an HBase table data, return (ImmutableBytesWritable, Result)
     * pairs.
     */
    protected class TableRecordReader extends RecordReader<RHRaw, RHResult> {

        private ResultScanner scanner = null;
        private Scan scan = null;
        private HTable htable = null;
        private byte[] lastRow = null;
        private RHRaw key = null;
        private RHResult value = null;
        private Result oldresult = null;

        public void restart(byte[] firstRow) throws IOException {
            Scan newScan = new Scan(scan);
            newScan.setStartRow(firstRow);
            this.scanner = this.htable.getScanner(newScan);
        }

        public void init() throws IOException {
            restart(scan.getStartRow());
        }

        public void setHTable(HTable htable) {
            this.htable = htable;
        }

        public void setScan(Scan scan) {
            this.scan = scan;
        }

        public void close() {
            this.scanner.close();
        }

        public RHRaw getCurrentKey() throws IOException, InterruptedException {
            return key;
        }

        public RHResult getCurrentValue() throws IOException, InterruptedException {
            return value;
        }

        public void initialize(InputSplit inputsplit, TaskAttemptContext context)
                throws IOException, InterruptedException {
        }

        public boolean nextKeyValue() throws IOException, InterruptedException {
            if (key == null)
                key = new RHRaw();
            if (value == null) {
                value = new RHResult();
                oldresult = new Result();
            }
            try {
                oldresult = this.scanner.next();
                if (oldresult != null) {
                    value.set(oldresult);
                }
            } catch (IOException e) {
                //LOG.debug("recovered from " + StringUtils.stringifyException(e));
                restart(lastRow);
                scanner.next(); // skip presumed already mapped row
                oldresult = scanner.next();
                value.set(oldresult);
            }
            if (oldresult != null && oldresult.size() > 0) {
                byte[] _b = oldresult.getRow();
                key.set(_b);
                lastRow = _b;
                return true;
            }
            return false;
        }

        public float getProgress() {
            return 0;
        }
    }
}