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