1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.mapreduce;
20
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25
26 import java.io.File;
27 import java.io.IOException;
28 import java.util.Map;
29 import java.util.NavigableMap;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.fs.FileUtil;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.TableName;
36 import org.apache.hadoop.hbase.TableNotEnabledException;
37 import org.apache.hadoop.hbase.TableNotFoundException;
38 import org.apache.hadoop.hbase.client.Admin;
39 import org.apache.hadoop.hbase.testclassification.LargeTests;
40 import org.apache.hadoop.hbase.client.HTable;
41 import org.apache.hadoop.hbase.client.Put;
42 import org.apache.hadoop.hbase.client.Result;
43 import org.apache.hadoop.hbase.client.Scan;
44 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
45 import org.apache.hadoop.hbase.util.Bytes;
46 import org.apache.hadoop.mapreduce.Counter;
47 import org.apache.hadoop.mapreduce.Counters;
48 import org.apache.hadoop.mapreduce.Job;
49 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
50 import org.junit.Test;
51 import org.junit.experimental.categories.Category;
52
53
54
55
56
57
58 @Category(LargeTests.class)
59 public class TestTableMapReduce extends TestTableMapReduceBase {
60 private static final Log LOG = LogFactory.getLog(TestTableMapReduce.class);
61
62 @Override
63 protected Log getLog() { return LOG; }
64
65
66
67
68 static class ProcessContentsMapper extends TableMapper<ImmutableBytesWritable, Put> {
69
70
71
72
73
74
75
76
77
78 @Override
79 public void map(ImmutableBytesWritable key, Result value,
80 Context context)
81 throws IOException, InterruptedException {
82 if (value.size() != 1) {
83 throw new IOException("There should only be one input column");
84 }
85 Map<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
86 cf = value.getMap();
87 if(!cf.containsKey(INPUT_FAMILY)) {
88 throw new IOException("Wrong input columns. Missing: '" +
89 Bytes.toString(INPUT_FAMILY) + "'.");
90 }
91
92
93 String originalValue = Bytes.toString(value.getValue(INPUT_FAMILY, INPUT_FAMILY));
94 StringBuilder newValue = new StringBuilder(originalValue);
95 newValue.reverse();
96
97 Put outval = new Put(key.get());
98 outval.add(OUTPUT_FAMILY, null, Bytes.toBytes(newValue.toString()));
99 context.write(key, outval);
100 }
101 }
102
103 protected void runTestOnTable(HTable table) throws IOException {
104 Job job = null;
105 try {
106 LOG.info("Before map/reduce startup");
107 job = new Job(table.getConfiguration(), "process column contents");
108 job.setNumReduceTasks(1);
109 Scan scan = new Scan();
110 scan.addFamily(INPUT_FAMILY);
111 TableMapReduceUtil.initTableMapperJob(
112 Bytes.toString(table.getTableName()), scan,
113 ProcessContentsMapper.class, ImmutableBytesWritable.class,
114 Put.class, job);
115 TableMapReduceUtil.initTableReducerJob(
116 Bytes.toString(table.getTableName()),
117 IdentityTableReducer.class, job);
118 FileOutputFormat.setOutputPath(job, new Path("test"));
119 LOG.info("Started " + Bytes.toString(table.getTableName()));
120 assertTrue(job.waitForCompletion(true));
121 LOG.info("After map/reduce completion");
122
123
124 verify(table.getName());
125
126 verifyJobCountersAreEmitted(job);
127 } catch (InterruptedException e) {
128 throw new IOException(e);
129 } catch (ClassNotFoundException e) {
130 throw new IOException(e);
131 } finally {
132 table.close();
133 if (job != null) {
134 FileUtil.fullyDelete(
135 new File(job.getConfiguration().get("hadoop.tmp.dir")));
136 }
137 }
138 }
139
140
141
142
143
144
145 private void verifyJobCountersAreEmitted(Job job) throws IOException {
146 Counters counters = job.getCounters();
147 Counter counter
148 = counters.findCounter(TableRecordReaderImpl.HBASE_COUNTER_GROUP_NAME, "RPC_CALLS");
149 assertNotNull("Unable to find Job counter for HBase scan metrics, RPC_CALLS", counter);
150 assertTrue("Counter value for RPC_CALLS should be larger than 0", counter.getValue() > 0);
151 }
152
153 @Test(expected = TableNotEnabledException.class)
154 public void testWritingToDisabledTable() throws IOException {
155
156 try (Admin admin = UTIL.getConnection().getAdmin();
157 HTable table = new HTable(UTIL.getConfiguration(), TABLE_FOR_NEGATIVE_TESTS)) {
158 admin.disableTable(table.getName());
159 runTestOnTable(table);
160 fail("Should not have reached here, should have thrown an exception");
161 }
162 }
163
164 @Test(expected = TableNotFoundException.class)
165 public void testWritingToNonExistentTable() throws IOException {
166
167 try (HTable table = new HTable(UTIL.getConfiguration(),
168 TableName.valueOf("table-does-not-exist"))) {
169 runTestOnTable(table);
170 fail("Should not have reached here, should have thrown an exception");
171 }
172 }
173 }