001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.mapreduce;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import java.io.ByteArrayOutputStream;
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.PrintStream;
028import org.apache.commons.io.IOUtils;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.fs.FileUtil;
031import org.apache.hadoop.fs.LocalFileSystem;
032import org.apache.hadoop.fs.Path;
033import org.apache.hadoop.hbase.HBaseClassTestRule;
034import org.apache.hadoop.hbase.HBaseConfiguration;
035import org.apache.hadoop.hbase.HBaseTestingUtility;
036import org.apache.hadoop.hbase.TableName;
037import org.apache.hadoop.hbase.client.Put;
038import org.apache.hadoop.hbase.client.Table;
039import org.apache.hadoop.hbase.testclassification.LargeTests;
040import org.apache.hadoop.hbase.testclassification.MapReduceTests;
041import org.apache.hadoop.hbase.util.Bytes;
042import org.apache.hadoop.hbase.util.LauncherSecurityManager;
043import org.apache.hadoop.util.ToolRunner;
044import org.junit.AfterClass;
045import org.junit.BeforeClass;
046import org.junit.ClassRule;
047import org.junit.Rule;
048import org.junit.Test;
049import org.junit.experimental.categories.Category;
050import org.junit.rules.TestName;
051
052@Category({MapReduceTests.class, LargeTests.class})
053public class TestCellCounter {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057      HBaseClassTestRule.forClass(TestCellCounter.class);
058
059  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
060  private static final byte[] ROW1 = Bytes.toBytesBinary("\\x01row1");
061  private static final byte[] ROW2 = Bytes.toBytesBinary("\\x01row2");
062  private static final String FAMILY_A_STRING = "a";
063  private static final String FAMILY_B_STRING = "b";
064  private static final byte[] FAMILY_A = Bytes.toBytes(FAMILY_A_STRING);
065  private static final byte[] FAMILY_B = Bytes.toBytes(FAMILY_B_STRING);
066  private static final byte[] QUALIFIER = Bytes.toBytes("q");
067
068  private static Path FQ_OUTPUT_DIR;
069  private static final String OUTPUT_DIR = "target" + File.separator + "test-data" + File.separator
070      + "output";
071  private static long now = System.currentTimeMillis();
072
073  @Rule
074  public TestName name = new TestName();
075
076  @BeforeClass
077  public static void beforeClass() throws Exception {
078    UTIL.startMiniCluster();
079    FQ_OUTPUT_DIR = new Path(OUTPUT_DIR).makeQualified(new LocalFileSystem());
080    FileUtil.fullyDelete(new File(OUTPUT_DIR));
081  }
082
083  @AfterClass
084  public static void afterClass() throws Exception {
085    UTIL.shutdownMiniCluster();
086  }
087
088  /**
089   * Test CellCounter all data should print to output
090   *
091   */
092  @Test
093  public void testCellCounter() throws Exception {
094    final TableName sourceTable = TableName.valueOf(name.getMethodName());
095    byte[][] families = { FAMILY_A, FAMILY_B };
096    Table t = UTIL.createTable(sourceTable, families);
097    try{
098      Put p = new Put(ROW1);
099      p.addColumn(FAMILY_A, QUALIFIER, now, Bytes.toBytes("Data11"));
100      p.addColumn(FAMILY_B, QUALIFIER, now + 1, Bytes.toBytes("Data12"));
101      p.addColumn(FAMILY_A, QUALIFIER, now + 2, Bytes.toBytes("Data13"));
102      t.put(p);
103      p = new Put(ROW2);
104      p.addColumn(FAMILY_B, QUALIFIER, now, Bytes.toBytes("Dat21"));
105      p.addColumn(FAMILY_A, QUALIFIER, now + 1, Bytes.toBytes("Data22"));
106      p.addColumn(FAMILY_B, QUALIFIER, now + 2, Bytes.toBytes("Data23"));
107      t.put(p);
108      String[] args = { sourceTable.getNameAsString(), FQ_OUTPUT_DIR.toString(), ";", "^row1" };
109      runCount(args);
110      FileInputStream inputStream = new FileInputStream(OUTPUT_DIR + File.separator +
111          "part-r-00000");
112      String data = IOUtils.toString(inputStream);
113      inputStream.close();
114      assertTrue(data.contains("Total Families Across all Rows" + "\t" + "2"));
115      assertTrue(data.contains("Total Qualifiers across all Rows" + "\t" + "2"));
116      assertTrue(data.contains("Total ROWS" + "\t" + "1"));
117      assertTrue(data.contains("b;q" + "\t" + "1"));
118      assertTrue(data.contains("a;q" + "\t" + "1"));
119      assertTrue(data.contains("row1;a;q_Versions" + "\t" + "1"));
120      assertTrue(data.contains("row1;b;q_Versions" + "\t" + "1"));
121    }finally{
122      t.close();
123      FileUtil.fullyDelete(new File(OUTPUT_DIR));
124    }
125  }
126
127  /**
128   * Test CellCounter all data should print to output
129   */
130  @Test
131  public void testCellCounterPrefix() throws Exception {
132    final TableName sourceTable = TableName.valueOf(name.getMethodName());
133    byte[][] families = { FAMILY_A, FAMILY_B };
134    Table t = UTIL.createTable(sourceTable, families);
135    try {
136      Put p = new Put(ROW1);
137      p.addColumn(FAMILY_A, QUALIFIER, now, Bytes.toBytes("Data11"));
138      p.addColumn(FAMILY_B, QUALIFIER, now + 1, Bytes.toBytes("Data12"));
139      p.addColumn(FAMILY_A, QUALIFIER, now + 2, Bytes.toBytes("Data13"));
140      t.put(p);
141      p = new Put(ROW2);
142      p.addColumn(FAMILY_B, QUALIFIER, now, Bytes.toBytes("Dat21"));
143      p.addColumn(FAMILY_A, QUALIFIER, now + 1, Bytes.toBytes("Data22"));
144      p.addColumn(FAMILY_B, QUALIFIER, now + 2, Bytes.toBytes("Data23"));
145      t.put(p);
146      String[] args = { sourceTable.getNameAsString(), FQ_OUTPUT_DIR.toString(), ";", "\\x01row1" };
147      runCount(args);
148      FileInputStream inputStream =
149          new FileInputStream(OUTPUT_DIR + File.separator + "part-r-00000");
150      String data = IOUtils.toString(inputStream);
151      inputStream.close();
152      assertTrue(data.contains("Total Families Across all Rows" + "\t" + "2"));
153      assertTrue(data.contains("Total Qualifiers across all Rows" + "\t" + "2"));
154      assertTrue(data.contains("Total ROWS" + "\t" + "1"));
155      assertTrue(data.contains("b;q" + "\t" + "1"));
156      assertTrue(data.contains("a;q" + "\t" + "1"));
157      assertTrue(data.contains("row1;a;q_Versions" + "\t" + "1"));
158      assertTrue(data.contains("row1;b;q_Versions" + "\t" + "1"));
159    } finally {
160      t.close();
161      FileUtil.fullyDelete(new File(OUTPUT_DIR));
162    }
163  }
164
165  /**
166   * Test CellCounter with time range all data should print to output
167   */
168  @Test
169  public void testCellCounterStartTimeRange() throws Exception {
170    final TableName sourceTable = TableName.valueOf(name.getMethodName());
171    byte[][] families = { FAMILY_A, FAMILY_B };
172    Table t = UTIL.createTable(sourceTable, families);
173    try{
174      Put p = new Put(ROW1);
175      p.addColumn(FAMILY_A, QUALIFIER, now, Bytes.toBytes("Data11"));
176      p.addColumn(FAMILY_B, QUALIFIER, now + 1, Bytes.toBytes("Data12"));
177      p.addColumn(FAMILY_A, QUALIFIER, now + 2, Bytes.toBytes("Data13"));
178      t.put(p);
179      p = new Put(ROW2);
180      p.addColumn(FAMILY_B, QUALIFIER, now, Bytes.toBytes("Dat21"));
181      p.addColumn(FAMILY_A, QUALIFIER, now + 1, Bytes.toBytes("Data22"));
182      p.addColumn(FAMILY_B, QUALIFIER, now + 2, Bytes.toBytes("Data23"));
183      t.put(p);
184      String[] args = {
185          sourceTable.getNameAsString(), FQ_OUTPUT_DIR.toString(),  ";", "^row1",
186          "--starttime=" + now,
187          "--endtime=" + now + 2 };
188      runCount(args);
189      FileInputStream inputStream = new FileInputStream(OUTPUT_DIR + File.separator +
190          "part-r-00000");
191      String data = IOUtils.toString(inputStream);
192      inputStream.close();
193      assertTrue(data.contains("Total Families Across all Rows" + "\t" + "2"));
194      assertTrue(data.contains("Total Qualifiers across all Rows" + "\t" + "2"));
195      assertTrue(data.contains("Total ROWS" + "\t" + "1"));
196      assertTrue(data.contains("b;q" + "\t" + "1"));
197      assertTrue(data.contains("a;q" + "\t" + "1"));
198      assertTrue(data.contains("row1;a;q_Versions" + "\t" + "1"));
199      assertTrue(data.contains("row1;b;q_Versions" + "\t" + "1"));
200    }finally{
201      t.close();
202      FileUtil.fullyDelete(new File(OUTPUT_DIR));
203    }
204  }
205
206  /**
207   * Test CellCounter with time range all data should print to output
208   */
209  @Test
210  public void testCellCounteEndTimeRange() throws Exception {
211    final TableName sourceTable = TableName.valueOf(name.getMethodName());
212    byte[][] families = { FAMILY_A, FAMILY_B };
213    Table t = UTIL.createTable(sourceTable, families);
214    try{
215      Put p = new Put(ROW1);
216      p.addColumn(FAMILY_A, QUALIFIER, now, Bytes.toBytes("Data11"));
217      p.addColumn(FAMILY_B, QUALIFIER, now + 1, Bytes.toBytes("Data12"));
218      p.addColumn(FAMILY_A, QUALIFIER, now + 2, Bytes.toBytes("Data13"));
219      t.put(p);
220      p = new Put(ROW2);
221      p.addColumn(FAMILY_B, QUALIFIER, now, Bytes.toBytes("Dat21"));
222      p.addColumn(FAMILY_A, QUALIFIER, now + 1, Bytes.toBytes("Data22"));
223      p.addColumn(FAMILY_B, QUALIFIER, now + 2, Bytes.toBytes("Data23"));
224      t.put(p);
225      String[] args = {
226          sourceTable.getNameAsString(), FQ_OUTPUT_DIR.toString(),  ";", "^row1",
227          "--endtime=" + now + 1 };
228      runCount(args);
229      FileInputStream inputStream = new FileInputStream(OUTPUT_DIR + File.separator +
230          "part-r-00000");
231      String data = IOUtils.toString(inputStream);
232      inputStream.close();
233      assertTrue(data.contains("Total Families Across all Rows" + "\t" + "2"));
234      assertTrue(data.contains("Total Qualifiers across all Rows" + "\t" + "2"));
235      assertTrue(data.contains("Total ROWS" + "\t" + "1"));
236      assertTrue(data.contains("b;q" + "\t" + "1"));
237      assertTrue(data.contains("a;q" + "\t" + "1"));
238      assertTrue(data.contains("row1;a;q_Versions" + "\t" + "1"));
239      assertTrue(data.contains("row1;b;q_Versions" + "\t" + "1"));
240    }finally{
241      t.close();
242      FileUtil.fullyDelete(new File(OUTPUT_DIR));
243    }
244  }
245
246  /**
247   * Test CellCounter with time range all data should print to output
248   */
249  @Test
250  public void testCellCounteOutOfTimeRange() throws Exception {
251    final TableName sourceTable = TableName.valueOf(name.getMethodName());
252    byte[][] families = { FAMILY_A, FAMILY_B };
253    Table t = UTIL.createTable(sourceTable, families);
254    try{
255      Put p = new Put(ROW1);
256      p.addColumn(FAMILY_A, QUALIFIER, now, Bytes.toBytes("Data11"));
257      p.addColumn(FAMILY_B, QUALIFIER, now + 1, Bytes.toBytes("Data12"));
258      p.addColumn(FAMILY_A, QUALIFIER, now + 2, Bytes.toBytes("Data13"));
259      t.put(p);
260      p = new Put(ROW2);
261      p.addColumn(FAMILY_B, QUALIFIER, now, Bytes.toBytes("Dat21"));
262      p.addColumn(FAMILY_A, QUALIFIER, now + 1, Bytes.toBytes("Data22"));
263      p.addColumn(FAMILY_B, QUALIFIER, now + 2, Bytes.toBytes("Data23"));
264      t.put(p);
265      String[] args = {
266      sourceTable.getNameAsString(), FQ_OUTPUT_DIR.toString(),  ";", "--starttime=" + now + 1,
267          "--endtime=" + now + 2 };
268
269      runCount(args);
270      FileInputStream inputStream = new FileInputStream(OUTPUT_DIR + File.separator +
271          "part-r-00000");
272      String data = IOUtils.toString(inputStream);
273    inputStream.close();
274      // nothing should hace been emitted to the reducer
275      assertTrue(data.isEmpty());
276    }finally{
277      t.close();
278      FileUtil.fullyDelete(new File(OUTPUT_DIR));
279    }
280  }
281
282
283  private boolean runCount(String[] args) throws Exception {
284    // need to make a copy of the configuration because to make sure
285    // different temp dirs are used.
286    int status = ToolRunner.run(new Configuration(UTIL.getConfiguration()), new CellCounter(),
287        args);
288    return status == 0;
289  }
290
291  /**
292   * Test main method of CellCounter
293   */
294  @Test
295  public void testCellCounterMain() throws Exception {
296
297    PrintStream oldPrintStream = System.err;
298    SecurityManager SECURITY_MANAGER = System.getSecurityManager();
299    LauncherSecurityManager newSecurityManager= new LauncherSecurityManager();
300    System.setSecurityManager(newSecurityManager);
301    ByteArrayOutputStream data = new ByteArrayOutputStream();
302    String[] args = {};
303    System.setErr(new PrintStream(data));
304    try {
305      System.setErr(new PrintStream(data));
306
307      try {
308        CellCounter.main(args);
309        fail("should be SecurityException");
310      } catch (SecurityException e) {
311        assertEquals(-1, newSecurityManager.getExitCode());
312        assertTrue(data.toString().contains("ERROR: Wrong number of parameters:"));
313        // should be information about usage
314        assertTrue(data.toString().contains("Usage:"));
315      }
316
317    } finally {
318      System.setErr(oldPrintStream);
319      System.setSecurityManager(SECURITY_MANAGER);
320    }
321  }
322
323  /**
324   * Test CellCounter for complete table all data should print to output
325   */
326  @Test
327  public void testCellCounterForCompleteTable() throws Exception {
328    final TableName sourceTable = TableName.valueOf(name.getMethodName());
329    String outputPath = OUTPUT_DIR + sourceTable;
330    LocalFileSystem localFileSystem = new LocalFileSystem();
331    Path outputDir =
332        new Path(outputPath).makeQualified(localFileSystem.getUri(),
333          localFileSystem.getWorkingDirectory());
334    byte[][] families = { FAMILY_A, FAMILY_B };
335    Table t = UTIL.createTable(sourceTable, families);
336    try {
337      Put p = new Put(ROW1);
338      p.addColumn(FAMILY_A, QUALIFIER, now, Bytes.toBytes("Data11"));
339      p.addColumn(FAMILY_B, QUALIFIER, now + 1, Bytes.toBytes("Data12"));
340      p.addColumn(FAMILY_A, QUALIFIER, now + 2, Bytes.toBytes("Data13"));
341      t.put(p);
342      p = new Put(ROW2);
343      p.addColumn(FAMILY_B, QUALIFIER, now, Bytes.toBytes("Dat21"));
344      p.addColumn(FAMILY_A, QUALIFIER, now + 1, Bytes.toBytes("Data22"));
345      p.addColumn(FAMILY_B, QUALIFIER, now + 2, Bytes.toBytes("Data23"));
346      t.put(p);
347      String[] args = { sourceTable.getNameAsString(), outputDir.toString(), ";" };
348      runCount(args);
349      FileInputStream inputStream =
350          new FileInputStream(outputPath + File.separator + "part-r-00000");
351      String data = IOUtils.toString(inputStream);
352      inputStream.close();
353      assertTrue(data.contains("Total Families Across all Rows" + "\t" + "2"));
354      assertTrue(data.contains("Total Qualifiers across all Rows" + "\t" + "4"));
355      assertTrue(data.contains("Total ROWS" + "\t" + "2"));
356      assertTrue(data.contains("b;q" + "\t" + "2"));
357      assertTrue(data.contains("a;q" + "\t" + "2"));
358      assertTrue(data.contains("row1;a;q_Versions" + "\t" + "1"));
359      assertTrue(data.contains("row1;b;q_Versions" + "\t" + "1"));
360      assertTrue(data.contains("row2;a;q_Versions" + "\t" + "1"));
361      assertTrue(data.contains("row2;b;q_Versions" + "\t" + "1"));
362
363      FileUtil.fullyDelete(new File(outputPath));
364      args = new String[] { "-D " + TableInputFormat.SCAN_COLUMN_FAMILY + "=a, b",
365                  sourceTable.getNameAsString(), outputDir.toString(), ";"};
366      runCount(args);
367      inputStream = new FileInputStream(outputPath + File.separator + "part-r-00000");
368      String data2 = IOUtils.toString(inputStream);
369      inputStream.close();
370      assertEquals(data, data2);
371    } finally {
372      t.close();
373      localFileSystem.close();
374      FileUtil.fullyDelete(new File(outputPath));
375    }
376  }
377
378  @Test
379  public void TestCellCounterWithoutOutputDir() throws Exception {
380    String[] args = new String[] { "tableName" };
381    assertEquals("CellCounter should exit with -1 as output directory is not specified.", -1,
382      ToolRunner.run(HBaseConfiguration.create(), new CellCounter(), args));
383  }
384}