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.ipc;
21
22 import java.io.BufferedInputStream;
23 import java.io.BufferedOutputStream;
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.FilterInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InterruptedIOException;
30 import java.io.OutputStream;
31 import java.net.ConnectException;
32 import java.net.InetSocketAddress;
33 import java.net.Socket;
34 import java.net.SocketAddress;
35 import java.net.SocketException;
36 import java.net.SocketTimeoutException;
37 import java.net.UnknownHostException;
38 import java.nio.ByteBuffer;
39 import java.security.PrivilegedExceptionAction;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.LinkedList;
43 import java.util.Map;
44 import java.util.Map.Entry;
45 import java.util.Random;
46 import java.util.concurrent.ConcurrentSkipListMap;
47 import java.util.concurrent.atomic.AtomicBoolean;
48 import java.util.concurrent.atomic.AtomicLong;
49
50 import javax.net.SocketFactory;
51 import javax.security.sasl.SaslException;
52
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
55 import org.apache.hadoop.classification.InterfaceAudience;
56 import org.apache.hadoop.classification.InterfaceStability;
57 import org.apache.hadoop.conf.Configuration;
58 import org.apache.hadoop.hbase.CellScanner;
59 import org.apache.hadoop.hbase.DoNotRetryIOException;
60 import org.apache.hadoop.hbase.HBaseIOException;
61 import org.apache.hadoop.hbase.HConstants;
62 import org.apache.hadoop.hbase.ServerName;
63 import org.apache.hadoop.hbase.codec.Codec;
64 import org.apache.hadoop.hbase.codec.KeyValueCodec;
65 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
66 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
67 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMeta;
68 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ConnectionHeader;
69 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ExceptionResponse;
70 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.RequestHeader;
71 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ResponseHeader;
72 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.UserInformation;
73 import org.apache.hadoop.hbase.protobuf.generated.TracingProtos.RPCTInfo;
74 import org.apache.hadoop.hbase.security.AuthMethod;
75 import org.apache.hadoop.hbase.security.HBaseSaslRpcClient;
76 import org.apache.hadoop.hbase.security.SecurityInfo;
77 import org.apache.hadoop.hbase.security.User;
78 import org.apache.hadoop.hbase.security.UserProvider;
79 import org.apache.hadoop.hbase.security.token.AuthenticationTokenSelector;
80 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
81 import org.apache.hadoop.hbase.util.ExceptionUtil;
82 import org.apache.hadoop.hbase.util.Pair;
83 import org.apache.hadoop.hbase.util.PoolMap;
84 import org.apache.hadoop.hbase.util.PoolMap.PoolType;
85 import org.apache.hadoop.io.IOUtils;
86 import org.apache.hadoop.io.Text;
87 import org.apache.hadoop.io.compress.CompressionCodec;
88 import org.apache.hadoop.ipc.RemoteException;
89 import org.apache.hadoop.net.NetUtils;
90 import org.apache.hadoop.security.SecurityUtil;
91 import org.apache.hadoop.security.UserGroupInformation;
92 import org.apache.hadoop.security.token.Token;
93 import org.apache.hadoop.security.token.TokenIdentifier;
94 import org.apache.hadoop.security.token.TokenSelector;
95 import org.cloudera.htrace.Span;
96 import org.cloudera.htrace.Trace;
97
98 import com.google.common.annotations.VisibleForTesting;
99 import com.google.protobuf.BlockingRpcChannel;
100 import com.google.protobuf.Descriptors.MethodDescriptor;
101 import com.google.protobuf.Message;
102 import com.google.protobuf.Message.Builder;
103 import com.google.protobuf.RpcController;
104 import com.google.protobuf.ServiceException;
105 import com.google.protobuf.TextFormat;
106
107
108
109
110
111
112 @InterfaceAudience.Private
113 public class RpcClient {
114
115
116 public static final Log LOG = LogFactory.getLog("org.apache.hadoop.ipc.RpcClient");
117 protected final PoolMap<ConnectionId, Connection> connections;
118
119 protected int counter;
120 protected final AtomicBoolean running = new AtomicBoolean(true);
121 final protected Configuration conf;
122 final protected int maxIdleTime;
123
124 final protected int maxRetries;
125 final protected long failureSleep;
126 protected final boolean tcpNoDelay;
127 protected final boolean tcpKeepAlive;
128 protected int pingInterval;
129 protected FailedServers failedServers;
130 private final Codec codec;
131 private final CompressionCodec compressor;
132 private final IPCUtil ipcUtil;
133
134 protected final SocketFactory socketFactory;
135 protected String clusterId;
136 protected final SocketAddress localAddr;
137
138 private final boolean fallbackAllowed;
139 private UserProvider userProvider;
140
141 final private static String PING_INTERVAL_NAME = "ipc.ping.interval";
142 final private static String SOCKET_TIMEOUT = "ipc.socket.timeout";
143 final static int DEFAULT_PING_INTERVAL = 60000;
144 final static int DEFAULT_SOCKET_TIMEOUT = 20000;
145 final static int PING_CALL_ID = -1;
146
147 public final static String FAILED_SERVER_EXPIRY_KEY = "hbase.ipc.client.failed.servers.expiry";
148 public final static int FAILED_SERVER_EXPIRY_DEFAULT = 2000;
149
150 public static final String IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY =
151 "hbase.ipc.client.fallback-to-simple-auth-allowed";
152 public static final boolean IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_DEFAULT = false;
153
154
155
156
157
158
159 private static ThreadLocal<Integer> rpcTimeout = new ThreadLocal<Integer>() {
160 @Override
161 protected Integer initialValue() {
162 return HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT;
163 }
164 };
165
166
167
168
169 static class FailedServers {
170 private final LinkedList<Pair<Long, String>> failedServers = new
171 LinkedList<Pair<Long, java.lang.String>>();
172 private final int recheckServersTimeout;
173
174 FailedServers(Configuration conf) {
175 this.recheckServersTimeout = conf.getInt(
176 FAILED_SERVER_EXPIRY_KEY, FAILED_SERVER_EXPIRY_DEFAULT);
177 }
178
179
180
181
182 public synchronized void addToFailedServers(InetSocketAddress address) {
183 final long expiry = EnvironmentEdgeManager.currentTimeMillis() + recheckServersTimeout;
184 failedServers.addFirst(new Pair<Long, String>(expiry, address.toString()));
185 }
186
187
188
189
190
191
192 public synchronized boolean isFailedServer(final InetSocketAddress address) {
193 if (failedServers.isEmpty()) {
194 return false;
195 }
196
197 final String lookup = address.toString();
198 final long now = EnvironmentEdgeManager.currentTimeMillis();
199
200
201 Iterator<Pair<Long, String>> it = failedServers.iterator();
202 while (it.hasNext()) {
203 Pair<Long, String> cur = it.next();
204 if (cur.getFirst() < now) {
205 it.remove();
206 } else {
207 if (lookup.equals(cur.getSecond())) {
208 return true;
209 }
210 }
211 }
212
213 return false;
214 }
215 }
216
217 @SuppressWarnings("serial")
218 @InterfaceAudience.Public
219 @InterfaceStability.Evolving
220
221 public static class FailedServerException extends HBaseIOException {
222 public FailedServerException(String s) {
223 super(s);
224 }
225 }
226
227
228
229
230
231
232
233
234
235 public static void setPingInterval(Configuration conf, int pingInterval) {
236 conf.setInt(PING_INTERVAL_NAME, pingInterval);
237 }
238
239
240
241
242
243
244
245
246 static int getPingInterval(Configuration conf) {
247 return conf.getInt(PING_INTERVAL_NAME, DEFAULT_PING_INTERVAL);
248 }
249
250
251
252
253
254
255 public static void setSocketTimeout(Configuration conf, int socketTimeout) {
256 conf.setInt(SOCKET_TIMEOUT, socketTimeout);
257 }
258
259
260
261
262 static int getSocketTimeout(Configuration conf) {
263 return conf.getInt(SOCKET_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
264 }
265
266
267 protected class Call {
268 final int id;
269 final Message param;
270
271
272
273
274 CellScanner cells;
275 Message response;
276
277 Message responseDefaultType;
278 IOException error;
279 boolean done;
280 long startTime;
281 final MethodDescriptor md;
282
283 protected Call(final MethodDescriptor md, Message param, final CellScanner cells,
284 final Message responseDefaultType) {
285 this.param = param;
286 this.md = md;
287 this.cells = cells;
288 this.startTime = System.currentTimeMillis();
289 this.responseDefaultType = responseDefaultType;
290 synchronized (RpcClient.this) {
291 this.id = counter++;
292 }
293 }
294
295 @Override
296 public String toString() {
297 return "callId: " + this.id + " methodName: " + this.md.getName() + " param {" +
298 (this.param != null? ProtobufUtil.getShortTextFormat(this.param): "") + "}";
299 }
300
301
302
303 protected synchronized void callComplete() {
304 this.done = true;
305 notify();
306 }
307
308
309
310
311
312
313 public void setException(IOException error) {
314 this.error = error;
315 callComplete();
316 }
317
318
319
320
321
322
323
324
325 public void setResponse(Message response, final CellScanner cells) {
326 this.response = response;
327 this.cells = cells;
328 callComplete();
329 }
330
331 public long getStartTime() {
332 return this.startTime;
333 }
334 }
335
336 protected final static Map<AuthenticationProtos.TokenIdentifier.Kind,
337 TokenSelector<? extends TokenIdentifier>> tokenHandlers =
338 new HashMap<AuthenticationProtos.TokenIdentifier.Kind, TokenSelector<? extends TokenIdentifier>>();
339 static {
340 tokenHandlers.put(AuthenticationProtos.TokenIdentifier.Kind.HBASE_AUTH_TOKEN,
341 new AuthenticationTokenSelector());
342 }
343
344
345
346
347
348 protected Connection createConnection(ConnectionId remoteId, final Codec codec,
349 final CompressionCodec compressor)
350 throws IOException {
351 return new Connection(remoteId, codec, compressor);
352 }
353
354
355
356
357 protected class Connection extends Thread {
358 private ConnectionHeader header;
359 protected ConnectionId remoteId;
360 protected Socket socket = null;
361 protected DataInputStream in;
362 protected DataOutputStream out;
363 private InetSocketAddress server;
364 private String serverPrincipal;
365 private AuthMethod authMethod;
366 private boolean useSasl;
367 private Token<? extends TokenIdentifier> token;
368 private HBaseSaslRpcClient saslRpcClient;
369 private int reloginMaxBackoff;
370 private final Codec codec;
371 private final CompressionCodec compressor;
372
373
374 protected final ConcurrentSkipListMap<Integer, Call> calls =
375 new ConcurrentSkipListMap<Integer, Call>();
376 protected final AtomicLong lastActivity =
377 new AtomicLong();
378 protected final AtomicBoolean shouldCloseConnection =
379 new AtomicBoolean();
380 protected IOException closeException;
381
382 Connection(ConnectionId remoteId, final Codec codec, final CompressionCodec compressor)
383 throws IOException {
384 if (remoteId.getAddress().isUnresolved()) {
385 throw new UnknownHostException("unknown host: " + remoteId.getAddress().getHostName());
386 }
387 this.server = remoteId.getAddress();
388 this.codec = codec;
389 this.compressor = compressor;
390
391 UserGroupInformation ticket = remoteId.getTicket().getUGI();
392 SecurityInfo securityInfo = SecurityInfo.getInfo(remoteId.getServiceName());
393 this.useSasl = userProvider.isHBaseSecurityEnabled();
394 if (useSasl && securityInfo != null) {
395 AuthenticationProtos.TokenIdentifier.Kind tokenKind = securityInfo.getTokenKind();
396 if (tokenKind != null) {
397 TokenSelector<? extends TokenIdentifier> tokenSelector =
398 tokenHandlers.get(tokenKind);
399 if (tokenSelector != null) {
400 token = tokenSelector.selectToken(new Text(clusterId),
401 ticket.getTokens());
402 } else if (LOG.isDebugEnabled()) {
403 LOG.debug("No token selector found for type "+tokenKind);
404 }
405 }
406 String serverKey = securityInfo.getServerPrincipal();
407 if (serverKey == null) {
408 throw new IOException(
409 "Can't obtain server Kerberos config key from SecurityInfo");
410 }
411 serverPrincipal = SecurityUtil.getServerPrincipal(
412 conf.get(serverKey), server.getAddress().getCanonicalHostName().toLowerCase());
413 if (LOG.isDebugEnabled()) {
414 LOG.debug("RPC Server Kerberos principal name for service="
415 + remoteId.getServiceName() + " is " + serverPrincipal);
416 }
417 }
418
419 if (!useSasl) {
420 authMethod = AuthMethod.SIMPLE;
421 } else if (token != null) {
422 authMethod = AuthMethod.DIGEST;
423 } else {
424 authMethod = AuthMethod.KERBEROS;
425 }
426
427 if (LOG.isDebugEnabled()) {
428 LOG.debug("Use " + authMethod + " authentication for service " + remoteId.serviceName +
429 ", sasl=" + useSasl);
430 }
431 reloginMaxBackoff = conf.getInt("hbase.security.relogin.maxbackoff", 5000);
432 this.remoteId = remoteId;
433
434 ConnectionHeader.Builder builder = ConnectionHeader.newBuilder();
435 builder.setServiceName(remoteId.getServiceName());
436 UserInformation userInfoPB;
437 if ((userInfoPB = getUserInfo(ticket)) != null) {
438 builder.setUserInfo(userInfoPB);
439 }
440 if (this.codec != null) {
441 builder.setCellBlockCodecClass(this.codec.getClass().getCanonicalName());
442 }
443 if (this.compressor != null) {
444 builder.setCellBlockCompressorClass(this.compressor.getClass().getCanonicalName());
445 }
446 this.header = builder.build();
447
448 this.setName("IPC Client (" + socketFactory.hashCode() +") connection to " +
449 remoteId.getAddress().toString() +
450 ((ticket==null)?" from an unknown user": (" from "
451 + ticket.getUserName())));
452 this.setDaemon(true);
453 }
454
455 private UserInformation getUserInfo(UserGroupInformation ugi) {
456 if (ugi == null || authMethod == AuthMethod.DIGEST) {
457
458 return null;
459 }
460 UserInformation.Builder userInfoPB = UserInformation.newBuilder();
461 if (authMethod == AuthMethod.KERBEROS) {
462
463 userInfoPB.setEffectiveUser(ugi.getUserName());
464 } else if (authMethod == AuthMethod.SIMPLE) {
465
466 userInfoPB.setEffectiveUser(ugi.getUserName());
467 if (ugi.getRealUser() != null) {
468 userInfoPB.setRealUser(ugi.getRealUser().getUserName());
469 }
470 }
471 return userInfoPB.build();
472 }
473
474
475 protected void touch() {
476 lastActivity.set(System.currentTimeMillis());
477 }
478
479
480
481
482
483
484
485
486
487 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NN_NAKED_NOTIFY",
488 justification="Notify because new call available for processing")
489 protected synchronized void addCall(Call call) {
490
491
492
493 if (this.shouldCloseConnection.get()) {
494 if (this.closeException == null) {
495 call.setException(new IOException(
496 "Call " + call.id + " not added as the connection " + remoteId + " is closing"));
497 } else {
498 call.setException(this.closeException);
499 }
500 synchronized (call) {
501 call.notifyAll();
502 }
503 } else {
504 calls.put(call.id, call);
505 synchronized (call) {
506 notify();
507 }
508 }
509 }
510
511
512
513
514
515 protected class PingInputStream extends FilterInputStream {
516
517 protected PingInputStream(InputStream in) {
518 super(in);
519 }
520
521
522
523
524
525 private void handleTimeout(SocketTimeoutException e) throws IOException {
526 if (shouldCloseConnection.get() || !running.get() || remoteId.rpcTimeout > 0) {
527 throw e;
528 }
529 sendPing();
530 }
531
532
533
534
535
536
537 @Override
538 public int read() throws IOException {
539 do {
540 try {
541 return super.read();
542 } catch (SocketTimeoutException e) {
543 handleTimeout(e);
544 }
545 } while (true);
546 }
547
548
549
550
551
552
553
554 @Override
555 public int read(byte[] buf, int off, int len) throws IOException {
556 do {
557 try {
558 return super.read(buf, off, len);
559 } catch (SocketTimeoutException e) {
560 handleTimeout(e);
561 }
562 } while (true);
563 }
564 }
565
566 protected synchronized void setupConnection() throws IOException {
567 short ioFailures = 0;
568 short timeoutFailures = 0;
569 while (true) {
570 try {
571 this.socket = socketFactory.createSocket();
572 this.socket.setTcpNoDelay(tcpNoDelay);
573 this.socket.setKeepAlive(tcpKeepAlive);
574 if (localAddr != null) {
575 this.socket.bind(localAddr);
576 }
577
578 NetUtils.connect(this.socket, remoteId.getAddress(),
579 getSocketTimeout(conf));
580 if (remoteId.rpcTimeout > 0) {
581 pingInterval = remoteId.rpcTimeout;
582 }
583 this.socket.setSoTimeout(pingInterval);
584 return;
585 } catch (SocketTimeoutException toe) {
586
587
588
589 handleConnectionFailure(timeoutFailures++, maxRetries, toe);
590 } catch (IOException ie) {
591 handleConnectionFailure(ioFailures++, maxRetries, ie);
592 }
593 }
594 }
595
596 protected void closeConnection() {
597 if (socket == null) {
598 return;
599 }
600
601
602 try {
603 if (socket.getOutputStream() != null) {
604 socket.getOutputStream().close();
605 }
606 } catch (IOException ignored) {
607 }
608 try {
609 if (socket.getInputStream() != null) {
610 socket.getInputStream().close();
611 }
612 } catch (IOException ignored) {
613 }
614 try {
615 if (socket.getChannel() != null) {
616 socket.getChannel().close();
617 }
618 } catch (IOException ignored) {
619 }
620 try {
621 socket.close();
622 } catch (IOException e) {
623 LOG.warn("Not able to close a socket", e);
624 }
625
626
627
628 socket = null;
629 }
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646 private void handleConnectionFailure(int curRetries, int maxRetries, IOException ioe)
647 throws IOException {
648 closeConnection();
649
650
651 if (curRetries >= maxRetries || ExceptionUtil.isInterrupt(ioe)) {
652 throw ioe;
653 }
654
655
656 try {
657 Thread.sleep(failureSleep);
658 } catch (InterruptedException ie) {
659 ExceptionUtil.rethrowIfInterrupt(ie);
660 }
661
662 LOG.info("Retrying connect to server: " + remoteId.getAddress() +
663 " after sleeping " + failureSleep + "ms. Already tried " + curRetries +
664 " time(s).");
665 }
666
667
668
669
670
671
672
673 protected synchronized boolean waitForWork() {
674 if (calls.isEmpty() && !shouldCloseConnection.get() && running.get()) {
675 long timeout = maxIdleTime - (System.currentTimeMillis()-lastActivity.get());
676 if (timeout>0) {
677 try {
678 wait(timeout);
679 } catch (InterruptedException ie) {
680 Thread.currentThread().interrupt();
681 }
682 }
683 }
684
685 if (!calls.isEmpty() && !shouldCloseConnection.get() && running.get()) {
686 return true;
687 } else if (shouldCloseConnection.get()) {
688 return false;
689 } else if (calls.isEmpty()) {
690 markClosed(null);
691 return false;
692 } else {
693 markClosed((IOException)new IOException().initCause(
694 new InterruptedException()));
695 return false;
696 }
697 }
698
699 public InetSocketAddress getRemoteAddress() {
700 return remoteId.getAddress();
701 }
702
703
704
705
706 protected synchronized void sendPing() throws IOException {
707
708 long curTime = System.currentTimeMillis();
709 if ( curTime - lastActivity.get() >= pingInterval) {
710 lastActivity.set(curTime);
711
712 synchronized (this.out) {
713 out.writeInt(PING_CALL_ID);
714 out.flush();
715 }
716 }
717 }
718
719 @Override
720 public void run() {
721 if (LOG.isDebugEnabled()) {
722 LOG.debug(getName() + ": starting, connections " + connections.size());
723 }
724
725 try {
726 while (waitForWork()) {
727 readResponse();
728 }
729 } catch (Throwable t) {
730 LOG.warn(getName() + ": unexpected exception receiving call responses", t);
731 markClosed(new IOException("Unexpected exception receiving call responses", t));
732 }
733
734 close();
735
736 if (LOG.isDebugEnabled())
737 LOG.debug(getName() + ": stopped, connections " + connections.size());
738 }
739
740 private synchronized void disposeSasl() {
741 if (saslRpcClient != null) {
742 try {
743 saslRpcClient.dispose();
744 saslRpcClient = null;
745 } catch (IOException ioe) {
746 LOG.error("Error disposing of SASL client", ioe);
747 }
748 }
749 }
750
751 private synchronized boolean shouldAuthenticateOverKrb() throws IOException {
752 UserGroupInformation loginUser = UserGroupInformation.getLoginUser();
753 UserGroupInformation currentUser =
754 UserGroupInformation.getCurrentUser();
755 UserGroupInformation realUser = currentUser.getRealUser();
756 return authMethod == AuthMethod.KERBEROS &&
757 loginUser != null &&
758
759 loginUser.hasKerberosCredentials() &&
760
761
762 (loginUser.equals(currentUser) || loginUser.equals(realUser));
763 }
764
765 private synchronized boolean setupSaslConnection(final InputStream in2,
766 final OutputStream out2) throws IOException {
767 saslRpcClient = new HBaseSaslRpcClient(authMethod, token, serverPrincipal, fallbackAllowed);
768 return saslRpcClient.saslConnect(in2, out2);
769 }
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789 private synchronized void handleSaslConnectionFailure(
790 final int currRetries,
791 final int maxRetries, final Exception ex, final Random rand,
792 final UserGroupInformation user)
793 throws IOException, InterruptedException{
794 user.doAs(new PrivilegedExceptionAction<Object>() {
795 public Object run() throws IOException, InterruptedException {
796 closeConnection();
797 if (shouldAuthenticateOverKrb()) {
798 if (currRetries < maxRetries) {
799 LOG.debug("Exception encountered while connecting to " +
800 "the server : " + ex);
801
802 if (UserGroupInformation.isLoginKeytabBased()) {
803 UserGroupInformation.getLoginUser().reloginFromKeytab();
804 } else {
805 UserGroupInformation.getLoginUser().reloginFromTicketCache();
806 }
807 disposeSasl();
808
809
810
811
812 Thread.sleep((rand.nextInt(reloginMaxBackoff) + 1));
813 return null;
814 } else {
815 String msg = "Couldn't setup connection for " +
816 UserGroupInformation.getLoginUser().getUserName() +
817 " to " + serverPrincipal;
818 LOG.warn(msg);
819 throw (IOException) new IOException(msg).initCause(ex);
820 }
821 } else {
822 LOG.warn("Exception encountered while connecting to " +
823 "the server : " + ex);
824 }
825 if (ex instanceof RemoteException) {
826 throw (RemoteException)ex;
827 }
828 if (ex instanceof SaslException) {
829 String msg = "SASL authentication failed." +
830 " The most likely cause is missing or invalid credentials." +
831 " Consider 'kinit'.";
832 LOG.fatal(msg, ex);
833 throw new RuntimeException(msg, ex);
834 }
835 throw new IOException(ex);
836 }
837 });
838 }
839
840 protected synchronized void setupIOstreams()
841 throws IOException, InterruptedException {
842 if (socket != null || shouldCloseConnection.get()) {
843 return;
844 }
845
846 if (failedServers.isFailedServer(remoteId.getAddress())) {
847 if (LOG.isDebugEnabled()) {
848 LOG.debug("Not trying to connect to " + server +
849 " this server is in the failed servers list");
850 }
851 IOException e = new FailedServerException(
852 "This server is in the failed servers list: " + server);
853 markClosed(e);
854 close();
855 throw e;
856 }
857
858 try {
859 if (LOG.isDebugEnabled()) {
860 LOG.debug("Connecting to " + server);
861 }
862 short numRetries = 0;
863 final short MAX_RETRIES = 5;
864 Random rand = null;
865 while (true) {
866 setupConnection();
867 InputStream inStream = NetUtils.getInputStream(socket);
868
869
870
871 OutputStream outStream = NetUtils.getOutputStream(socket, pingInterval);
872
873 writeConnectionHeaderPreamble(outStream);
874 if (useSasl) {
875 final InputStream in2 = inStream;
876 final OutputStream out2 = outStream;
877 UserGroupInformation ticket = remoteId.getTicket().getUGI();
878 if (authMethod == AuthMethod.KERBEROS) {
879 if (ticket != null && ticket.getRealUser() != null) {
880 ticket = ticket.getRealUser();
881 }
882 }
883 boolean continueSasl = false;
884 if (ticket == null) throw new FatalConnectionException("ticket/user is null");
885 try {
886 continueSasl = ticket.doAs(new PrivilegedExceptionAction<Boolean>() {
887 @Override
888 public Boolean run() throws IOException {
889 return setupSaslConnection(in2, out2);
890 }
891 });
892 } catch (Exception ex) {
893 if (rand == null) {
894 rand = new Random();
895 }
896 handleSaslConnectionFailure(numRetries++, MAX_RETRIES, ex, rand, ticket);
897 continue;
898 }
899 if (continueSasl) {
900
901 inStream = saslRpcClient.getInputStream(inStream);
902 outStream = saslRpcClient.getOutputStream(outStream);
903 } else {
904
905 authMethod = AuthMethod.SIMPLE;
906 useSasl = false;
907 }
908 }
909 this.in = new DataInputStream(new BufferedInputStream(new PingInputStream(inStream)));
910 this.out = new DataOutputStream(new BufferedOutputStream(outStream));
911
912 writeConnectionHeader();
913
914
915 touch();
916
917
918 start();
919 return;
920 }
921 } catch (Throwable t) {
922 failedServers.addToFailedServers(remoteId.address);
923 IOException e = null;
924 if (t instanceof LinkageError) {
925
926 e = new DoNotRetryIOException(t);
927 markClosed(e);
928 } else if (t instanceof IOException) {
929 e = (IOException)t;
930 markClosed(e);
931 } else {
932 e = new IOException("Could not set up IO Streams", t);
933 markClosed(e);
934 }
935 close();
936 throw e;
937 }
938 }
939
940
941
942
943 private void writeConnectionHeaderPreamble(OutputStream outStream) throws IOException {
944
945
946
947
948
949 int rpcHeaderLen = HConstants.RPC_HEADER.array().length;
950 byte [] preamble = new byte [rpcHeaderLen + 2];
951 System.arraycopy(HConstants.RPC_HEADER.array(), 0, preamble, 0, rpcHeaderLen);
952 preamble[rpcHeaderLen] = HConstants.RPC_CURRENT_VERSION;
953 preamble[rpcHeaderLen + 1] = authMethod.code;
954 outStream.write(preamble);
955 outStream.flush();
956 }
957
958
959
960
961
962 private void writeConnectionHeader() throws IOException {
963 synchronized (this.out) {
964 this.out.writeInt(this.header.getSerializedSize());
965 this.header.writeTo(this.out);
966 this.out.flush();
967 }
968 }
969
970
971 protected synchronized void close() {
972 if (!shouldCloseConnection.get()) {
973 LOG.error(getName() + ": the connection is not in the closed state");
974 return;
975 }
976
977
978
979 synchronized (connections) {
980 connections.removeValue(remoteId, this);
981 }
982
983
984 if (this.out != null) {
985 synchronized(this.out) {
986 IOUtils.closeStream(out);
987 this.out = null;
988 }
989 }
990 IOUtils.closeStream(in);
991 this.in = null;
992 disposeSasl();
993
994
995 if (closeException == null) {
996 if (!calls.isEmpty()) {
997 LOG.warn(getName() + ": connection is closed for no cause and calls are not empty. " +
998 "#Calls: " + calls.size());
999
1000
1001 closeException = new IOException("Unexpected closed connection");
1002 cleanupCalls();
1003 }
1004 } else {
1005
1006 if (LOG.isDebugEnabled()) {
1007 LOG.debug(getName() + ": closing ipc connection to " + server + ": " +
1008 closeException.getMessage(), closeException);
1009 }
1010
1011
1012 cleanupCalls();
1013 }
1014 if (LOG.isDebugEnabled())
1015 LOG.debug(getName() + ": closed");
1016 }
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026 protected void writeRequest(Call call, final int priority) {
1027 if (shouldCloseConnection.get()) return;
1028 try {
1029 RequestHeader.Builder builder = RequestHeader.newBuilder();
1030 builder.setCallId(call.id);
1031 if (Trace.isTracing()) {
1032 Span s = Trace.currentSpan();
1033 builder.setTraceInfo(RPCTInfo.newBuilder().
1034 setParentId(s.getSpanId()).setTraceId(s.getTraceId()));
1035 }
1036 builder.setMethodName(call.md.getName());
1037 builder.setRequestParam(call.param != null);
1038 ByteBuffer cellBlock = ipcUtil.buildCellBlock(this.codec, this.compressor, call.cells);
1039 if (cellBlock != null) {
1040 CellBlockMeta.Builder cellBlockBuilder = CellBlockMeta.newBuilder();
1041 cellBlockBuilder.setLength(cellBlock.limit());
1042 builder.setCellBlockMeta(cellBlockBuilder.build());
1043 }
1044
1045 if (priority != 0) builder.setPriority(priority);
1046
1047 RequestHeader header = builder.build();
1048 synchronized (this.out) {
1049 IPCUtil.write(this.out, header, call.param, cellBlock);
1050 }
1051 if (LOG.isDebugEnabled()) {
1052 LOG.debug(getName() + ": wrote request header " + TextFormat.shortDebugString(header));
1053 }
1054 } catch(IOException e) {
1055 markClosed(e);
1056 }
1057 }
1058
1059
1060
1061
1062 protected void readResponse() {
1063 if (shouldCloseConnection.get()) return;
1064 touch();
1065 int totalSize = -1;
1066 try {
1067
1068
1069 totalSize = in.readInt();
1070
1071
1072 ResponseHeader responseHeader = ResponseHeader.parseDelimitedFrom(in);
1073 int id = responseHeader.getCallId();
1074 if (LOG.isDebugEnabled()) {
1075 LOG.debug(getName() + ": got response header " +
1076 TextFormat.shortDebugString(responseHeader) + ", totalSize: " + totalSize + " bytes");
1077 }
1078 Call call = calls.get(id);
1079 if (call == null) {
1080
1081
1082
1083
1084
1085 int readSoFar = IPCUtil.getTotalSizeWhenWrittenDelimited(responseHeader);
1086 int whatIsLeftToRead = totalSize - readSoFar;
1087 LOG.debug("Unknown callId: " + id + ", skipping over this response of " +
1088 whatIsLeftToRead + " bytes");
1089 IOUtils.skipFully(in, whatIsLeftToRead);
1090 }
1091 if (responseHeader.hasException()) {
1092 ExceptionResponse exceptionResponse = responseHeader.getException();
1093 RemoteException re = createRemoteException(exceptionResponse);
1094 if (isFatalConnectionException(exceptionResponse)) {
1095 markClosed(re);
1096 } else {
1097 if (call != null) call.setException(re);
1098 }
1099 } else {
1100 Message value = null;
1101
1102 if (call != null && call.responseDefaultType != null) {
1103 Builder builder = call.responseDefaultType.newBuilderForType();
1104 builder.mergeDelimitedFrom(in);
1105 value = builder.build();
1106 }
1107 CellScanner cellBlockScanner = null;
1108 if (responseHeader.hasCellBlockMeta()) {
1109 int size = responseHeader.getCellBlockMeta().getLength();
1110 byte [] cellBlock = new byte[size];
1111 IOUtils.readFully(this.in, cellBlock, 0, cellBlock.length);
1112 cellBlockScanner = ipcUtil.createCellScanner(this.codec, this.compressor, cellBlock);
1113 }
1114
1115
1116 if (call != null) call.setResponse(value, cellBlockScanner);
1117 }
1118 if (call != null) calls.remove(id);
1119 } catch (IOException e) {
1120 if (e instanceof SocketTimeoutException && remoteId.rpcTimeout > 0) {
1121
1122
1123
1124 closeException = e;
1125 } else {
1126
1127 markClosed(e);
1128 }
1129 } finally {
1130 if (remoteId.rpcTimeout > 0) {
1131 cleanupCalls(remoteId.rpcTimeout);
1132 }
1133 }
1134 }
1135
1136
1137
1138
1139
1140 private boolean isFatalConnectionException(final ExceptionResponse e) {
1141 return e.getExceptionClassName().
1142 equals(FatalConnectionException.class.getName());
1143 }
1144
1145
1146
1147
1148
1149 private RemoteException createRemoteException(final ExceptionResponse e) {
1150 String innerExceptionClassName = e.getExceptionClassName();
1151 boolean doNotRetry = e.getDoNotRetry();
1152 return e.hasHostname()?
1153
1154 new RemoteWithExtrasException(innerExceptionClassName,
1155 e.getStackTrace(), e.getHostname(), e.getPort(), doNotRetry):
1156 new RemoteWithExtrasException(innerExceptionClassName,
1157 e.getStackTrace(), doNotRetry);
1158 }
1159
1160 protected synchronized void markClosed(IOException e) {
1161 if (shouldCloseConnection.compareAndSet(false, true)) {
1162 closeException = e;
1163 notifyAll();
1164 }
1165 }
1166
1167
1168 protected void cleanupCalls() {
1169 cleanupCalls(0);
1170 }
1171
1172 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NN_NAKED_NOTIFY",
1173 justification="Notify because timedout")
1174 protected void cleanupCalls(long rpcTimeout) {
1175 Iterator<Entry<Integer, Call>> itor = calls.entrySet().iterator();
1176 while (itor.hasNext()) {
1177 Call c = itor.next().getValue();
1178 long waitTime = System.currentTimeMillis() - c.getStartTime();
1179 if (waitTime >= rpcTimeout) {
1180 if (this.closeException == null) {
1181
1182
1183
1184
1185
1186
1187 this.closeException = new CallTimeoutException("Call id=" + c.id +
1188 ", waitTime=" + waitTime + ", rpcTimetout=" + rpcTimeout);
1189 }
1190 c.setException(this.closeException);
1191 synchronized (c) {
1192 c.notifyAll();
1193 }
1194 itor.remove();
1195 } else {
1196 break;
1197 }
1198 }
1199 try {
1200 if (!calls.isEmpty()) {
1201 Call firstCall = calls.get(calls.firstKey());
1202 long maxWaitTime = System.currentTimeMillis() - firstCall.getStartTime();
1203 if (maxWaitTime < rpcTimeout) {
1204 rpcTimeout -= maxWaitTime;
1205 }
1206 }
1207 if (!shouldCloseConnection.get()) {
1208 closeException = null;
1209 setSocketTimeout(socket, (int) rpcTimeout);
1210 }
1211 } catch (SocketException e) {
1212 LOG.debug("Couldn't lower timeout, which may result in longer than expected calls");
1213 }
1214 }
1215 }
1216
1217 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
1218 justification="Presume sync not needed setting socket timeout")
1219 private static void setSocketTimeout(final Socket socket, final int rpcTimeout)
1220 throws java.net.SocketException {
1221 if (socket == null) return;
1222 socket.setSoTimeout(rpcTimeout);
1223 }
1224
1225
1226
1227
1228 @SuppressWarnings("serial")
1229 @InterfaceAudience.Public
1230 @InterfaceStability.Evolving
1231 public static class CallTimeoutException extends IOException {
1232 public CallTimeoutException(final String msg) {
1233 super(msg);
1234 }
1235 }
1236
1237
1238
1239
1240
1241
1242
1243 RpcClient(Configuration conf, String clusterId, SocketFactory factory) {
1244 this(conf, clusterId, factory, null);
1245 }
1246
1247
1248
1249
1250
1251
1252
1253
1254 RpcClient(Configuration conf, String clusterId, SocketFactory factory, SocketAddress localAddr) {
1255 this.maxIdleTime = conf.getInt("hbase.ipc.client.connection.maxidletime", 10000);
1256 this.maxRetries = conf.getInt("hbase.ipc.client.connect.max.retries", 0);
1257 this.failureSleep = conf.getLong(HConstants.HBASE_CLIENT_PAUSE,
1258 HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
1259 this.tcpNoDelay = conf.getBoolean("hbase.ipc.client.tcpnodelay", true);
1260 this.tcpKeepAlive = conf.getBoolean("hbase.ipc.client.tcpkeepalive", true);
1261 this.pingInterval = getPingInterval(conf);
1262 this.ipcUtil = new IPCUtil(conf);
1263 this.conf = conf;
1264 this.codec = getCodec();
1265 this.compressor = getCompressor(conf);
1266 this.socketFactory = factory;
1267 this.clusterId = clusterId != null ? clusterId : HConstants.CLUSTER_ID_DEFAULT;
1268 this.connections = new PoolMap<ConnectionId, Connection>(getPoolType(conf), getPoolSize(conf));
1269 this.failedServers = new FailedServers(conf);
1270 this.fallbackAllowed = conf.getBoolean(IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY,
1271 IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_DEFAULT);
1272 this.localAddr = localAddr;
1273 this.userProvider = UserProvider.instantiate(conf);
1274
1275 if (LOG.isDebugEnabled()) {
1276 LOG.debug("Codec=" + this.codec + ", compressor=" + this.compressor +
1277 ", tcpKeepAlive=" + this.tcpKeepAlive +
1278 ", tcpNoDelay=" + this.tcpNoDelay +
1279 ", maxIdleTime=" + this.maxIdleTime +
1280 ", maxRetries=" + this.maxRetries +
1281 ", fallbackAllowed=" + this.fallbackAllowed +
1282 ", ping interval=" + this.pingInterval + "ms" +
1283 ", bind address=" + (this.localAddr != null ? this.localAddr : "null"));
1284 }
1285 }
1286
1287
1288
1289
1290
1291
1292 public RpcClient(Configuration conf, String clusterId) {
1293 this(conf, clusterId, NetUtils.getDefaultSocketFactory(conf), null);
1294 }
1295
1296
1297
1298
1299
1300
1301
1302 public RpcClient(Configuration conf, String clusterId, SocketAddress localAddr) {
1303 this(conf, clusterId, NetUtils.getDefaultSocketFactory(conf), localAddr);
1304 }
1305
1306
1307
1308
1309
1310 Codec getCodec() {
1311
1312
1313 String className = conf.get(HConstants.RPC_CODEC_CONF_KEY, getDefaultCodec(this.conf));
1314 if (className == null || className.length() == 0) return null;
1315 try {
1316 return (Codec)Class.forName(className).newInstance();
1317 } catch (Exception e) {
1318 throw new RuntimeException("Failed getting codec " + className, e);
1319 }
1320 }
1321
1322 @VisibleForTesting
1323 public static String getDefaultCodec(final Configuration c) {
1324
1325
1326
1327 return c.get("hbase.client.default.rpc.codec", KeyValueCodec.class.getCanonicalName());
1328 }
1329
1330
1331
1332
1333
1334
1335 private static CompressionCodec getCompressor(final Configuration conf) {
1336 String className = conf.get("hbase.client.rpc.compressor", null);
1337 if (className == null || className.isEmpty()) return null;
1338 try {
1339 return (CompressionCodec)Class.forName(className).newInstance();
1340 } catch (Exception e) {
1341 throw new RuntimeException("Failed getting compressor " + className, e);
1342 }
1343 }
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360 protected static PoolType getPoolType(Configuration config) {
1361 return PoolType.valueOf(config.get(HConstants.HBASE_CLIENT_IPC_POOL_TYPE),
1362 PoolType.RoundRobin, PoolType.ThreadLocal);
1363 }
1364
1365
1366
1367
1368
1369
1370
1371
1372 protected static int getPoolSize(Configuration config) {
1373 return config.getInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, 1);
1374 }
1375
1376
1377
1378
1379
1380 SocketFactory getSocketFactory() {
1381 return socketFactory;
1382 }
1383
1384
1385
1386 public void stop() {
1387 if (LOG.isDebugEnabled()) LOG.debug("Stopping rpc client");
1388 if (!running.compareAndSet(true, false)) return;
1389
1390
1391 synchronized (connections) {
1392 for (Connection conn : connections.values()) {
1393 conn.interrupt();
1394 }
1395 }
1396
1397
1398 while (!connections.isEmpty()) {
1399 try {
1400 Thread.sleep(100);
1401 } catch (InterruptedException ignored) {
1402 }
1403 }
1404 }
1405
1406 Pair<Message, CellScanner> call(MethodDescriptor md, Message param, CellScanner cells,
1407 Message returnType, User ticket, InetSocketAddress addr, int rpcTimeout)
1408 throws InterruptedException, IOException {
1409 return call(md, param, cells, returnType, ticket, addr, rpcTimeout, HConstants.NORMAL_QOS);
1410 }
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430 Pair<Message, CellScanner> call(MethodDescriptor md, Message param, CellScanner cells,
1431 Message returnType, User ticket, InetSocketAddress addr,
1432 int rpcTimeout, int priority)
1433 throws InterruptedException, IOException {
1434 Call call = new Call(md, param, cells, returnType);
1435 Connection connection =
1436 getConnection(ticket, call, addr, rpcTimeout, this.codec, this.compressor);
1437 connection.writeRequest(call, priority);
1438
1439
1440 synchronized (call) {
1441 while (!call.done) {
1442 if (connection.shouldCloseConnection.get()) {
1443 throw new IOException("Unexpected closed connection");
1444 }
1445 call.wait(1000);
1446 }
1447
1448 if (call.error != null) {
1449 if (call.error instanceof RemoteException) {
1450 call.error.fillInStackTrace();
1451 throw call.error;
1452 }
1453
1454 throw wrapException(addr, call.error);
1455 }
1456 return new Pair<Message, CellScanner>(call.response, call.cells);
1457 }
1458 }
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472 protected IOException wrapException(InetSocketAddress addr,
1473 IOException exception) {
1474 if (exception instanceof ConnectException) {
1475
1476 return (ConnectException)new ConnectException(
1477 "Call to " + addr + " failed on connection exception: " + exception).initCause(exception);
1478 } else if (exception instanceof SocketTimeoutException) {
1479 return (SocketTimeoutException)new SocketTimeoutException("Call to " + addr +
1480 " failed because " + exception).initCause(exception);
1481 } else {
1482 return (IOException)new IOException("Call to " + addr + " failed on local exception: " +
1483 exception).initCause(exception);
1484 }
1485 }
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495 public void cancelConnections(String hostname, int port, IOException ioe) {
1496 synchronized (connections) {
1497 for (Connection connection : connections.values()) {
1498 if (connection.isAlive() &&
1499 connection.getRemoteAddress().getPort() == port &&
1500 connection.getRemoteAddress().getHostName().equals(hostname)) {
1501 LOG.info("The server on " + hostname + ":" + port +
1502 " is dead - stopping the connection " + connection.remoteId);
1503 connection.closeConnection();
1504
1505
1506 }
1507 }
1508 }
1509 }
1510
1511
1512
1513 protected Connection getConnection(User ticket, Call call, InetSocketAddress addr,
1514 int rpcTimeout, final Codec codec, final CompressionCodec compressor)
1515 throws IOException, InterruptedException {
1516 if (!running.get()) throw new StoppedRpcClientException();
1517 Connection connection;
1518 ConnectionId remoteId =
1519 new ConnectionId(ticket, call.md.getService().getName(), addr, rpcTimeout);
1520 synchronized (connections) {
1521 connection = connections.get(remoteId);
1522 if (connection == null) {
1523 connection = createConnection(remoteId, this.codec, this.compressor);
1524 connections.put(remoteId, connection);
1525 }
1526 }
1527 connection.addCall(call);
1528
1529
1530
1531
1532
1533
1534
1535
1536 connection.setupIOstreams();
1537 return connection;
1538 }
1539
1540
1541
1542
1543
1544 protected static class ConnectionId {
1545 final InetSocketAddress address;
1546 final User ticket;
1547 final int rpcTimeout;
1548 private static final int PRIME = 16777619;
1549 final String serviceName;
1550
1551 ConnectionId(User ticket,
1552 String serviceName,
1553 InetSocketAddress address,
1554 int rpcTimeout) {
1555 this.address = address;
1556 this.ticket = ticket;
1557 this.rpcTimeout = rpcTimeout;
1558 this.serviceName = serviceName;
1559 }
1560
1561 String getServiceName() {
1562 return this.serviceName;
1563 }
1564
1565 InetSocketAddress getAddress() {
1566 return address;
1567 }
1568
1569 User getTicket() {
1570 return ticket;
1571 }
1572
1573 @Override
1574 public String toString() {
1575 return this.address.toString() + "/" + this.serviceName + "/" + this.ticket + "/" +
1576 this.rpcTimeout;
1577 }
1578
1579 @Override
1580 public boolean equals(Object obj) {
1581 if (obj instanceof ConnectionId) {
1582 ConnectionId id = (ConnectionId) obj;
1583 return address.equals(id.address) &&
1584 ((ticket != null && ticket.equals(id.ticket)) ||
1585 (ticket == id.ticket)) && rpcTimeout == id.rpcTimeout &&
1586 this.serviceName == id.serviceName;
1587 }
1588 return false;
1589 }
1590
1591 @Override
1592 public int hashCode() {
1593 int hashcode = (address.hashCode() +
1594 PRIME * (PRIME * this.serviceName.hashCode() ^
1595 (ticket == null ? 0 : ticket.hashCode()) )) ^
1596 rpcTimeout;
1597 return hashcode;
1598 }
1599 }
1600
1601 public static void setRpcTimeout(int t) {
1602 rpcTimeout.set(t);
1603 }
1604
1605 public static int getRpcTimeout() {
1606 return rpcTimeout.get();
1607 }
1608
1609
1610
1611
1612
1613 public static int getRpcTimeout(int defaultTimeout) {
1614 return Math.min(defaultTimeout, rpcTimeout.get());
1615 }
1616
1617 public static void resetRpcTimeout() {
1618 rpcTimeout.remove();
1619 }
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637 Message callBlockingMethod(MethodDescriptor md, RpcController controller,
1638 Message param, Message returnType, final User ticket, final InetSocketAddress isa,
1639 final int rpcTimeout)
1640 throws ServiceException {
1641 long startTime = 0;
1642 if (LOG.isTraceEnabled()) {
1643 startTime = System.currentTimeMillis();
1644 }
1645 PayloadCarryingRpcController pcrc = (PayloadCarryingRpcController)controller;
1646 CellScanner cells = null;
1647 if (pcrc != null) {
1648 cells = pcrc.cellScanner();
1649
1650 pcrc.setCellScanner(null);
1651 }
1652 Pair<Message, CellScanner> val = null;
1653 try {
1654 val = call(md, param, cells, returnType, ticket, isa, rpcTimeout,
1655 pcrc != null? pcrc.getPriority(): HConstants.NORMAL_QOS);
1656 if (pcrc != null) {
1657
1658 if (val.getSecond() != null) pcrc.setCellScanner(val.getSecond());
1659 } else if (val.getSecond() != null) {
1660 throw new ServiceException("Client dropping data on the floor!");
1661 }
1662
1663 if (LOG.isTraceEnabled()) {
1664 long callTime = System.currentTimeMillis() - startTime;
1665 if (LOG.isTraceEnabled()) {
1666 LOG.trace("Call: " + md.getName() + ", callTime: " + callTime + "ms");
1667 }
1668 }
1669 return val.getFirst();
1670 } catch (Throwable e) {
1671 throw new ServiceException(e);
1672 }
1673 }
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683 public BlockingRpcChannel createBlockingRpcChannel(final ServerName sn,
1684 final User ticket, final int rpcTimeout) {
1685 return new BlockingRpcChannelImplementation(this, sn, ticket, rpcTimeout);
1686 }
1687
1688
1689
1690
1691
1692 public static class BlockingRpcChannelImplementation implements BlockingRpcChannel {
1693 private final InetSocketAddress isa;
1694 private volatile RpcClient rpcClient;
1695 private final int rpcTimeout;
1696 private final User ticket;
1697
1698 protected BlockingRpcChannelImplementation(final RpcClient rpcClient, final ServerName sn,
1699 final User ticket, final int rpcTimeout) {
1700 this.isa = new InetSocketAddress(sn.getHostname(), sn.getPort());
1701 this.rpcClient = rpcClient;
1702
1703
1704 this.rpcTimeout = getRpcTimeout(rpcTimeout);
1705 this.ticket = ticket;
1706 }
1707
1708 @Override
1709 public Message callBlockingMethod(MethodDescriptor md, RpcController controller,
1710 Message param, Message returnType)
1711 throws ServiceException {
1712 return this.rpcClient.callBlockingMethod(md, controller, param, returnType, this.ticket,
1713 this.isa, this.rpcTimeout);
1714 }
1715 }
1716 }