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}