1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.regionserver;
21
22 import static org.junit.Assert.*;
23
24 import java.io.IOException;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.hbase.Coprocessor;
31 import org.apache.hadoop.hbase.CoprocessorEnvironment;
32 import org.apache.hadoop.hbase.HBaseTestingUtility;
33 import org.apache.hadoop.hbase.HConstants;
34 import org.apache.hadoop.hbase.HRegionInfo;
35 import org.apache.hadoop.hbase.HRegionLocation;
36 import org.apache.hadoop.hbase.MiniHBaseCluster;
37 import org.apache.hadoop.hbase.client.Get;
38 import org.apache.hadoop.hbase.client.HTable;
39 import org.apache.hadoop.hbase.client.Put;
40 import org.apache.hadoop.hbase.client.Row;
41 import org.apache.hadoop.hbase.client.coprocessor.Batch;
42 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
43 import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
44 import org.apache.hadoop.hbase.ipc.HMasterInterface;
45 import org.apache.hadoop.hbase.ipc.HMasterRegionInterface;
46 import org.apache.hadoop.hbase.ipc.ProtocolSignature;
47 import org.apache.hadoop.hbase.util.Bytes;
48 import org.apache.hadoop.hbase.util.JVMClusterUtil;
49 import org.apache.hadoop.hbase.ipc.VersionedProtocol;
50 import org.junit.AfterClass;
51 import org.junit.BeforeClass;
52 import org.junit.Test;
53
54 import com.google.common.collect.Lists;
55
56 public class TestServerCustomProtocol {
57 private static final Log LOG = LogFactory.getLog(TestServerCustomProtocol.class);
58
59
60 public static interface PingProtocol extends CoprocessorProtocol {
61 public String ping();
62 public int getPingCount();
63 public int incrementCount(int diff);
64 public String hello(String name);
65 public void noop();
66 }
67
68
69 public static class PingHandler implements Coprocessor, PingProtocol, VersionedProtocol {
70 static long VERSION = 1;
71 private int counter = 0;
72 @Override
73 public String ping() {
74 counter++;
75 return "pong";
76 }
77
78 @Override
79 public int getPingCount() {
80 return counter;
81 }
82
83 @Override
84 public int incrementCount(int diff) {
85 counter += diff;
86 return counter;
87 }
88
89 @Override
90 public String hello(String name) {
91 if (name == null) {
92 return "Who are you?";
93 } else if ("nobody".equals(name)) {
94 return null;
95 }
96 return "Hello, "+name;
97 }
98
99 @Override
100 public void noop() {
101
102 }
103
104 @Override
105 public ProtocolSignature getProtocolSignature(
106 String protocol, long version, int clientMethodsHashCode)
107 throws IOException {
108 return new ProtocolSignature(VERSION, null);
109 }
110
111 @Override
112 public long getProtocolVersion(String s, long l) throws IOException {
113 return VERSION;
114 }
115
116 @Override
117 public void start(CoprocessorEnvironment env) throws IOException {
118 }
119
120 @Override
121 public void stop(CoprocessorEnvironment env) throws IOException {
122 }
123 }
124
125 private static final byte[] TEST_TABLE = Bytes.toBytes("test");
126 private static final byte[] TEST_FAMILY = Bytes.toBytes("f1");
127
128 private static final byte[] ROW_A = Bytes.toBytes("aaa");
129 private static final byte[] ROW_B = Bytes.toBytes("bbb");
130 private static final byte[] ROW_C = Bytes.toBytes("ccc");
131
132 private static final byte[] ROW_AB = Bytes.toBytes("abb");
133 private static final byte[] ROW_BC = Bytes.toBytes("bcc");
134
135 private static HBaseTestingUtility util = new HBaseTestingUtility();
136 private static MiniHBaseCluster cluster = null;
137
138 @BeforeClass
139 public static void setupBeforeClass() throws Exception {
140 util.getConfiguration().set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
141 PingHandler.class.getName());
142 util.startMiniCluster(1);
143 cluster = util.getMiniHBaseCluster();
144
145 HTable table = util.createTable(TEST_TABLE, TEST_FAMILY);
146 util.createMultiRegions(util.getConfiguration(), table, TEST_FAMILY,
147 new byte[][]{ HConstants.EMPTY_BYTE_ARRAY,
148 ROW_B, ROW_C});
149
150 Put puta = new Put( ROW_A );
151 puta.add(TEST_FAMILY, Bytes.toBytes("col1"), Bytes.toBytes(1));
152 table.put(puta);
153
154 Put putb = new Put( ROW_B );
155 putb.add(TEST_FAMILY, Bytes.toBytes("col1"), Bytes.toBytes(1));
156 table.put(putb);
157
158 Put putc = new Put( ROW_C );
159 putc.add(TEST_FAMILY, Bytes.toBytes("col1"), Bytes.toBytes(1));
160 table.put(putc);
161 }
162
163 @AfterClass
164 public static void tearDownAfterClass() throws Exception {
165 util.shutdownMiniCluster();
166 }
167
168 @Test
169 public void testSingleProxy() throws Exception {
170 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
171
172 PingProtocol pinger = table.coprocessorProxy(PingProtocol.class, ROW_A);
173 String result = pinger.ping();
174 assertEquals("Invalid custom protocol response", "pong", result);
175 result = pinger.hello("George");
176 assertEquals("Invalid custom protocol response", "Hello, George", result);
177 result = pinger.hello(null);
178 assertEquals("Should handle NULL parameter", "Who are you?", result);
179 result = pinger.hello("nobody");
180 assertNull(result);
181 int cnt = pinger.getPingCount();
182 assertTrue("Count should be incremented", cnt > 0);
183 int newcnt = pinger.incrementCount(5);
184 assertEquals("Counter should have incremented by 5", cnt+5, newcnt);
185 }
186
187 @Test
188 public void testSingleMethod() throws Throwable {
189 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
190
191 List<? extends Row> rows = Lists.newArrayList(
192 new Get(ROW_A), new Get(ROW_B), new Get(ROW_C));
193
194 Batch.Call<PingProtocol,String> call = Batch.forMethod(PingProtocol.class,
195 "ping");
196 Map<byte[],String> results =
197 table.coprocessorExec(PingProtocol.class, ROW_A, ROW_C, call);
198
199
200 verifyRegionResults(table, results, ROW_A);
201 verifyRegionResults(table, results, ROW_B);
202 verifyRegionResults(table, results, ROW_C);
203
204 Batch.Call<PingProtocol,String> helloCall =
205 Batch.forMethod(PingProtocol.class, "hello", "NAME");
206 results =
207 table.coprocessorExec(PingProtocol.class, ROW_A, ROW_C, helloCall);
208
209
210 verifyRegionResults(table, results, "Hello, NAME", ROW_A);
211 verifyRegionResults(table, results, "Hello, NAME", ROW_B);
212 verifyRegionResults(table, results, "Hello, NAME", ROW_C);
213 }
214
215 @Test
216 public void testRowRange() throws Throwable {
217 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
218
219
220 Map<byte[],String> results = table.coprocessorExec(PingProtocol.class,
221 null, null, new Batch.Call<PingProtocol,String>() {
222 public String call(PingProtocol instance) {
223 return instance.ping();
224 }
225 });
226
227 verifyRegionResults(table, results, ROW_A);
228 verifyRegionResults(table, results, ROW_B);
229 verifyRegionResults(table, results, ROW_C);
230
231
232 results = table.coprocessorExec(PingProtocol.class, ROW_BC, null,
233 new Batch.Call<PingProtocol,String>() {
234 public String call(PingProtocol instance) {
235 return instance.ping();
236 }
237 });
238
239 HRegionLocation loc = table.getRegionLocation(ROW_A);
240 assertNull("Should be missing region for row aaa (prior to start row)",
241 results.get(loc.getRegionInfo().getRegionName()));
242 verifyRegionResults(table, results, ROW_B);
243 verifyRegionResults(table, results, ROW_C);
244
245
246 results = table.coprocessorExec(PingProtocol.class, null, ROW_BC,
247 new Batch.Call<PingProtocol,String>() {
248 public String call(PingProtocol instance) {
249 return instance.ping();
250 }
251 });
252
253 verifyRegionResults(table, results, ROW_A);
254 verifyRegionResults(table, results, ROW_B);
255 loc = table.getRegionLocation(ROW_C);
256 assertNull("Should be missing region for row ccc (past stop row)",
257 results.get(loc.getRegionInfo().getRegionName()));
258
259
260 results = table.coprocessorExec(PingProtocol.class, ROW_AB, ROW_BC,
261 new Batch.Call<PingProtocol,String>() {
262 public String call(PingProtocol instance) {
263 return instance.ping();
264 }
265 });
266
267 verifyRegionResults(table, results, ROW_A);
268 verifyRegionResults(table, results, ROW_B);
269 loc = table.getRegionLocation(ROW_C);
270 assertNull("Should be missing region for row ccc (past stop row)",
271 results.get(loc.getRegionInfo().getRegionName()));
272
273
274 results = table.coprocessorExec(PingProtocol.class, ROW_B, ROW_BC,
275 new Batch.Call<PingProtocol,String>() {
276 public String call(PingProtocol instance) {
277 return instance.ping();
278 }
279 });
280
281 verifyRegionResults(table, results, ROW_B);
282 loc = table.getRegionLocation(ROW_A);
283 assertNull("Should be missing region for row aaa (prior to start)",
284 results.get(loc.getRegionInfo().getRegionName()));
285 loc = table.getRegionLocation(ROW_C);
286 assertNull("Should be missing region for row ccc (past stop row)",
287 results.get(loc.getRegionInfo().getRegionName()));
288 }
289
290 @Test
291 public void testCompountCall() throws Throwable {
292 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
293
294 Map<byte[],String> results = table.coprocessorExec(PingProtocol.class,
295 ROW_A, ROW_C,
296 new Batch.Call<PingProtocol,String>() {
297 public String call(PingProtocol instance) {
298 return instance.hello(instance.ping());
299 }
300 });
301
302 verifyRegionResults(table, results, "Hello, pong", ROW_A);
303 verifyRegionResults(table, results, "Hello, pong", ROW_B);
304 verifyRegionResults(table, results, "Hello, pong", ROW_C);
305 }
306
307 @Test
308 public void testNullCall() throws Throwable {
309 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
310
311 Map<byte[],String> results = table.coprocessorExec(PingProtocol.class,
312 ROW_A, ROW_C,
313 new Batch.Call<PingProtocol,String>() {
314 public String call(PingProtocol instance) {
315 return instance.hello(null);
316 }
317 });
318
319 verifyRegionResults(table, results, "Who are you?", ROW_A);
320 verifyRegionResults(table, results, "Who are you?", ROW_B);
321 verifyRegionResults(table, results, "Who are you?", ROW_C);
322 }
323
324 @Test
325 public void testNullReturn() throws Throwable {
326 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
327
328 Map<byte[],String> results = table.coprocessorExec(PingProtocol.class,
329 ROW_A, ROW_C,
330 new Batch.Call<PingProtocol,String>(){
331 public String call(PingProtocol instance) {
332 return instance.hello("nobody");
333 }
334 });
335
336 verifyRegionResults(table, results, null, ROW_A);
337 verifyRegionResults(table, results, null, ROW_B);
338 verifyRegionResults(table, results, null, ROW_C);
339 }
340
341 @Test
342 public void testVoidReturnType() throws Throwable {
343 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
344
345 Map<byte[],Object> results = table.coprocessorExec(PingProtocol.class,
346 ROW_A, ROW_C,
347 new Batch.Call<PingProtocol,Object>(){
348 public Object call(PingProtocol instance) {
349 instance.noop();
350 return null;
351 }
352 });
353
354 assertEquals("Should have results from three regions", 3, results.size());
355
356 for (Object v : results.values()) {
357 assertNull(v);
358 }
359 }
360
361 private void verifyRegionResults(HTable table,
362 Map<byte[],String> results, byte[] row) throws Exception {
363 verifyRegionResults(table, results, "pong", row);
364 }
365
366 private void verifyRegionResults(HTable table,
367 Map<byte[],String> results, String expected, byte[] row)
368 throws Exception {
369 HRegionLocation loc = table.getRegionLocation(row);
370 byte[] region = loc.getRegionInfo().getRegionName();
371 StringBuffer resultsKeyStr = new StringBuffer();
372 for (Map.Entry<byte [], String> e: results.entrySet()) {
373 resultsKeyStr.append(Bytes.toString(e.getKey()));
374 resultsKeyStr.append(", ");
375 }
376 LOG.info("ResultsKeyStr=" + resultsKeyStr);
377
378 assertTrue("Results should contain region " +
379 Bytes.toStringBinary(region)+" for row '"+Bytes.toStringBinary(row)+"'",
380 results.containsKey(region));
381 assertEquals("Invalid result for row '"+Bytes.toStringBinary(row)+"'",
382 expected, results.get(region));
383 }
384 }