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}