View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.ipc;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertTrue;
22  import static org.junit.Assert.fail;
23  import static org.mockito.Matchers.anyObject;
24  import static org.mockito.Mockito.spy;
25  import static org.mockito.Mockito.verify;
26  import static org.mockito.internal.verification.VerificationModeFactory.times;
27  
28  import java.io.IOException;
29  import java.net.ConnectException;
30  import java.net.InetSocketAddress;
31  import java.net.SocketTimeoutException;
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.hadoop.conf.Configuration;
38  import org.apache.hadoop.hbase.Cell;
39  import org.apache.hadoop.hbase.CellScanner;
40  import org.apache.hadoop.hbase.CellUtil;
41  import org.apache.hadoop.hbase.HBaseConfiguration;
42  import org.apache.hadoop.hbase.KeyValue;
43  import org.apache.hadoop.hbase.exceptions.ConnectionClosingException;
44  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EchoRequestProto;
45  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EchoResponseProto;
46  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EmptyRequestProto;
47  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EmptyResponseProto;
48  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestRpcServiceProtos;
49  import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
50  import org.apache.hadoop.hbase.security.User;
51  import org.apache.hadoop.hbase.util.Bytes;
52  import org.apache.hadoop.hbase.util.Pair;
53  import org.apache.hadoop.io.compress.GzipCodec;
54  import org.apache.hadoop.util.StringUtils;
55  import org.junit.Test;
56  
57  import com.google.common.collect.ImmutableList;
58  import com.google.common.collect.Lists;
59  import com.google.protobuf.BlockingService;
60  import com.google.protobuf.Descriptors.MethodDescriptor;
61  import com.google.protobuf.Message;
62  import com.google.protobuf.RpcController;
63  import com.google.protobuf.ServiceException;
64  
65  /**
66   * Some basic ipc tests.
67   */
68  public abstract class AbstractTestIPC {
69  
70    private static final Log LOG = LogFactory.getLog(AbstractTestIPC.class);
71  
72    private static byte[] CELL_BYTES = Bytes.toBytes("xyz");
73    private static KeyValue CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, CELL_BYTES);
74    static byte[] BIG_CELL_BYTES = new byte[10 * 1024];
75    static KeyValue BIG_CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, BIG_CELL_BYTES);
76    static final Configuration CONF = HBaseConfiguration.create();
77    // We are using the test TestRpcServiceProtos generated classes and Service because they are
78    // available and basic with methods like 'echo', and ping. Below we make a blocking service
79    // by passing in implementation of blocking interface. We use this service in all tests that
80    // follow.
81    static final BlockingService SERVICE =
82        TestRpcServiceProtos.TestProtobufRpcProto
83            .newReflectiveBlockingService(new TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface() {
84  
85              @Override
86              public EmptyResponseProto ping(RpcController controller, EmptyRequestProto request)
87                  throws ServiceException {
88                return null;
89              }
90  
91              @Override
92              public EmptyResponseProto error(RpcController controller, EmptyRequestProto request)
93                  throws ServiceException {
94                return null;
95              }
96  
97              @Override
98              public EchoResponseProto echo(RpcController controller, EchoRequestProto request)
99                  throws ServiceException {
100               if (controller instanceof PayloadCarryingRpcController) {
101                 PayloadCarryingRpcController pcrc = (PayloadCarryingRpcController) controller;
102                 // If cells, scan them to check we are able to iterate what we were given and since
103                 // this is
104                 // an echo, just put them back on the controller creating a new block. Tests our
105                 // block
106                 // building.
107                 CellScanner cellScanner = pcrc.cellScanner();
108                 List<Cell> list = null;
109                 if (cellScanner != null) {
110                   list = new ArrayList<Cell>();
111                   try {
112                     while (cellScanner.advance()) {
113                       list.add(cellScanner.current());
114                     }
115                   } catch (IOException e) {
116                     throw new ServiceException(e);
117                   }
118                 }
119                 cellScanner = CellUtil.createCellScanner(list);
120                 ((PayloadCarryingRpcController) controller).setCellScanner(cellScanner);
121               }
122               return EchoResponseProto.newBuilder().setMessage(request.getMessage()).build();
123             }
124           });
125 
126   /**
127    * Instance of server. We actually don't do anything speical in here so could just use
128    * HBaseRpcServer directly.
129    */
130   static class TestRpcServer extends RpcServer {
131 
132     TestRpcServer() throws IOException {
133       this(new FifoRpcScheduler(CONF, 1));
134     }
135 
136     TestRpcServer(RpcScheduler scheduler) throws IOException {
137       super(null, "testRpcServer", Lists
138           .newArrayList(new BlockingServiceAndInterface(SERVICE, null)), new InetSocketAddress(
139           "localhost", 0), CONF, scheduler);
140     }
141 
142     @Override
143     public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
144         Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status)
145         throws IOException {
146       return super.call(service, md, param, cellScanner, receiveTime, status);
147     }
148   }
149 
150   protected abstract AbstractRpcClient createRpcClientNoCodec(Configuration conf);
151 
152   /**
153    * Ensure we do not HAVE TO HAVE a codec.
154    * @throws InterruptedException
155    * @throws IOException
156    */
157   @Test
158   public void testNoCodec() throws InterruptedException, IOException {
159     Configuration conf = HBaseConfiguration.create();
160     AbstractRpcClient client = createRpcClientNoCodec(conf);
161     TestRpcServer rpcServer = new TestRpcServer();
162     try {
163       rpcServer.start();
164       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
165       final String message = "hello";
166       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage(message).build();
167       InetSocketAddress address = rpcServer.getListenerAddress();
168       if (address == null) {
169         throw new IOException("Listener channel is closed");
170       }
171       Pair<Message, CellScanner> r =
172           client.call(null, md, param, md.getOutputType().toProto(), User.getCurrent(), address);
173       assertTrue(r.getSecond() == null);
174       // Silly assertion that the message is in the returned pb.
175       assertTrue(r.getFirst().toString().contains(message));
176     } finally {
177       client.close();
178       rpcServer.stop();
179     }
180   }
181 
182   protected abstract AbstractRpcClient createRpcClient(Configuration conf);
183 
184   /**
185    * It is hard to verify the compression is actually happening under the wraps. Hope that if
186    * unsupported, we'll get an exception out of some time (meantime, have to trace it manually to
187    * confirm that compression is happening down in the client and server).
188    * @throws IOException
189    * @throws InterruptedException
190    * @throws SecurityException
191    * @throws NoSuchMethodException
192    */
193   @Test
194   public void testCompressCellBlock() throws IOException, InterruptedException, SecurityException,
195       NoSuchMethodException, ServiceException {
196     Configuration conf = new Configuration(HBaseConfiguration.create());
197     conf.set("hbase.client.rpc.compressor", GzipCodec.class.getCanonicalName());
198     List<Cell> cells = new ArrayList<Cell>();
199     int count = 3;
200     for (int i = 0; i < count; i++) {
201       cells.add(CELL);
202     }
203     AbstractRpcClient client = createRpcClient(conf);
204     TestRpcServer rpcServer = new TestRpcServer();
205     try {
206       rpcServer.start();
207       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
208       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
209       PayloadCarryingRpcController pcrc =
210           new PayloadCarryingRpcController(CellUtil.createCellScanner(cells));
211       InetSocketAddress address = rpcServer.getListenerAddress();
212       if (address == null) {
213         throw new IOException("Listener channel is closed");
214       }
215       Pair<Message, CellScanner> r =
216           client.call(pcrc, md, param, md.getOutputType().toProto(), User.getCurrent(), address);
217       int index = 0;
218       while (r.getSecond().advance()) {
219         assertTrue(CELL.equals(r.getSecond().current()));
220         index++;
221       }
222       assertEquals(count, index);
223     } finally {
224       client.close();
225       rpcServer.stop();
226     }
227   }
228 
229   protected abstract AbstractRpcClient createRpcClientRTEDuringConnectionSetup(Configuration conf)
230       throws IOException;
231 
232   @Test
233   public void testRTEDuringConnectionSetup() throws Exception {
234     Configuration conf = HBaseConfiguration.create();
235     TestRpcServer rpcServer = new TestRpcServer();
236     AbstractRpcClient client = createRpcClientRTEDuringConnectionSetup(conf);
237     try {
238       rpcServer.start();
239       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
240       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
241       InetSocketAddress address = rpcServer.getListenerAddress();
242       if (address == null) {
243         throw new IOException("Listener channel is closed");
244       }
245       client.call(null, md, param, null, User.getCurrent(), address);
246       fail("Expected an exception to have been thrown!");
247     } catch (Exception e) {
248       LOG.info("Caught expected exception: " + e.toString());
249       assertTrue(StringUtils.stringifyException(e).contains("Injected fault"));
250     } finally {
251       client.close();
252       rpcServer.stop();
253     }
254   }
255 
256   /** Tests that the rpc scheduler is called when requests arrive. */
257   @Test
258   public void testRpcScheduler() throws IOException, InterruptedException {
259     RpcScheduler scheduler = spy(new FifoRpcScheduler(CONF, 1));
260     RpcServer rpcServer = new TestRpcServer(scheduler);
261     verify(scheduler).init((RpcScheduler.Context) anyObject());
262     AbstractRpcClient client = createRpcClient(CONF);
263     try {
264       rpcServer.start();
265       verify(scheduler).start();
266       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
267       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
268       InetSocketAddress address = rpcServer.getListenerAddress();
269       if (address == null) {
270         throw new IOException("Listener channel is closed");
271       }
272       for (int i = 0; i < 10; i++) {
273         client.call(
274           new PayloadCarryingRpcController(
275               CellUtil.createCellScanner(ImmutableList.<Cell> of(CELL))), md, param, md
276               .getOutputType().toProto(), User.getCurrent(), address);
277       }
278       verify(scheduler, times(10)).dispatch((CallRunner) anyObject());
279     } finally {
280       rpcServer.stop();
281       verify(scheduler).stop();
282     }
283   }
284 
285   @Test
286   public void testWrapException() throws Exception {
287     AbstractRpcClient client =
288         (AbstractRpcClient) RpcClientFactory.createClient(CONF, "AbstractTestIPC");
289     final InetSocketAddress address = InetSocketAddress.createUnresolved("localhost", 0);
290     assertTrue(client.wrapException(address, new ConnectException()) instanceof ConnectException);
291     assertTrue(client.wrapException(address,
292       new SocketTimeoutException()) instanceof SocketTimeoutException);
293     assertTrue(client.wrapException(address, new ConnectionClosingException(
294         "Test AbstractRpcClient#wrapException")) instanceof ConnectionClosingException);
295     assertTrue(client
296         .wrapException(address, new CallTimeoutException("Test AbstractRpcClient#wrapException"))
297         .getCause() instanceof CallTimeoutException);
298   }
299 }