1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24
25 import java.io.IOException;
26 import java.util.Map;
27 import java.util.Map.Entry;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.hbase.Coprocessor;
32 import org.apache.hadoop.hbase.CoprocessorEnvironment;
33 import org.apache.hadoop.hbase.HBaseTestingUtility;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.HRegionInfo;
36 import org.apache.hadoop.hbase.HRegionLocation;
37 import org.apache.hadoop.hbase.testclassification.MediumTests;
38 import org.apache.hadoop.hbase.TableName;
39 import org.apache.hadoop.hbase.ServerName;
40 import org.apache.hadoop.hbase.client.HTable;
41 import org.apache.hadoop.hbase.client.Put;
42 import org.apache.hadoop.hbase.client.RegionLocator;
43 import org.apache.hadoop.hbase.client.Table;
44 import org.apache.hadoop.hbase.client.coprocessor.Batch;
45 import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
46 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
47 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
48 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
49 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos;
50 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.CountRequest;
51 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.CountResponse;
52 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.HelloRequest;
53 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.HelloResponse;
54 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.IncrementCountRequest;
55 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.IncrementCountResponse;
56 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.NoopRequest;
57 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.NoopResponse;
58 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.PingRequest;
59 import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.PingResponse;
60 import org.apache.hadoop.hbase.ipc.BlockingRpcCallback;
61 import org.apache.hadoop.hbase.util.Bytes;
62 import org.junit.After;
63 import org.junit.AfterClass;
64 import org.junit.Before;
65 import org.junit.BeforeClass;
66 import org.junit.Test;
67 import org.junit.experimental.categories.Category;
68
69 import com.google.protobuf.RpcCallback;
70 import com.google.protobuf.RpcController;
71 import com.google.protobuf.Service;
72 import com.google.protobuf.ServiceException;
73
74 @Category(MediumTests.class)
75 public class TestServerCustomProtocol {
76 private static final Log LOG = LogFactory.getLog(TestServerCustomProtocol.class);
77 static final String WHOAREYOU = "Who are you?";
78 static final String NOBODY = "nobody";
79 static final String HELLO = "Hello, ";
80
81
82 public static class PingHandler extends PingProtos.PingService
83 implements Coprocessor, CoprocessorService {
84 private int counter = 0;
85
86 @Override
87 public void start(CoprocessorEnvironment env) throws IOException {
88 if (env instanceof RegionCoprocessorEnvironment) return;
89 throw new CoprocessorException("Must be loaded on a table region!");
90 }
91
92 @Override
93 public void stop(CoprocessorEnvironment env) throws IOException {
94
95 }
96
97 @Override
98 public void ping(RpcController controller, PingRequest request,
99 RpcCallback<PingResponse> done) {
100 this.counter++;
101 done.run(PingResponse.newBuilder().setPong("pong").build());
102 }
103
104 @Override
105 public void count(RpcController controller, CountRequest request,
106 RpcCallback<CountResponse> done) {
107 done.run(CountResponse.newBuilder().setCount(this.counter).build());
108 }
109
110 @Override
111 public void increment(RpcController controller,
112 IncrementCountRequest request, RpcCallback<IncrementCountResponse> done) {
113 this.counter += request.getDiff();
114 done.run(IncrementCountResponse.newBuilder().setCount(this.counter).build());
115 }
116
117 @Override
118 public void hello(RpcController controller, HelloRequest request,
119 RpcCallback<HelloResponse> done) {
120 if (!request.hasName()) done.run(HelloResponse.newBuilder().setResponse(WHOAREYOU).build());
121 else if (request.getName().equals(NOBODY)) done.run(HelloResponse.newBuilder().build());
122 else done.run(HelloResponse.newBuilder().setResponse(HELLO + request.getName()).build());
123 }
124
125 @Override
126 public void noop(RpcController controller, NoopRequest request,
127 RpcCallback<NoopResponse> done) {
128 done.run(NoopResponse.newBuilder().build());
129 }
130
131 @Override
132 public Service getService() {
133 return this;
134 }
135 }
136
137 private static final TableName TEST_TABLE = TableName.valueOf("test");
138 private static final byte[] TEST_FAMILY = Bytes.toBytes("f1");
139
140 private static final byte[] ROW_A = Bytes.toBytes("aaa");
141 private static final byte[] ROW_B = Bytes.toBytes("bbb");
142 private static final byte[] ROW_C = Bytes.toBytes("ccc");
143
144 private static final byte[] ROW_AB = Bytes.toBytes("abb");
145 private static final byte[] ROW_BC = Bytes.toBytes("bcc");
146
147 private static HBaseTestingUtility util = new HBaseTestingUtility();
148
149 @BeforeClass
150 public static void setupBeforeClass() throws Exception {
151 util.getConfiguration().set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
152 PingHandler.class.getName());
153 util.startMiniCluster();
154 }
155
156 @Before
157 public void before() throws Exception {
158 HTable table = util.createTable(TEST_TABLE, TEST_FAMILY);
159 util.createMultiRegions(util.getConfiguration(), table, TEST_FAMILY,
160 new byte[][]{ HConstants.EMPTY_BYTE_ARRAY, ROW_B, ROW_C});
161
162 Put puta = new Put( ROW_A );
163 puta.add(TEST_FAMILY, Bytes.toBytes("col1"), Bytes.toBytes(1));
164 table.put(puta);
165
166 Put putb = new Put( ROW_B );
167 putb.add(TEST_FAMILY, Bytes.toBytes("col1"), Bytes.toBytes(1));
168 table.put(putb);
169
170 Put putc = new Put( ROW_C );
171 putc.add(TEST_FAMILY, Bytes.toBytes("col1"), Bytes.toBytes(1));
172 table.put(putc);
173 }
174
175 @After
176 public void after() throws Exception {
177 util.deleteTable(TEST_TABLE);
178 }
179
180 @AfterClass
181 public static void tearDownAfterClass() throws Exception {
182 util.shutdownMiniCluster();
183 }
184
185 @Test
186 public void testSingleProxy() throws Throwable {
187 Table table = new HTable(util.getConfiguration(), TEST_TABLE);
188 Map<byte [], String> results = ping(table, null, null);
189
190 assertEquals(3, results.size());
191 for (Map.Entry<byte [], String> e: results.entrySet()) {
192 assertEquals("Invalid custom protocol response", "pong", e.getValue());
193 }
194 hello(table, "George", HELLO + "George");
195 LOG.info("Did george");
196 hello(table, null, "Who are you?");
197 LOG.info("Who are you");
198 hello(table, NOBODY, null);
199 LOG.info(NOBODY);
200 Map<byte [], Integer> intResults = table.coprocessorService(PingProtos.PingService.class,
201 null, null,
202 new Batch.Call<PingProtos.PingService, Integer>() {
203 @Override
204 public Integer call(PingProtos.PingService instance) throws IOException {
205 BlockingRpcCallback<PingProtos.CountResponse> rpcCallback =
206 new BlockingRpcCallback<PingProtos.CountResponse>();
207 instance.count(null, PingProtos.CountRequest.newBuilder().build(), rpcCallback);
208 return rpcCallback.get().getCount();
209 }
210 });
211 int count = -1;
212 for (Map.Entry<byte [], Integer> e: intResults.entrySet()) {
213 assertTrue(e.getValue() > 0);
214 count = e.getValue();
215 }
216 final int diff = 5;
217 intResults = table.coprocessorService(PingProtos.PingService.class,
218 null, null,
219 new Batch.Call<PingProtos.PingService, Integer>() {
220 @Override
221 public Integer call(PingProtos.PingService instance) throws IOException {
222 BlockingRpcCallback<PingProtos.IncrementCountResponse> rpcCallback =
223 new BlockingRpcCallback<PingProtos.IncrementCountResponse>();
224 instance.increment(null, PingProtos.IncrementCountRequest.newBuilder().setDiff(diff).build(),
225 rpcCallback);
226 return rpcCallback.get().getCount();
227 }
228 });
229
230 assertEquals(3, results.size());
231 for (Map.Entry<byte [], Integer> e: intResults.entrySet()) {
232 assertEquals(e.getValue().intValue(), count + diff);
233 }
234 table.close();
235 }
236
237 private Map<byte [], String> hello(final Table table, final String send, final String response)
238 throws ServiceException, Throwable {
239 Map<byte [], String> results = hello(table, send);
240 for (Map.Entry<byte [], String> e: results.entrySet()) {
241 assertEquals("Invalid custom protocol response", response, e.getValue());
242 }
243 return results;
244 }
245
246 private Map<byte [], String> hello(final Table table, final String send)
247 throws ServiceException, Throwable {
248 return hello(table, send, null, null);
249 }
250
251 private Map<byte [], String> hello(final Table table, final String send, final byte [] start,
252 final byte [] end)
253 throws ServiceException, Throwable {
254 return table.coprocessorService(PingProtos.PingService.class,
255 start, end,
256 new Batch.Call<PingProtos.PingService, String>() {
257 @Override
258 public String call(PingProtos.PingService instance) throws IOException {
259 BlockingRpcCallback<PingProtos.HelloResponse> rpcCallback =
260 new BlockingRpcCallback<PingProtos.HelloResponse>();
261 PingProtos.HelloRequest.Builder builder = PingProtos.HelloRequest.newBuilder();
262 if (send != null) builder.setName(send);
263 instance.hello(null, builder.build(), rpcCallback);
264 PingProtos.HelloResponse r = rpcCallback.get();
265 return r != null && r.hasResponse()? r.getResponse(): null;
266 }
267 });
268 }
269
270 private Map<byte [], String> compoundOfHelloAndPing(final Table table, final byte [] start,
271 final byte [] end)
272 throws ServiceException, Throwable {
273 return table.coprocessorService(PingProtos.PingService.class,
274 start, end,
275 new Batch.Call<PingProtos.PingService, String>() {
276 @Override
277 public String call(PingProtos.PingService instance) throws IOException {
278 BlockingRpcCallback<PingProtos.HelloResponse> rpcCallback =
279 new BlockingRpcCallback<PingProtos.HelloResponse>();
280 PingProtos.HelloRequest.Builder builder = PingProtos.HelloRequest.newBuilder();
281
282 builder.setName(doPing(instance));
283 instance.hello(null, builder.build(), rpcCallback);
284 PingProtos.HelloResponse r = rpcCallback.get();
285 return r != null && r.hasResponse()? r.getResponse(): null;
286 }
287 });
288 }
289
290 private Map<byte [], String> noop(final Table table, final byte [] start,
291 final byte [] end)
292 throws ServiceException, Throwable {
293 return table.coprocessorService(PingProtos.PingService.class, start, end,
294 new Batch.Call<PingProtos.PingService, String>() {
295 @Override
296 public String call(PingProtos.PingService instance) throws IOException {
297 BlockingRpcCallback<PingProtos.NoopResponse> rpcCallback =
298 new BlockingRpcCallback<PingProtos.NoopResponse>();
299 PingProtos.NoopRequest.Builder builder = PingProtos.NoopRequest.newBuilder();
300 instance.noop(null, builder.build(), rpcCallback);
301 rpcCallback.get();
302
303 return null;
304 }
305 });
306 }
307
308 @Test
309 public void testSingleMethod() throws Throwable {
310 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
311 Map<byte [], String> results = table.coprocessorService(PingProtos.PingService.class,
312 null, ROW_A,
313 new Batch.Call<PingProtos.PingService, String>() {
314 @Override
315 public String call(PingProtos.PingService instance) throws IOException {
316 BlockingRpcCallback<PingProtos.PingResponse> rpcCallback =
317 new BlockingRpcCallback<PingProtos.PingResponse>();
318 instance.ping(null, PingProtos.PingRequest.newBuilder().build(), rpcCallback);
319 return rpcCallback.get().getPong();
320 }
321 });
322
323
324 assertEquals(1, results.size());
325 verifyRegionResults(table, results, ROW_A);
326
327 final String name = "NAME";
328 results = hello(table, name, null, ROW_A);
329
330
331 assertEquals(1, results.size());
332 verifyRegionResults(table, results, "Hello, NAME", ROW_A);
333 table.close();
334 }
335
336 @Test
337 public void testRowRange() throws Throwable {
338 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
339 for (Entry<HRegionInfo, ServerName> e: table.getRegionLocations().entrySet()) {
340 LOG.info("Region " + e.getKey().getRegionNameAsString() + ", servername=" + e.getValue());
341 }
342
343
344
345
346
347
348 Map<byte [], String> results = ping(table, null, ROW_A);
349
350 assertEquals(1, results.size());
351 verifyRegionResults(table, results, ROW_A);
352
353
354 results = ping(table, ROW_BC, null);
355 assertEquals(2, results.size());
356
357 HRegionLocation loc = table.getRegionLocation(ROW_A, true);
358 assertNull("Should be missing region for row aaa (prior to start row)",
359 results.get(loc.getRegionInfo().getRegionName()));
360 verifyRegionResults(table, results, ROW_B);
361 verifyRegionResults(table, results, ROW_C);
362
363
364 results = ping(table, null, ROW_BC);
365
366 assertEquals(2, results.size());
367 verifyRegionResults(table, results, ROW_A);
368 verifyRegionResults(table, results, ROW_B);
369 loc = table.getRegionLocation(ROW_C, true);
370 assertNull("Should be missing region for row ccc (past stop row)",
371 results.get(loc.getRegionInfo().getRegionName()));
372
373
374 results = ping(table, ROW_AB, ROW_BC);
375
376 assertEquals(2, results.size());
377 verifyRegionResults(table, results, ROW_A);
378 verifyRegionResults(table, results, ROW_B);
379 loc = table.getRegionLocation(ROW_C, true);
380 assertNull("Should be missing region for row ccc (past stop row)",
381 results.get(loc.getRegionInfo().getRegionName()));
382
383
384 results = ping(table, ROW_B, ROW_BC);
385
386 assertEquals(1, results.size());
387 verifyRegionResults(table, results, ROW_B);
388 loc = table.getRegionLocation(ROW_A, true);
389 assertNull("Should be missing region for row aaa (prior to start)",
390 results.get(loc.getRegionInfo().getRegionName()));
391 loc = table.getRegionLocation(ROW_C, true);
392 assertNull("Should be missing region for row ccc (past stop row)",
393 results.get(loc.getRegionInfo().getRegionName()));
394 table.close();
395 }
396
397 private Map<byte [], String> ping(final Table table, final byte [] start, final byte [] end)
398 throws ServiceException, Throwable {
399 return table.coprocessorService(PingProtos.PingService.class, start, end,
400 new Batch.Call<PingProtos.PingService, String>() {
401 @Override
402 public String call(PingProtos.PingService instance) throws IOException {
403 return doPing(instance);
404 }
405 });
406 }
407
408 private static String doPing(PingProtos.PingService instance) throws IOException {
409 BlockingRpcCallback<PingProtos.PingResponse> rpcCallback =
410 new BlockingRpcCallback<PingProtos.PingResponse>();
411 instance.ping(null, PingProtos.PingRequest.newBuilder().build(), rpcCallback);
412 return rpcCallback.get().getPong();
413 }
414
415 @Test
416 public void testCompoundCall() throws Throwable {
417 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
418 Map<byte [], String> results = compoundOfHelloAndPing(table, ROW_A, ROW_C);
419 verifyRegionResults(table, results, "Hello, pong", ROW_A);
420 verifyRegionResults(table, results, "Hello, pong", ROW_B);
421 verifyRegionResults(table, results, "Hello, pong", ROW_C);
422 table.close();
423 }
424
425 @Test
426 public void testNullCall() throws Throwable {
427 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
428 Map<byte[],String> results = hello(table, null, ROW_A, ROW_C);
429 verifyRegionResults(table, results, "Who are you?", ROW_A);
430 verifyRegionResults(table, results, "Who are you?", ROW_B);
431 verifyRegionResults(table, results, "Who are you?", ROW_C);
432 }
433
434 @Test
435 public void testNullReturn() throws Throwable {
436 HTable table = new HTable(util.getConfiguration(), TEST_TABLE);
437 Map<byte[],String> results = hello(table, "nobody", ROW_A, ROW_C);
438 verifyRegionResults(table, results, null, ROW_A);
439 verifyRegionResults(table, results, null, ROW_B);
440 verifyRegionResults(table, results, null, ROW_C);
441 }
442
443 @Test
444 public void testEmptyReturnType() throws Throwable {
445 Table table = new HTable(util.getConfiguration(), TEST_TABLE);
446 Map<byte[],String> results = noop(table, ROW_A, ROW_C);
447 assertEquals("Should have results from three regions", 3, results.size());
448
449 for (Object v : results.values()) {
450 assertNull(v);
451 }
452 }
453
454 private void verifyRegionResults(RegionLocator table,
455 Map<byte[],String> results, byte[] row) throws Exception {
456 verifyRegionResults(table, results, "pong", row);
457 }
458
459 private void verifyRegionResults(RegionLocator table,
460 Map<byte[], String> results, String expected, byte[] row)
461 throws Exception {
462 for (Map.Entry<byte [], String> e: results.entrySet()) {
463 LOG.info("row=" + Bytes.toString(row) + ", expected=" + expected +
464 ", result key=" + Bytes.toString(e.getKey()) +
465 ", value=" + e.getValue());
466 }
467 HRegionLocation loc = table.getRegionLocation(row, true);
468 byte[] region = loc.getRegionInfo().getRegionName();
469 assertTrue("Results should contain region " +
470 Bytes.toStringBinary(region) + " for row '" + Bytes.toStringBinary(row)+ "'",
471 results.containsKey(region));
472 assertEquals("Invalid result for row '"+Bytes.toStringBinary(row)+"'",
473 expected, results.get(region));
474 }
475 }